生成具有给定分布的随机数
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了生成具有给定分布的随机数相关的知识,希望对你有一定的参考价值。
看看这个问题:
Swift probability of random number being selected?
最佳答案建议使用switch语句来完成工作。但是,如果我需要考虑大量案例,那么代码看起来非常不优雅;我有一个巨大的switch语句,在每种情况下一遍又一遍地重复使用非常相似的代码。
当您有大量概率需要考虑时,是否有一种更好,更清晰的方式来选择具有一定概率的随机数? (比如~30)
这是一个受到Generate random numbers with a given (numerical) distribution的各种答案强烈影响的Swift实现:
func randomNumber(#probabilities: [Double]) -> Int {
// Sum of all probabilities (so that we don't have to require that the sum is 1.0):
let sum = reduce(probabilities, 0, +)
// Random number in the range 0.0 <= rnd < sum :
let rnd = sum * Double(arc4random_uniform(UInt32.max)) / Double(UInt32.max)
// Find the first interval of accumulated probabilities into which `rnd` falls:
var accum = 0.0
for (i, p) in enumerate(probabilities) {
accum += p
if rnd < accum {
return i
}
}
// This point might be reached due to floating point inaccuracies:
return (probabilities.count - 1)
}
例子:
let x = randomNumber(probabilities: [0.2, 0.3, 0.5])
以概率0.2返回0,以概率0.3返回1,以概率0.5返回2。
let x = randomNumber(probabilities: [1.0, 2.0])
以概率1/3返回0,以概率2/3返回1。
Swift 2 / Xcode 7的更新:
func randomNumber(probabilities probabilities: [Double]) -> Int {
// Sum of all probabilities (so that we don't have to require that the sum is 1.0):
let sum = probabilities.reduce(0, combine: +)
// Random number in the range 0.0 <= rnd < sum :
let rnd = sum * Double(arc4random_uniform(UInt32.max)) / Double(UInt32.max)
// Find the first interval of accumulated probabilities into which `rnd` falls:
var accum = 0.0
for (i, p) in probabilities.enumerate() {
accum += p
if rnd < accum {
return i
}
}
// This point might be reached due to floating point inaccuracies:
return (probabilities.count - 1)
}
Swift 3 / Xcode 8的更新:
func randomNumber(probabilities: [Double]) -> Int {
// Sum of all probabilities (so that we don't have to require that the sum is 1.0):
let sum = probabilities.reduce(0, +)
// Random number in the range 0.0 <= rnd < sum :
let rnd = sum * Double(arc4random_uniform(UInt32.max)) / Double(UInt32.max)
// Find the first interval of accumulated probabilities into which `rnd` falls:
var accum = 0.0
for (i, p) in probabilities.enumerated() {
accum += p
if rnd < accum {
return i
}
}
// This point might be reached due to floating point inaccuracies:
return (probabilities.count - 1)
}
Swift 4.2 / Xcode 10的更新:
func randomNumber(probabilities: [Double]) -> Int {
// Sum of all probabilities (so that we don't have to require that the sum is 1.0):
let sum = probabilities.reduce(0, +)
// Random number in the range 0.0 <= rnd < sum :
let rnd = Double.random(in: 0.0 ..< sum)
// Find the first interval of accumulated probabilities into which `rnd` falls:
var accum = 0.0
for (i, p) in probabilities.enumerated() {
accum += p
if rnd < accum {
return i
}
}
// This point might be reached due to floating point inaccuracies:
return (probabilities.count - 1)
}
当您有大量概率需要考虑时,是否有一种更好,更清晰的方式来选择具有一定概率的随机数?
当然。编写一个基于概率表生成数字的函数。这基本上就是你指向的switch语句:代码中定义的表。您可以使用定义为概率和结果列表的表对数据执行相同的操作:
probability outcome ----------- ------- 0.4 1 0.2 2 0.1 3 0.15 4 0.15 5
现在,您可以随机选择0到1之间的数字。从列表顶部开始,添加概率,直到超过您选择的数字,并使用相应的结果。例如,假设您选择的数字是0.6527637。从顶部开始:0.4更小,所以继续前进。 0.6(0.4 + 0.2)较小,所以继续。 0.7(0.6 + 0.1)更大,所以停止。结果是3。
为了清楚起见,我在这里简短地保留了表格,但是只要你喜欢就可以使用它,并且你可以在数据文件中定义它,这样你就不必在列表改变时重新编译。
请注意,Swift没有特别关于此方法的特定内容 - 您可以在C或Swift或Lisp中执行相同的操作。
对于我的小型图书馆swiftstats来说,这似乎是一个无耻插件的好机会:https://github.com/r0fls/swiftstats
例如,这将从正态分布生成3个随机变量,均值为0且方差为1:
import SwiftStats
let n = SwiftStats.Distributions.Normal(0, 1.0)
print(n.random())
支持的发行版包括:正常,指数,二项式等...
它还支持使用最大似然估计器将样本数据拟合到给定分布。
有关详细信息,请参阅项目自述文件。
您可以使用指数函数或二次函数 - 将x作为随机数,将y作为新的随机数。然后,你只需要摇动方程式,直到它适合你的用例。说我有(x ^ 2)/ 10 +(x / 300)。把你的随机数放入(作为一些浮点形式),然后在Int()出现时获得发言权。所以,如果我的随机数发生器从0变为9,我有40%的几率获得0,有30%的几率获得1 - 3,有20%的几率获得4 - 6,并有10%的几率获得你基本上试图伪造某种正态分布。
这里有一个关于它在Swift中会是什么样子的想法:
func giveY (x: UInt32) -> Int {
let xD = Double(x)
return Int(xD * xD / 10 + xD / 300)
}
let ans = giveY (arc4random_uniform(10))
编辑:
我上面不太清楚 - 我的意思是你可以用一些函数替换switch语句,这些函数将返回一组数字,其概率分布可以通过使用wolfram或其他东西的回归得出。因此,对于您链接的问题,您可以执行以下操作:
import Foundation
func returnLevelChange() -> Double {
return 0.06 * exp(0.4 * Double(arc4random_uniform(10))) - 0.1
}
newItemLevel = oldItemLevel * returnLevelChange()
所以该函数在-0.05和2.1之间返回一个double。这将是你的“x%差/比当前项目水平更好”的数字。但是,由于它是一个指数函数,它不会返回均匀的数字扩散。 arc4random_uniform(10)从0到9返回一个int,每个都会产生一个像这样的double:
0: -0.04
1: -0.01
2: 0.03
3: 0.1
4: 0.2
5: 0.34
6: 0.56
7: 0.89
8: 1.37
9: 2.1
由于来自arc4random_uniform的每个int都有相同的显示机会,因此你得到如下概率:
40% chance of -0.04 to 0.1 (~ -5% - 10%)
30% chance of 0.2 to 0.56 (~ 20% - 55%)
20% chance of 0.89 to 1.37 (~ 90% - 140%)
10% chance of 2.1 (~ 200%)
这类似于其他人的概率。现在,对于你的功能来说,它要困难得多,而其他答案几乎肯定更适用和优雅。但你还是可以做到的。
按照概率的顺序排列每个字母 - 从最大到最小。然后,获得他们的累积总和,从0开始,没有最后一个。 (所以50%,30%,20%的概率变为0,0.5,0.8)。然后你将它们相乘,直到它们的整数具有合理的精度(0,5,8)。然后,绘制它们 - 你的累积概率是你的x,你想要以给定概率选择的东西(你的字母)就是你的y。 (你显然无法在y轴上绘制实际字母,所以你只需在一些数组中绘制它们的索引)。然后,你会尝试在那里找到一些回归,并将其作为你的功能。例如,尝试这些数字,我得到了
e^0.14x - 1
还有这个:
let letters: [Character] = ["a", "b", "c"]
func randLetter() -> Character {
return letters[Int(exp(0.14 * Double(arc4random_uniform(10))) - 1)]
}
50%的时间返回“a”,30%的时间返回“b”,20%的时间返回“c”。对于更多的字母来说显然非常麻烦,并且需要一段时间来找出正确的回归,如果你想改变权重,你必须手动完成。但如果你确实找到了一个适合你的值的好方程式,那么实际的函数只能是几行,而且速度很快。
以上是关于生成具有给定分布的随机数的主要内容,如果未能解决你的问题,请参考以下文章