Swift 4中如何从正态分布返回Float或Double类型值?
嘿,我懂你想要的是连续型的正态分布数值,而不是整数对吧?你当前用的GKGaussianDistribution的nextInt()确实只能返回整数,不过有两个靠谱的方案可以拿到你要的Float/Double类型值:
方案一:基于GameplayKit生成连续正态分布
虽然GKGaussianDistribution没有直接返回连续值的方法,但我们可以借助GKRandomSource的nextUniform()(返回0到1之间的Double),再通过Box-Muller变换把均匀分布转换成符合需求的正态分布。代码示例如下:
import GameplayKit class DistributionGenerator { let randomSource: GKRandomSource init() { randomSource = GKARC4RandomSource() // 也可以用GKMersenneTwisterRandomSource,随机性更强 } // 生成Double类型的正态分布值 func generateNormal(mean: Double, stdDev: Double) -> Double { // 先生成标准正态分布(均值0,标准差1) let u1 = randomSource.nextUniform() let u2 = randomSource.nextUniform() let z0 = sqrt(-2 * log(u1)) * cos(2 * Double.pi * u2) // 转换为指定均值和标准差的分布 return z0 * stdDev + mean } // 生成Float类型的正态分布值 func generateNormalFloat(mean: Float, stdDev: Float) -> Float { return Float(generateNormal(mean: Double(mean), stdDev: Double(stdDev))) } } // 使用示例 let generator = DistributionGenerator() let epsilonDouble = generator.generateNormal(mean: 0.0, stdDev: 4.0) let epsilonFloat = generator.generateNormalFloat(mean: 0.0, stdDev: 4.0)
方案二:手动实现(不依赖GameplayKit)
如果不想引入GameplayKit框架,也可以用系统自带的随机数生成函数结合Box-Muller变换来实现,代码更轻量:
// 生成Double类型的正态分布值 func generateNormal(mean: Double, stdDev: Double) -> Double { // 辅助函数:生成0到1之间的均匀分布随机数 func uniform() -> Double { return Double(arc4random()) / Double(UInt32.max) } let u1 = uniform() let u2 = uniform() let z0 = sqrt(-2 * log(u1)) * cos(2 * Double.pi * u2) return z0 * stdDev + mean } // 生成Float类型的正态分布值 func generateNormalFloat(mean: Float, stdDev: Float) -> Float { return Float(generateNormal(mean: Double(mean), stdDev: Double(stdDev))) } // 使用示例 let epsilon = generateNormal(mean: 0.0, stdDev: 4.0)
小补充
Box-Muller变换其实能一次性生成两个独立的标准正态分布值(另一个是sqrt(-2*log(u1))*sin(2*Double.pi*u2)),如果你的代码需要频繁生成随机数,可以缓存这个值下次复用,能提升一点效率。另外,不建议直接把GKGaussianDistribution生成的整数转成浮点型,这种方式本质是离散值的近似,精度很低,远不如上面的连续生成方法靠谱。
内容的提问来源于stack exchange,提问作者Charlie Roe
相关产品推荐
相关产品推荐

