用给定的分布生成随机数
看看这个问题:
随机数被选中的概率是多less?
最好的答案build议使用一个switch语句,完成这项工作。 但是,如果我有很多情况要考虑的话,代码看起来很不雅观; 我有一个巨大的开关语句,在每种情况下,代码非常相似,一遍又一遍地重复。
有一个更好,更清洁的方法来select一个随机数字时,你有大量的概率考虑一定的概率? (像〜30)
这是一个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,1以概率0.3返回2,以概率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) }
有一个更好,更清洁的方法来select一个随机数字时,你有大量的概率考虑一定的概率?
当然。 编写一个基于概率表生成数字的函数。 这基本上是你所指的switch语句是:在代码中定义的表。 你可以用数据做同样的事情,使用一个定义为概率和结果列表的表格:
概率结果 ----------- ------- 0.4 1 0.2 2 0.1 3 0.15 4 0.15 5
现在你可以随机select一个0到1之间的数字。 从列表顶部开始,将可能性加起来,直到超出所选的数字,然后使用相应的结果。 例如,假设您select的数字是0.6527637。 从顶部开始:0.4更小,所以继续前进。 0.6(0.4 + 0.2)更小,所以继续下去。 0.7(0.6 + 0.1)更大,所以停下来。 结果是3。
为了清楚起见,我在这里保留了简短的表格,但是只要你喜欢,你可以把它定义在一个数据文件中,这样当列表改变时你就不必重新编译了。
请注意,关于这个方法没有什么特别的Swift特性 – 你可以在C或Swift或Lisp中做同样的事情。
你可以用指数函数或二次函数来做 – 有x是你的随机数,y是新的随机数。 然后,你只需要微调方程,直到它适合你的用例。 说我有(x ^ 2)/ 10 +(x / 300)。 把你的随机数放入(作为一些浮点forms),然后在出现时用Int()来发言。 所以,如果我的随机数发生器从0到9,我有40%的几率获得0,有30%的几率得到1 – 3,20%的几率得到4 – 6,10% 8.你基本上试图伪造某种正态分布。
下面是Swift中的样子:
func giveY (x: UInt32) -> Int { let xD = Double(x) return Int(xD * xD / 10 + xD / 300) } let ans = giveY (arc4random_uniform(10))
编辑:
上面我不是很清楚 – 我的意思是你可以用一些函数来replaceswitch语句,这个函数会返回一组数字,并且用一个概率分布来表示,你可以用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之间返回一个双精度值。 那会是你的“x%差/比当前项目水平更好”的数字。 但是,由于它是一个指数函数,它不会返回数字的均匀分布。 arc4random_uniform(10)返回一个从0到9的整数,每一个会产生一个这样的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%)
这与其他人的可能性类似。 现在,对于你的function来说,要困难得多,其他的答案几乎肯定会更加适用和优雅。 但是你仍然可以做到这一点。
按照概率的顺序排列每个字母 – 从最大到最小。 然后,得到他们的累计和,从0开始,没有最后一个。 (所以50%,30%,20%的概率变成0,0.5,0.8)。 然后将它们相乘,直到它们具有合理的准确性(0,5,8)。 然后,绘制它们 – 你的累积概率就是你的x,你想用给定的概率(你的字母)select的东西就是你的。 (你显然不能在y轴上绘制实际的字母,所以你只需要在一些数组中绘制它们的索引)。 然后,你会尝试在那里find一些回归,这是你的function。 例如,尝试这些数字,我得到了
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”。 显然对于更多的字母来说很麻烦,而且要想find正确的回归还需要一段时间,如果你想改变权重,你必须手动完成。 但是如果你确实find了一个符合你的价值的好方程式,那么实际的function只有几行,而且很快。
根据标题,这是一个很好的机会,无耻插上我的小图书馆,swiftstats: https : //github.com/r0fls/swiftstats
答案和问题主体似乎不符合我对问题标题的期望,但是我的项目对标题要求有帮助:
用给定的分布生成随机数
例如,这将从均值为0和方差为1的正态分布生成3个随机variables:
import SwiftStats let n = SwiftStats.Distributions.Normal(0, 1.0) print(n.random())
支持的分布包括:正常,指数,二项式等…
它还支持将样本数据拟合到给定的分布,使用最大似然估计器进行分布。
有关更多信息,请参阅项目自述文件。