Split a random value into four that sum up to it

后端 未结 6 892
被撕碎了的回忆
被撕碎了的回忆 2020-12-20 05:50

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

6条回答
  •  忘掉有多难
    2020-12-20 06:27

    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

    • Build an array containing the number 0, followed by N-1 different random numbers in the range 1 .. M-1, and finally the number M.
    • Compute the differences of subsequent array elements.

    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))
    

提交回复
热议问题