我如何在Swift中生成大量的随机数字? [英] How can I generate large, ranged random numbers in Swift?

查看:170
本文介绍了我如何在Swift中生成大量的随机数字?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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



所有现有的问题, $ c> UInt.max )或不支持范围。我知道你可以从 / dev / urandom 读取随机字节,但是这无助于限制这些值给定的时间间隔它是没有效率的)。

解决方案

这是 UInt Int Double ,它们与
一起使用这些类型的全部范围。它被写成扩展方法
(现在更新为Swift 2),但是可以用全局函数来完成。



请注意 arc4random_uniform()只产生32位数字,所以如果 Int / 不能使用
, UInt
是64位整数(所有OS X机器和所有新的iOS设备都是
)。



UInt 我们使用 https://stackoverflow.com/a/26550169 / 1187415
(这只是一个Swift翻译的 https://stackoverflow.com/a/10989061/ 1187415 )。
范围覆盖 UInt 全部范围的情况分开处理。

 扩展UInt {
static func random(minValue minValue:UInt,maxValue:UInt) - > UInt {
precondition(minValue< = maxValue,尝试用minValue调用random()> maxValue)

if minValue == UInt.min&& maxValue == UInt.max {
//在UInt完整范围内的随机数:

var rnd:UInt = 0
arc4random_buf(& rnd,sizeofValue(rnd) )
return rnd
} else {
//计算范围为0 ...(maxValue-minValue),
的随机数,使用
的技巧// https://stackoverflow.com/a/26550169/1187415,https://stackoverflow.com/a/10989061/1187415
//并避免模偏移问题:

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

//将rnd转换回范围minValue ... maxValue :
return minValue + rnd
}


$ / code $


例子:

  let u1 = UInt.random(minValue:1000,maxValue:2000)
let u2 = UInt.random(minValue:UInt.min,maxValue:UInt.max )

使用
溢出操作符可以将有符号整数的情况简化为无符号的情况和 bitPattern:转换:

 扩展名Int {
static func random(minValue minValue:Int,maxValue:Int) - > Int {
precondition(minValue< = maxValue,尝试用minValue调用random()> maxValue)

//计算范围0 ...中的无符号随机数maxValue-minValue):
let diff = UInt(bitPattern:maxValue& - minValue)
let rnd = UInt.random(minValue:0,maxValue:diff)

/ /将'rnd'转换回范围minValue ... maxValue:
return minValue& + Int(bitPattern:rnd)
}
}



例子:

  let i1 = Int。 random(minValue:-1000,maxValue:1000)
let i2 = Int.random(minValue:Int.min,maxValue:Int.max)

最后,直接执行 Double

 扩展Double {
static func random(minValue minValue:Double,maxValue:Double) - > Double {
precondition(minValue< = maxValue,尝试用minValue调用random()> maxValue)

// 0.0 ... 1.0范围内的随机浮点数:
let rnd = Double(UInt.random(minValue:0,maxValue:UInt.max))/ Double(UInt.max)

//缩放到范围minValue ... maxValue :
return minValue + rnd *(maxValue - minValue)
}
}

示例:

  let d = Double.random(minValue:10.5,maxValue:123.5)






Swift 3的更新:

 扩展UInt {
static func random(minValue:UInt,maxValue:UInt) - > UInt {
precondition(minValue< = maxValue,尝试用minValue调用random()> maxValue)

if minValue == UInt.min&& maxValue == UInt.max {
//在整个UInt范围内的随机数:

var rnd:UInt = 0
arc4random_buf(& rnd,MemoryLayout.size的值):
return rnd
} else {
//计算0 ...(maxValue-minValue),
范围内的随机数
// https://stackoverflow.com/a/26550169/1187415,https://stackoverflow.com/a/10989061/1187415
//并避免模偏移问题:

let range = maxValue - minValue + 1
let randLimit = UInt.max - UInt.max%range
var rnd:UInt = 0
repeat {
当rnd> = randLimit
rnd = rnd%range

时,arc4random_buf(& rnd,MemoryLayout.size(ofValue:rnd))
}范围minValue ... maxValue:
返回minV alue + rnd
}
}
}

扩展Int {
static func random(minValue:Int,maxValue:Int) - > Int {
precondition(minValue< = maxValue,尝试用minValue调用random()> maxValue)

//计算范围0 ...中的无符号随机数maxValue-minValue):
let diff = UInt(bitPattern:maxValue& - minValue)
let rnd = UInt.random(minValue:0,maxValue:diff)

/ /将'rnd'转换回范围minValue ... maxValue:
return minValue& + Int(bitPattern:rnd)
}
}

扩展名Double {
static func random(minValue:Double,maxValue:Double) - > Double {
precondition(minValue< = maxValue,尝试用minValue调用random()> maxValue)

// 0.0 ... 1.0范围内的随机浮点数:
let rnd = Double(UInt.random(minValue:0,maxValue:UInt.max))/ Double(UInt.max)

//缩放到范围minValue ... maxValue :
return minValue + rnd *(maxValue - minValue)
}
}


I'm looking for an efficient method of generating large numbers (that includes floating point types!) in Swift, with arbitrary ranges (which may even be UInt.max or Int.max)

All the existing questions I've seen either crash for large values (UInt.max) or don't support ranges. I know that you can read from /dev/urandom for random bytes, but that doesn't help restrict these values to a given interval (and I'm pretty sure looping until it does isn't efficient).

解决方案

Here is a possible solution for UInt, Int and Double which works with the full range of those types. It is written as extension methods (now updated for Swift 2), but the same can be done with global functions.

Note that arc4random_uniform() produces 32-bit numbers only, so that can not be used if Int/UInt are 64-bit integers (which is the case for all OS X machines and all newer iOS devices).

For UInt we use the technique from https://stackoverflow.com/a/26550169/1187415 (which is just a Swift translation of https://stackoverflow.com/a/10989061/1187415). The case where the range covers the full range of UInt is treated separately.

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

Examples:

let u1 = UInt.random(minValue: 1000, maxValue: 2000)
let u2 = UInt.random(minValue: UInt.min, maxValue: UInt.max)

The case of signed integers can be reduced to the unsigned case using the overflow operators and the bitPattern: conversion:

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

Examples:

let i1 = Int.random(minValue: -1000, maxValue: 1000)
let i2 = Int.random(minValue: Int.min, maxValue: Int.max)

Finally, a straight-forward implementation for 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)
    }
}

Example:

let d = Double.random(minValue: 10.5, maxValue: 123.5)


Update for 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)
    }
}

这篇关于我如何在Swift中生成大量的随机数字?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆