Swift: случайное число для 64-битных целых чисел?
Итак, с моим текущим проектом мне нужно работать с 64-битными целыми числами, и мне нужно захватить случайные числа между диапазонами до 100 миллиардов. arc4random()/arc4random_uniform() работает только с 32-битными целыми знаками без знака.
Возможно, я немного помассирую его, потому что мой диапазон min/max для каждого звонка, вероятно, не превысит 2 миллиарда, но я бы хотел, чтобы будущая защита была на тот случай, если я решила, что мне нужен более широкий диапазон.
Любые советы?
Ответы
Ответ 1
Обновление: Начиная с Swift 4.2 (распространяется с Xcode 10.1) в стандартной библиотеке Swift есть унифицированный случайный API, см.
С недавним снимком разработчика Swift 4.2 вы можете просто позвонить
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 (см. Почему люди говорят, что при использовании генератора случайных чисел существует смещение по модулю?). Здесь я перевел ответ fooobar.com/questions/7093/... сверху ветки на 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 итераций.)
Ответ 2
Возможно, вы можете записать его в 32-битные целые числа:
var random64 = Int64(arc4random()) + (Int64(arc4random()) << 32)
Ответ 3
Вот некоторые помощники, которые я обычно включаю в свои проекты. Обратите внимание на ограниченный помощник 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)))
}
}
Ответ 4
Вот одно опрятное решение! (все равно, так как я только что закончил)
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)!
}
}