How can I generate large, ranged random numbers in Swift? -
i'm looking efficient method of generating large numbers (that includes floating point types!) in swift, arbitrary ranges (which may uint.max
or int.max
)
all existing questions i've seen either crash large values (uint.max
) or don't support ranges. know can read /dev/urandom
random bytes, doesn't restrict these values given interval (and i'm pretty sure looping until isn't efficient).
here possible solution uint
, int
, double
works full range of types. written extension methods (now updated swift 2), same can done global functions.
note arc4random_uniform()
produces 32-bit numbers only, can not used if int
/uint
64-bit integers (which case os x machines , newer ios devices).
for uint
use technique https://stackoverflow.com/a/26550169/1187415 (which swift translation of https://stackoverflow.com/a/10989061/1187415). case range covers full range of uint
treated separately.
extension uint { static func random(minvalue minvalue : uint, maxvalue : uint) -> uint { precondition(minvalue <= maxvalue, "attempt call random() minvalue > maxvalue") if minvalue == uint.min && maxvalue == uint.max { // random number in full range of uint: var rnd : uint = 0 arc4random_buf(&rnd, sizeofvalue(rnd)) return rnd } else { // compute random number in range 0 ... (maxvalue-minvalue), // using technique // https://stackoverflow.com/a/26550169/1187415, https://stackoverflow.com/a/10989061/1187415 // , avoiding "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` 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 reduced unsigned case using overflow operators , bitpattern:
conversion:
extension int { static func random(minvalue minvalue : int, maxvalue : int) -> int { precondition(minvalue <= maxvalue, "attempt call random() minvalue > maxvalue") // compute unsigned random number in range 0 ... (maxvalue-minvalue): let diff = uint(bitpattern: maxvalue &- minvalue) let rnd = uint.random(minvalue: 0, maxvalue: diff) // transform `rnd` 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, straight-forward implementation double
:
extension double { static func random(minvalue minvalue : double, maxvalue : double) -> double { precondition(minvalue <= maxvalue, "attempt call random() minvalue > maxvalue") // random floating point number in range 0.0 ... 1.0: let rnd = double(uint.random(minvalue: 0, maxvalue: uint.max))/double(uint.max) // scale range minvalue ... maxvalue: return minvalue + rnd * (maxvalue - minvalue) } }
example:
let d = double.random(minvalue: 10.5, maxvalue: 123.5)
update swift 3:
extension uint { static func random(minvalue: uint, maxvalue: uint) -> uint { precondition(minvalue <= maxvalue, "attempt call random() minvalue > maxvalue") if minvalue == uint.min && maxvalue == uint.max { // random number in full range of uint: var rnd: uint = 0 arc4random_buf(&rnd, memorylayout.size(ofvalue: rnd)) return rnd } else { // compute random number in range 0 ... (maxvalue-minvalue), // using technique // https://stackoverflow.com/a/26550169/1187415, https://stackoverflow.com/a/10989061/1187415 // , avoiding "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` range minvalue ... maxvalue: return minvalue + rnd } } } extension int { static func random(minvalue: int, maxvalue: int) -> int { precondition(minvalue <= maxvalue, "attempt call random() minvalue > maxvalue") // compute unsigned random number in range 0 ... (maxvalue-minvalue): let diff = uint(bitpattern: maxvalue &- minvalue) let rnd = uint.random(minvalue: 0, maxvalue: diff) // transform `rnd` range minvalue ... maxvalue: return minvalue &+ int(bitpattern: rnd) } } extension double { static func random(minvalue: double, maxvalue: double) -> double { precondition(minvalue <= maxvalue, "attempt call random() minvalue > maxvalue") // random floating point number in range 0.0 ... 1.0: let rnd = double(uint.random(minvalue: 0, maxvalue: uint.max))/double(uint.max) // scale range minvalue ... maxvalue: return minvalue + rnd * (maxvalue - minvalue) } }
Comments
Post a Comment