如何在Swift中生成大范围随机数?

如何在Swift中生成大范围随机数?,swift,random,floating-point,integer,long-integer,Swift,Random,Floating Point,Integer,Long Integer,我正在寻找一种在Swift中生成任意范围(甚至可能是UInt.max或Int.max)的大数(包括浮点类型!)的有效方法 我所看到的所有现有问题要么因大值(UInt.max)而崩溃,要么不支持范围。我知道您可以从/dev/uradom读取随机字节,但这无助于将这些值限制在给定的时间间隔内(我非常确定循环直到它无效)。这里有一个可能的解决方案,用于UInt,Int和Double,它可以与 这些类型的全部范围。它是作为扩展方法编写的 (现在针对Swift 2进行了更新),但全局函数也可以这样做 请注

我正在寻找一种在Swift中生成任意范围(甚至可能是
UInt.max
Int.max
)的大数(包括浮点类型!)的有效方法


我所看到的所有现有问题要么因大值(
UInt.max
)而崩溃,要么不支持范围。我知道您可以从
/dev/uradom
读取随机字节,但这无助于将这些值限制在给定的时间间隔内(我非常确定循环直到它无效)。

这里有一个可能的解决方案,用于
UInt
Int
Double
,它可以与 这些类型的全部范围。它是作为扩展方法编写的 (现在针对Swift 2进行了更新),但全局函数也可以这样做

请注意,
arc4random_uniform()
只生成32位数字,因此 如果
Int
/
UInt
是64位整数,则不能使用 适用于所有OS X机器和所有较新的iOS设备)

对于
UInt
我们使用 (这只是一个快速的翻译)。 该范围覆盖整个
UInt
范围的情况单独处理

extension UInt {
    static func random(minValue minValue : UInt, maxValue : UInt) -> UInt {
        precondition(minValue <= maxValue, "attempt to call random() with minValue > maxValue")

        if minValue == UInt.min && maxValue == UInt.max {
            // Random number in the full range of UInt:

            var rnd : UInt = 0
            arc4random_buf(&rnd, sizeofValue(rnd))
            return rnd
        } else {
            // Compute random number in the range 0 ... (maxValue-minValue),
            // using the technique from 
            // https://stackoverflow.com/a/26550169/1187415, https://stackoverflow.com/a/10989061/1187415
            // and avoiding the "modulo bias problem":

            let range = maxValue - minValue + 1
            let randLimit = UInt.max - UInt.max % range
            var rnd : UInt = 0
            repeat {
                arc4random_buf(&rnd, sizeofValue(rnd))
            } while rnd >= randLimit
            rnd = rnd % range

            // Transform `rnd` back to the range minValue ... maxValue:
            return minValue + rnd
        }
    }
}
有符号整数的大小写可以使用 溢出运算符和
位模式:
转换:

extension Int {
    static func random(minValue minValue : Int, maxValue : Int) -> Int {
        precondition(minValue <= maxValue, "attempt to call random() with minValue > maxValue")

        // Compute unsigned random number in the range 0 ... (maxValue-minValue):
        let diff = UInt(bitPattern: maxValue &- minValue)
        let rnd = UInt.random(minValue: 0, maxValue: diff)

        // Transform `rnd` back to the range minValue ... maxValue:
        return minValue &+ Int(bitPattern: rnd)
    }
}
最后,一个直接的
Double
实现:

extension Double {
    static func random(minValue minValue : Double, maxValue : Double) -> Double {
        precondition(minValue <= maxValue, "attempt to call random() with minValue > maxValue")

        // Random floating point number in the range 0.0 ... 1.0:
        let rnd = Double(UInt.random(minValue: 0, maxValue: UInt.max))/Double(UInt.max)

        // Scale to range minValue ... maxValue:
        return minValue + rnd * (maxValue - minValue)
    }
}

Swift 3的更新:

extension UInt {
    static func random(minValue: UInt, maxValue: UInt) -> UInt {
        precondition(minValue <= maxValue, "attempt to call random() with minValue > maxValue")

        if minValue == UInt.min && maxValue == UInt.max {
            // Random number in the full range of UInt:

            var rnd: UInt = 0
            arc4random_buf(&rnd, MemoryLayout.size(ofValue: rnd))
            return rnd
        } else {
            // Compute random number in the range 0 ... (maxValue-minValue),
            // using the technique from 
            // https://stackoverflow.com/a/26550169/1187415, https://stackoverflow.com/a/10989061/1187415
            // and avoiding the "modulo bias problem":

            let range = maxValue - minValue + 1
            let randLimit = UInt.max - UInt.max % range
            var rnd: UInt = 0
            repeat {
                arc4random_buf(&rnd, MemoryLayout.size(ofValue: rnd))
            } while rnd >= randLimit
            rnd = rnd % range

            // Transform `rnd` back to the range minValue ... maxValue:
            return minValue + rnd
        }
    }
}

extension Int {
    static func random(minValue: Int, maxValue: Int) -> Int {
        precondition(minValue <= maxValue, "attempt to call random() with minValue > maxValue")

        // Compute unsigned random number in the range 0 ... (maxValue-minValue):
        let diff = UInt(bitPattern: maxValue &- minValue)
        let rnd = UInt.random(minValue: 0, maxValue: diff)

        // Transform `rnd` back to the range minValue ... maxValue:
        return minValue &+ Int(bitPattern: rnd)
    }
}

extension Double {
    static func random(minValue: Double, maxValue: Double) -> Double {
        precondition(minValue <= maxValue, "attempt to call random() with minValue > maxValue")

        // Random floating point number in the range 0.0 ... 1.0:
        let rnd = Double(UInt.random(minValue: 0, maxValue: UInt.max))/Double(UInt.max)

        // Scale to range minValue ... maxValue:
        return minValue + rnd * (maxValue - minValue)
    }
}
扩展单元{
静态函数随机(最小值:UInt,最大值:UInt)->UInt{
前提条件(minValue=randLimit
rnd=rnd%范围
//将“rnd”转换回范围minValue…maxValue:
返回最小值+rnd
}
}
}
扩展整数{
静态func随机(minValue:Int,maxValue:Int)->Int{
前提条件(最小值双精度){

前提条件(minValue这里有一种可能的解决方案,适用于
UInt
Int
Double
,可与 这些类型的完整范围。它是作为扩展方法编写的 (现在针对Swift 2进行了更新),但全局函数也可以这样做

请注意,
arc4random_uniform()
只生成32位数字,因此 如果
Int
/
UInt
是64位整数,则不能使用 适用于所有OS X机器和所有较新的iOS设备)

对于
UInt
我们使用 (这只是一个快速的翻译)。 该范围覆盖整个
UInt
范围的情况单独处理

extension UInt {
    static func random(minValue minValue : UInt, maxValue : UInt) -> UInt {
        precondition(minValue <= maxValue, "attempt to call random() with minValue > maxValue")

        if minValue == UInt.min && maxValue == UInt.max {
            // Random number in the full range of UInt:

            var rnd : UInt = 0
            arc4random_buf(&rnd, sizeofValue(rnd))
            return rnd
        } else {
            // Compute random number in the range 0 ... (maxValue-minValue),
            // using the technique from 
            // https://stackoverflow.com/a/26550169/1187415, https://stackoverflow.com/a/10989061/1187415
            // and avoiding the "modulo bias problem":

            let range = maxValue - minValue + 1
            let randLimit = UInt.max - UInt.max % range
            var rnd : UInt = 0
            repeat {
                arc4random_buf(&rnd, sizeofValue(rnd))
            } while rnd >= randLimit
            rnd = rnd % range

            // Transform `rnd` back to the range minValue ... maxValue:
            return minValue + rnd
        }
    }
}
有符号整数的大小写可以使用 溢出运算符和
位模式:
转换:

extension Int {
    static func random(minValue minValue : Int, maxValue : Int) -> Int {
        precondition(minValue <= maxValue, "attempt to call random() with minValue > maxValue")

        // Compute unsigned random number in the range 0 ... (maxValue-minValue):
        let diff = UInt(bitPattern: maxValue &- minValue)
        let rnd = UInt.random(minValue: 0, maxValue: diff)

        // Transform `rnd` back to the range minValue ... maxValue:
        return minValue &+ Int(bitPattern: rnd)
    }
}
最后,一个直接的
Double
实现:

extension Double {
    static func random(minValue minValue : Double, maxValue : Double) -> Double {
        precondition(minValue <= maxValue, "attempt to call random() with minValue > maxValue")

        // Random floating point number in the range 0.0 ... 1.0:
        let rnd = Double(UInt.random(minValue: 0, maxValue: UInt.max))/Double(UInt.max)

        // Scale to range minValue ... maxValue:
        return minValue + rnd * (maxValue - minValue)
    }
}

Swift 3的更新:

extension UInt {
    static func random(minValue: UInt, maxValue: UInt) -> UInt {
        precondition(minValue <= maxValue, "attempt to call random() with minValue > maxValue")

        if minValue == UInt.min && maxValue == UInt.max {
            // Random number in the full range of UInt:

            var rnd: UInt = 0
            arc4random_buf(&rnd, MemoryLayout.size(ofValue: rnd))
            return rnd
        } else {
            // Compute random number in the range 0 ... (maxValue-minValue),
            // using the technique from 
            // https://stackoverflow.com/a/26550169/1187415, https://stackoverflow.com/a/10989061/1187415
            // and avoiding the "modulo bias problem":

            let range = maxValue - minValue + 1
            let randLimit = UInt.max - UInt.max % range
            var rnd: UInt = 0
            repeat {
                arc4random_buf(&rnd, MemoryLayout.size(ofValue: rnd))
            } while rnd >= randLimit
            rnd = rnd % range

            // Transform `rnd` back to the range minValue ... maxValue:
            return minValue + rnd
        }
    }
}

extension Int {
    static func random(minValue: Int, maxValue: Int) -> Int {
        precondition(minValue <= maxValue, "attempt to call random() with minValue > maxValue")

        // Compute unsigned random number in the range 0 ... (maxValue-minValue):
        let diff = UInt(bitPattern: maxValue &- minValue)
        let rnd = UInt.random(minValue: 0, maxValue: diff)

        // Transform `rnd` back to the range minValue ... maxValue:
        return minValue &+ Int(bitPattern: rnd)
    }
}

extension Double {
    static func random(minValue: Double, maxValue: Double) -> Double {
        precondition(minValue <= maxValue, "attempt to call random() with minValue > maxValue")

        // Random floating point number in the range 0.0 ... 1.0:
        let rnd = Double(UInt.random(minValue: 0, maxValue: UInt.max))/Double(UInt.max)

        // Scale to range minValue ... maxValue:
        return minValue + rnd * (maxValue - minValue)
    }
}
扩展单元{
静态函数随机(最小值:UInt,最大值:UInt)->UInt{
前提条件(minValue=randLimit
rnd=rnd%范围
//将“rnd”转换回范围minValue…maxValue:
返回最小值+rnd
}
}
}
扩展整数{
静态func随机(minValue:Int,maxValue:Int)->Int{
前提条件(最小值双精度){

前提条件(minValue不是你问题的完整答案,但这里有64位随机数的解决方案:@MartinR:我见过这个问题。你的答案是如何解释最小/最大限制的?哎呀!我刚刚知道了,忽略我的问题。请将你的答案发布在这里,供将来的访问者使用。@ndmeiri:我在评论中指的是我的问题。不完整。@ndmeiri:回答您的问题,但这里有64位随机数的解决方案:@MartinR:我看到了这个问题。您的答案如何解释最小/最大限制?哎呀!我刚得到答案,忽略我的问题。请将您的答案发布在这里,供未来的访问者使用。@ndmeiri:我在评论中提到了我的问题。我非常感谢您!我只是即时消息我要说的是,表演非常精彩。谢谢你,@MartinR。我想你的意思是
重复{…}虽然
取代了
直到
在Swift 2版本中?@Stefan:是的,谢谢。我现在用Swift 2替换了Swift 1.2版本以避免混淆,希望现在可以了。我非常感谢你!我刚刚实现了这一点,我不得不说性能非常好。谢谢你,@MartinR。我想你的意思是
重复{…}虽然
而不是直到在Swift 2版本中?@Stefan:是的,谢谢。为了避免混淆,我现在用Swift 2替换了Swift 1.2版本,希望现在可以了。