这是https://stackoverflow.com/a/8064754/1187415 中给出的算法的 Swift 实现,略带
修改,因为所有数字都必须是正数。
产生N个总和为M的正随机整数的方法是
- 构建一个包含数字 0 的数组,后跟 N-1 个不同的
1 .. M-1 范围内的随机数,最后是数字 M。
- 计算后续数组元素的差异。
在第一步中,我们需要 N-1 个元素的随机子集
集合 { 1, ..., M-1 }。这可以通过迭代这个来实现
以 n/m 的概率设置和选择每个元素,其中
m 是我们可以选择的剩余元素数量
n 是要选择的剩余元素数。
不是将选择的随机数存储在数组中,而是
立即计算与先前选择的数字的差异
并存储。
这给出了以下功能:
func randomNumbers(#count : Int, withSum sum : Int) -> [Int] {
precondition(sum >= count, "`sum` must not be less than `count`")
var diffs : [Int] = []
var last = 0 // last number chosen
var m = UInt32(sum - 1) // remaining # of elements to choose from
var n = UInt32(count - 1) // remaining # of elements to choose
for i in 1 ..< sum {
// Choose this number `i` with probability n/m:
if arc4random_uniform(m) < n {
diffs.append(i - last)
last = i
n--
}
m--
}
diffs.append(sum - last)
return diffs
}
println(randomNumbers(count: 4, withSum: 24))
如果所有元素都相等(例如 6+6+6+6=24)的解不是
允许,您可以重复该方法,直到找到有效的解决方案:
func differentRandomNumbers(#count : Int, withSum sum : Int) -> [Int] {
precondition(count >= 2, "`count` must be at least 2")
var v : [Int]
do {
v = randomNumbers(count: count, withSum: sum)
} while (!contains(v, { $0 != v[0]} ))
return v
}
这是一个简单的测试。它计算 1,000,000 个随机表示
7 作为 3 个正整数之和,并计算分布
结果。
let set = NSCountedSet()
for i in 1 ... 1_000_000 {
let v = randomNumbers(count: 3, withSum: 7)
set.addObject(v)
}
for (_, v) in enumerate(set) {
let count = set.countForObject(v)
println("\(v as! [Int]) \(count)")
}
结果:
[1, 4, 2] 66786
[1, 5, 1] 67082
[3, 1, 3] 66273
[2, 2, 3] 66808
[2, 3, 2] 66966
[5, 1, 1] 66545
[2, 1, 4] 66381
[1, 3, 3] 67153
[3, 3, 1] 67034
[4, 1, 2] 66423
[3, 2, 2] 66674
[2, 4, 1] 66418
[4, 2, 1] 66292
[1, 1, 5] 66414
[1, 2, 4] 66751
Swift 3 更新:
func randomNumbers(count : Int, withSum sum : Int) -> [Int] {
precondition(sum >= count, "`sum` must not be less than `count`")
var diffs : [Int] = []
var last = 0 // last number chosen
var m = UInt32(sum - 1) // remaining # of elements to choose from
var n = UInt32(count - 1) // remaining # of elements to choose
for i in 1 ..< sum {
// Choose this number `i` with probability n/m:
if arc4random_uniform(m) < n {
diffs.append(i - last)
last = i
n -= 1
}
m -= 1
}
diffs.append(sum - last)
return diffs
}
print(randomNumbers(count: 4, withSum: 24))