I have one value like 24, and I have four textboxes. How can I dynamically generate four values that add up to 24?
All the values must be integers and can\'t be nega
Here is a Swift implementation of the algorithm given in https://stackoverflow.com/a/8064754/1187415, with a slight modification because all numbers are required to be positive.
The method to producing N positive random integers with sum M is
In the first step, we need a random subset of N-1 elements out of the set { 1, ..., M-1 }. This can be achieved by iterating over this set and choosing each element with probability n/m, where m is the remaining number of elements we can choose from and n is the remaining number of elements to choose.
Instead of storing the chosen random numbers in an array, the difference to the previously chosen number is computed immediately and stored.
This gives the following function:
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))
If a solution with all elements equal (e.g 6+6+6+6=24) is not allowed, you can repeat the method until a valid solution is found:
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
}
Here is a simple test. It computes 1,000,000 random representations of 7 as the sum of 3 positive integers, and counts the distribution of the results.
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)")
}
Result:
[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
Update for 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))