Swift:64位整数的随机数?

时间:2014-10-24 14:20:37

标签: ios random swift xcode6 arc4random

因此,在我目前的项目中,我需要使用64位整数,我需要在范围高达1000亿之间获取随机数。 arc4random()/ arc4random_uniform()仅适用于无符号32位整数。

我可能会稍微捏一下,因为我的每次通话的最小/最大范围可能不会超过20亿,但是如果我决定这样做,那么我希望将来自我保护,我确实需要更广泛的范围

有什么建议吗?

4 个答案:

答案 0 :(得分:25)

更新:从 Swift 4.2 开始(随Xcode 10.1一起发布),Swift标准库中有一个统一的随机API,请参阅

您只需致电

即可
UInt64.random(in: minValue ... maxValue)

获取给定范围内的随机数。


(Swift的前一个答案< 4.2:)使用arc4random_buf(),你可以创建“任意大的”随机数, 所以这可能是一个解决方案:

// Swift 2:
func random64(upper_bound: UInt64) -> UInt64 {

    // Generate 64-bit random number:
    var rnd : UInt64 = 0
    arc4random_buf(&rnd, sizeofValue(rnd))

    return rnd % upper_bound
}

// Swift 3:
func random64(upper_bound: UInt64) -> UInt64 {

    // Generate 64-bit random number:
    var rnd : UInt64 = 0
    arc4random_buf(&rnd, MemoryLayout.size(ofValue: rnd))

    return rnd % upper_bound
}

当上限不是2的幂时,此方法会遇到“模偏差”问题(参见Why do people say there is modulo bias when using a random number generator?)。我在这里翻译了答案 https://stackoverflow.com/a/10989061/1187415从上面的帖子到Swift:

// Swift 2:
func random64(upper_bound: UInt64) -> UInt64 {

    // Generate 64-bit random value in a range that is
    // divisible by upper_bound:
    let range = UInt64.max - UInt64.max % upper_bound
    var rnd : UInt64 = 0
    repeat {
        arc4random_buf(&rnd, sizeofValue(rnd))
    } while rnd >= range

    return rnd % upper_bound
}

// Swift 3:
func random64(upper_bound: UInt64) -> UInt64 {

    // Generate 64-bit random value in a range that is
    // divisible by upper_bound:
    let range = UInt64.max - UInt64.max % upper_bound
    var rnd : UInt64 = 0
    repeat {
        arc4random_buf(&rnd, MemoryLayout.size(ofValue: rnd))
    } while rnd >= range

    return rnd % upper_bound
}

(乍一看,它看起来好像循环可能没有终止,但它可以显示出来 平均需要少于2次迭代。)

答案 1 :(得分:17)

也许你可以把它组成32位整数:

var random64 = Int64(arc4random()) + (Int64(arc4random()) << 32)

答案 2 :(得分:2)

以下是我通常在项目中包含的一些助手。注意UInt64有界帮助器,它的工作方式与Martin R的答案大致相同,除了检查范围小于UInt32.max且只执行一次对arc4random()的调用的情况。

extension UInt32 {

    /// Returns a random number in `0...UInt32.max`
    static func random() -> UInt32 {
        return arc4random()
    }

    /// Returns a random number in `0..<upperBound`
    static func random(_ upperBound: UInt32) -> UInt32 {
        return arc4random_uniform(upperBound)
    }
}

extension UInt64 {

    private static func randomLowerHalf() -> UInt64 {
        return UInt64(UInt32.random())
    }

    private static func randomLowerHalf(_ upperBound: UInt32) -> UInt64 {
        return UInt64(UInt32.random(upperBound))
    }

    static func random() -> UInt64 {
        return (randomLowerHalf() << 32) &+ randomLowerHalf()
    }

    static func random(_ upperBound: UInt64) -> UInt64 {
        if let upperBound = UInt32(exactly: upperBound) {
            return randomLowerHalf(upperBound)
        } else if UInt64(UInt32.max) == upperBound {
            return randomLowerHalf()
        } else {
            var result: UInt64
            repeat {
                result = random()
            } while result >= upperBound
            return result
        }
    }
}

extension Int32 {

    static func random() -> Int32 {
        return Int32(bitPattern: UInt32.random())
    }

    static func random(_ upperBound: Int32) -> Int32 {
        assert(0 < upperBound, "upperBound(\(upperBound)) must be greater than 0")
        return Int32(bitPattern: UInt32.random(UInt32(bitPattern: upperBound)))
    }
}

extension Int64 {

    static func random() -> Int64 {
        return Int64(bitPattern: UInt64.random())
    }

    static func random(_ upperBound: Int64) -> Int64 {
        assert(0 < upperBound, "upperBound(\(upperBound)) must be greater than 0")
        return Int64(bitPattern: UInt64.random(UInt64(bitPattern: upperBound)))
    }
}

extension Int {

    static func random() -> Int {
        return Int(IntMax.random())
    }

    static func random(_ upperBound: Int) -> Int {
        assert(0 < upperBound, "upperBound(\(upperBound)) must be greater than 0")
        return Int(IntMax.random(IntMax(upperBound)))
    }
}

答案 3 :(得分:2)

这是一个简洁的解决方案! (无论如何,因为我刚刚做完了)

let hex = UUID().uuidString.components(separatedBy: "-").suffix(2).joined()
let rand = UInt64(hex, radix: 0x10)

使用Swift REPL进行快速测试:

https://repl.it/GeIs/0

for _ in 0..<5_000_000 {
    let hex = UUID().uuidString.components(separatedBy: "-").suffix(2).joined()
    set.insert(UInt64(hex, radix: 0x10)!)
}
set.count // prints 5_000_000

作为延伸......

import Foundation


extension UInt64 {

    static var random: UInt64 {

        let hex = UUID().uuidString
            .components(separatedBy: "-")
            .suffix(2)
            .joined()

        return UInt64(hex, radix: 0x10)!
    }
}