簡體   English   中英

Swift:64 位整數的隨機數?

[英]Swift: Random number for 64-bit integers?

因此,在我當前的項目中,我需要使用 64 位整數,並且需要在高達 1000 億的范圍內抓取隨機數。 arc4random()/arc4random_uniform() 僅適用於無符號 32 位整數。

我可能會稍微捏造一下,因為我每次調用的最小/最大范圍可能不會超過 20 億,但我想對自己進行未來驗證,以防我決定,好吧,我確實需要更廣泛的范圍。

有什么建議嗎?

更新: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 的冪時,此方法會遇到“模偏差”問題(請參閱為什么人們說使用隨機數生成器時存在模偏差? )。 在這里,我已將答案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 次迭代。)

也許你可以用兩個 32 位整數組成它:

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

以下是我通常包含在我的項目中的一些助手。 請注意 UInt64 bounded helper,它的工作方式與 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)))
    }
}

這是一個巧妙的解決方案! (反正我是這么想的,因為我剛剛編的)

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

您可以使用UInt64.random(in:)UInt64.max API 生成隨機無符號 64 位數字:

UInt64.random(in: 0...UInt64.max)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM