Я понимаю, что книга Свифта предоставила реализацию генератора случайных чисел. Является ли наилучшей практикой копировать и вставлять эту реализацию в собственную программу? Или есть библиотека, которая делает это, что мы можем использовать сейчас?
Я понимаю, что книга Свифта предоставила реализацию генератора случайных чисел. Является ли наилучшей практикой копировать и вставлять эту реализацию в собственную программу? Или есть библиотека, которая делает это, что мы можем использовать сейчас?
Ответы:
Swift 4.2+
Swift 4.2, поставляемый с Xcode 10, представляет новые простые в использовании случайные функции для многих типов данных. Вы можете вызвать random()
метод для числовых типов.
let randomInt = Int.random(in: 0..<6)
let randomDouble = Double.random(in: 2.71828...3.14159)
let randomBool = Bool.random()
Используйте arc4random_uniform(n)
для случайного целого числа от 0 до n-1.
let diceRoll = Int(arc4random_uniform(6) + 1)
Приведите результат к Int, чтобы вам не приходилось явно вводить ваши переменные как UInt32
(что кажется не-Swifty).
0
. В вашем коде, diceRoll
может быть 0
. Просто говорю ...
Int(arc4random_uniform(6)+1)
.
arc3random_uniform(n)
a, UInt32(n)
если вы используете значение, которое еще не относится к этому типу.
Редактировать: Обновлено для Swift 3.0
arc4random
хорошо работает в Swift, но базовые функции ограничены 32-битными целочисленными типами ( Int
64-битные на iPhone 5S и современных Mac). Вот обобщенная функция для случайного числа типа, выражаемого целочисленным литералом:
public func arc4random<T: ExpressibleByIntegerLiteral>(_ type: T.Type) -> T {
var r: T = 0
arc4random_buf(&r, MemoryLayout<T>.size)
return r
}
Мы можем использовать эту новую универсальную функцию для расширения UInt64
, добавления граничных аргументов и смягчения смещения по модулю. (Это снято прямо с arc4random.c )
public extension UInt64 {
public static func random(lower: UInt64 = min, upper: UInt64 = max) -> UInt64 {
var m: UInt64
let u = upper - lower
var r = arc4random(UInt64.self)
if u > UInt64(Int64.max) {
m = 1 + ~u
} else {
m = ((max - (u * 2)) + 1) % u
}
while r < m {
r = arc4random(UInt64.self)
}
return (r % u) + lower
}
}
При этом мы можем расширить Int64
для тех же аргументов, касающихся переполнения:
public extension Int64 {
public static func random(lower: Int64 = min, upper: Int64 = max) -> Int64 {
let (s, overflow) = Int64.subtractWithOverflow(upper, lower)
let u = overflow ? UInt64.max - UInt64(~s) : UInt64(s)
let r = UInt64.random(upper: u)
if r > UInt64(Int64.max) {
return Int64(r - (UInt64(~lower) + 1))
} else {
return Int64(r) + lower
}
}
}
Чтобы завершить семью ...
private let _wordSize = __WORDSIZE
public extension UInt32 {
public static func random(lower: UInt32 = min, upper: UInt32 = max) -> UInt32 {
return arc4random_uniform(upper - lower) + lower
}
}
public extension Int32 {
public static func random(lower: Int32 = min, upper: Int32 = max) -> Int32 {
let r = arc4random_uniform(UInt32(Int64(upper) - Int64(lower)))
return Int32(Int64(r) + Int64(lower))
}
}
public extension UInt {
public static func random(lower: UInt = min, upper: UInt = max) -> UInt {
switch (_wordSize) {
case 32: return UInt(UInt32.random(UInt32(lower), upper: UInt32(upper)))
case 64: return UInt(UInt64.random(UInt64(lower), upper: UInt64(upper)))
default: return lower
}
}
}
public extension Int {
public static func random(lower: Int = min, upper: Int = max) -> Int {
switch (_wordSize) {
case 32: return Int(Int32.random(Int32(lower), upper: Int32(upper)))
case 64: return Int(Int64.random(Int64(lower), upper: Int64(upper)))
default: return lower
}
}
}
После всего этого мы можем наконец сделать что-то вроде этого:
let diceRoll = UInt64.random(lower: 1, upper: 7)
var r = arc4random(UInt64)
. Пожалуйста, посоветуйте, что вы здесь имели в виду?
arc4random
(определенной в первом блоке кода) с аргументом UInt64
a Type
.
arc4random_buf
. Цель этих расширений состоит в том, чтобы делать именно то, что arc4random_uniform
делает (смягчать смещение по модулю), за исключением 64-битных типов.
Редактировать для Swift 4.2
Начиная с Swift 4.2, вместо использования импортированной функции C arc4random_uniform (), теперь вы можете использовать собственные встроенные функции Swift.
// Generates integers starting with 0 up to, and including, 10
Int.random(in: 0 ... 10)
Вы можете использовать, random(in:)
чтобы получить случайные значения для других примитивных значений; такие как Int, Double, Float и даже Bool.
Swift версии <4.2
Этот метод будет генерировать случайное Int
значение между заданным минимумом и максимумом
func randomInt(min: Int, max: Int) -> Int {
return min + Int(arc4random_uniform(UInt32(max - min + 1)))
}
Я использовал этот код:
var k: Int = random() % 10;
random is unavailable in Swift: Use arc4random instead.
Начиная с iOS 9, вы можете использовать новые классы GameplayKit для генерации случайных чисел различными способами.
У вас есть четыре типа источников на выбор: общий случайный источник (без имени, вплоть до системы, чтобы выбрать, что он делает), линейный конгруэнтный, ARC4 и Mersenne Twister. Они могут генерировать случайные целые числа, числа с плавающей запятой и значения типа bools.
На простейшем уровне вы можете сгенерировать случайное число из встроенного в систему случайного источника, например:
GKRandomSource.sharedRandom().nextInt()
Это создает число от -2 147 483 648 до 2 147 483 647. Если вы хотите число от 0 до верхней границы (исключая), вы должны использовать это:
GKRandomSource.sharedRandom().nextIntWithUpperBound(6)
В GameplayKit есть несколько удобных конструкторов для работы с кубиками. Например, вы можете бросить шестигранный кубик так:
let d6 = GKRandomDistribution.d6()
d6.nextInt()
Кроме того, вы можете формировать случайное распределение, используя такие вещи, как GKShuffledDistribution. Это займет немного больше объяснений, но если вам интересно, вы можете прочитать мой учебник по случайным числам GameplayKit .
import GameplayKit
. Swift 3 изменил синтаксис наGKRandomSource.sharedRandom().nextInt(upperBound: 6)
Вы можете сделать это так же, как в C:
let randomNumber = arc4random()
randomNumber
выводится как тип UInt32
(32-разрядное целое число без знака)
rand
, arc4random
, drand48
и друзья все в Darwin
модуле. Он уже импортирован для вас, если вы создаете приложение Cocoa, UIKit или Foundation, но вам это нужно на import Darwin
игровых площадках.
arc4random_uniform()
Применение:
arc4random_uniform(someNumber: UInt32) -> UInt32
Это дает вам случайные целые числа в диапазоне 0
до someNumber - 1
.
Максимальное значение для UInt32
4,294,967,295 (то есть 2^32 - 1
).
Примеры:
Подбрасывание монет
let flip = arc4random_uniform(2) // 0 or 1
Кубик ролл
let roll = arc4random_uniform(6) + 1 // 1...6
Случайный день в октябре
let day = arc4random_uniform(31) + 1 // 1...31
Случайный год в 1990-х
let year = 1990 + arc4random_uniform(10)
Общая форма:
let number = min + arc4random_uniform(max - min + 1)
где number
, max
и min
есть UInt32
.
arc4random ()
Вы также можете получить случайное число с помощью arc4random()
, который производит UInt32
от 0 до 2 ^ 32-1. Таким образом, чтобы получить случайное число между 0
и x-1
, вы можете разделить его на x
и взять остаток. Или, другими словами, используйте оператор остатка (%) :
let number = arc4random() % 5 // 0...4
Однако это приводит к небольшому смещению по модулю (см. Также здесь и здесь ), поэтому arc4random_uniform()
рекомендуется.
Преобразование в и из Int
Обычно это было бы хорошо , чтобы сделать что - то вроде этого, чтобы преобразовать обратно и вперед между Int
и UInt32
:
let number: Int = 10
let random = Int(arc4random_uniform(UInt32(number)))
Проблема, однако, заключается в том, что он Int
имеет диапазон -2,147,483,648...2,147,483,647
на 32-битных системах и диапазон -9,223,372,036,854,775,808...9,223,372,036,854,775,807
на 64-битных системах. Сравните это с UInt32
ассортиментом 0...4,294,967,295
. U
Из UInt32
средств без знака .
Рассмотрим следующие ошибки:
UInt32(-1) // negative numbers cause integer overflow error
UInt32(4294967296) // numbers greater than 4,294,967,295 cause integer overflow error
Так что вам просто нужно быть уверенным, что ваши входные параметры находятся в пределах UInt32
диапазона и что вам также не нужен выход, выходящий за пределы этого диапазона.
Я был в состоянии просто использовать, rand()
чтобы получить случайный CInt. Вы можете сделать это Int, используя что-то вроде этого:
let myVar: Int = Int(rand())
Вы можете использовать свою любимую C-случайную функцию и просто конвертировать ее в значение Int, если это необходимо.
random()
, который возвращает Int
скорее, чем UInt32
- и, как упомянуто @SomeGuy, просто вызывать srandom(arc4random())
один раз в любом месте, прежде чем использовать его, чтобы убедиться, что он имеет различное, рандомизированное начальное число для каждого выполнения вашей программы.
Ответ @ jstn хорош, но немного многословен. Swift известен как протоколно-ориентированный язык, поэтому мы можем достичь того же результата, не прибегая к реализации стандартного кода для каждого класса в целочисленном семействе, добавив реализацию по умолчанию для расширения протокола.
public extension ExpressibleByIntegerLiteral {
public static func arc4random() -> Self {
var r: Self = 0
arc4random_buf(&r, MemoryLayout<Self>.size)
return r
}
}
Теперь мы можем сделать:
let i = Int.arc4random()
let j = UInt32.arc4random()
и все другие целочисленные классы в порядке.
В Swift 4.2 вы можете генерировать случайные числа, вызывая random()
метод для любого числового типа, который вам нужен, предоставляя диапазон, с которым вы хотите работать. Например, при этом генерируется случайное число в диапазоне от 1 до 9 включительно с обеих сторон.
let randInt = Int.random(in: 1..<10)
Также с другими типами
let randFloat = Float.random(in: 1..<20)
let randDouble = Double.random(in: 1...30)
let randCGFloat = CGFloat.random(in: 1...40)
Вот библиотека, которая хорошо справляется со своей задачей https://github.com/thellimist/SwiftRandom
public extension Int {
/// SwiftRandom extension
public static func random(lower: Int = 0, _ upper: Int = 100) -> Int {
return lower + Int(arc4random_uniform(UInt32(upper - lower + 1)))
}
}
public extension Double {
/// SwiftRandom extension
public static func random(lower: Double = 0, _ upper: Double = 100) -> Double {
return (Double(arc4random()) / 0xFFFFFFFF) * (upper - lower) + lower
}
}
public extension Float {
/// SwiftRandom extension
public static func random(lower: Float = 0, _ upper: Float = 100) -> Float {
return (Float(arc4random()) / 0xFFFFFFFF) * (upper - lower) + lower
}
}
public extension CGFloat {
/// SwiftRandom extension
public static func random(lower: CGFloat = 0, _ upper: CGFloat = 1) -> CGFloat {
return CGFloat(Float(arc4random()) / Float(UINT32_MAX)) * (upper - lower) + lower
}
}
let MAX : UInt32 = 9
let MIN : UInt32 = 1
func randomNumber()
{
var random_number = Int(arc4random_uniform(MAX) + MIN)
print ("random = ", random_number);
}
Я хотел бы добавить к существующим ответам, что пример генератора случайных чисел в книге Свифта - это Линейный генератор конгруэнтности (LCG), он строго ограничен и не должен быть исключением за исключением тривиальных примеров, где качество случайности не не имеет значения вообще. И LCG никогда не должен использоваться в криптографических целях .
arc4random()
намного лучше и может использоваться для большинства целей, но опять же не должен использоваться в криптографических целях.
Если вы хотите что-то гарантированно криптографически безопасное, используйте SecCopyRandomBytes()
. Обратите внимание, что если вы встраиваете генератор случайных чисел во что-то, кто-то другой может (неправильно) использовать его для криптографических целей (таких как генерация пароля, ключа или соли), тогда вам следует рассмотреть возможность использования в SecCopyRandomBytes()
любом случае, даже если ваша потребность не Это вполне необходимо.
Существует новый набор API:
let randomIntFrom0To10 = Int.random(in: 0 ..< 10)
let randomDouble = Double.random(in: 1 ... 10)
Все числовые типы теперь имеют random(in:)
метод, который принимает range
.
Возвращает число, равномерно распределенное в этом диапазоне.
TL; DR
Вы должны использовать импортированные C API-интерфейсы (они отличаются для разных платформ) .
И более того...
Что если я скажу вам, что случайность не такая уж случайная?
Если вы используете arc4random()
(чтобы вычислить остаток) как arc4random() % aNumber
, результат не будет равномерно распределен между 0
и aNumber
. Существует проблема, которая называется смещением по модулю .
Модульный уклон
Как правило, функция генерирует случайное число между 0
и MAX (зависит от типа и т.д.) . Для быстрого и простого примера, скажем, максимальное число равно, 7
и вы заботитесь о случайном числе в диапазоне 0 ..< 2
(или интервале [0, 3), если вы предпочитаете это) .
Эти вероятности для отдельных чисел:
Другими словами, у вас больше шансов получить 0 или 1, чем 2 . Конечно, нужно помнить, что это чрезвычайно упрощено, а число MAX намного выше, что делает его более «справедливым».
Эта проблема решается с помощью SE-0202 - Случайное объединение в Swift 4.2
Без arc4Random_uniform () в некоторых версиях Xcode (в 7.1 он запускается, но не выполняет автозаполнение для меня). Вы можете сделать это вместо этого.
Для генерации случайного числа от 0-5. Первый
import GameplayKit
затем
let diceRoll = GKRandomSource.sharedRandom().nextIntWithUpperBound(6)
var randomNumber = Int(arc4random_uniform(UInt32(5)))
Здесь 5 убедится, что случайное число генерируется от нуля до четырех. Вы можете установить значение соответственно.
Swift 4.2
Пока, чтобы импортировать Фонд C lib arc4random_uniform()
// 1
let digit = Int.random(in: 0..<10)
// 2
if let anotherDigit = (0..<10).randomElement() {
print(anotherDigit)
} else {
print("Empty range.")
}
// 3
let double = Double.random(in: 0..<1)
let float = Float.random(in: 0..<1)
let cgFloat = CGFloat.random(in: 0..<1)
let bool = Bool.random()
Следующий код создаст безопасное случайное число от 0 до 255:
extension UInt8 {
public static var random: UInt8 {
var number: UInt8 = 0
_ = SecRandomCopyBytes(kSecRandomDefault, 1, &number)
return number
}
}
Вы называете это так:
print(UInt8.random)
Для больших чисел это становится более сложным.
Это лучшее, что я мог придумать:
extension UInt16 {
public static var random: UInt16 {
let count = Int(UInt8.random % 2) + 1
var numbers = [UInt8](repeating: 0, count: 2)
_ = SecRandomCopyBytes(kSecRandomDefault, count, &numbers)
return numbers.reversed().reduce(0) { $0 << 8 + UInt16($1) }
}
}
extension UInt32 {
public static var random: UInt32 {
let count = Int(UInt8.random % 4) + 1
var numbers = [UInt8](repeating: 0, count: 4)
_ = SecRandomCopyBytes(kSecRandomDefault, count, &numbers)
return numbers.reversed().reduce(0) { $0 << 8 + UInt32($1) }
}
}
Эти методы используют дополнительное случайное число, чтобы определить, сколько UInt8
s будет использоваться для создания случайного числа. Последняя строка преобразует [UInt8]
в UInt16
или UInt32
.
Я не знаю, считаются ли последние два действительно случайными, но вы можете настроить их по своему вкусу :)
Swift 4.2
Swift 4.2 включил в стандартную библиотеку нативный и довольно полнофункциональный API случайных чисел. ( Предложение Swift Evolution SE-0202 )
let intBetween0to9 = Int.random(in: 0...9)
let doubleBetween0to1 = Double.random(in: 0...1)
Все типы чисел имеют статическое случайное число (in :), которое принимает диапазон и возвращает случайное число в данном диапазоне.
Swift 4.2, Xcode 10.1 .
Для iOS, macOS и tvOS вы можете использовать общесистемный случайный источник в платформе XCode GameKit
. Здесь вы можете найти GKRandomSource
класс с его sharedRandom()
методом класса:
import GameKit
let number: [Int] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
func randomGenerator() -> Int {
let random = GKRandomSource.sharedRandom().nextInt(upperBound: number.count)
return number[random]
}
randomGenerator()
Или просто используйте randomElement()
метод, который возвращает случайный элемент коллекции:
let number: [Int] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
let randomNumber = number.randomElement()!
print(randomNumber)
Вы можете использовать GeneratorOf
как это:
var fibs = ArraySlice([1, 1])
var fibGenerator = GeneratorOf{
_ -> Int? in
fibs.append(fibs.reduce(0, combine:+))
return fibs.removeAtIndex(0)
}
println(fibGenerator.next())
println(fibGenerator.next())
println(fibGenerator.next())
println(fibGenerator.next())
println(fibGenerator.next())
println(fibGenerator.next())
Я использую этот код для генерации случайного числа:
//
// FactModel.swift
// Collection
//
// Created by Ahmadreza Shamimi on 6/11/16.
// Copyright © 2016 Ahmadreza Shamimi. All rights reserved.
//
import GameKit
struct FactModel {
let fun = ["I love swift","My name is Ahmadreza","I love coding" ,"I love PHP","My name is ALireza","I love Coding too"]
func getRandomNumber() -> String {
let randomNumber = GKRandomSource.sharedRandom().nextIntWithUpperBound(fun.count)
return fun[randomNumber]
}
}
xCode 9.1, Swift 4
import Foundation
class Random {
subscript<T>(_ min: T, _ max: T) -> T where T : BinaryInteger {
get {
return rand(min-1, max+1)
}
}
}
let rand = Random()
func rand<T>(_ min: T, _ max: T) -> T where T : BinaryInteger {
let _min = min + 1
let difference = max - _min
return T(arc4random_uniform(UInt32(difference))) + _min
}
let x = rand(-5, 5) // x = [-4, -3, -2, -1, 0, 1, 2, 3, 4]
let x = rand[0, 10] // x = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
Не забудьте добавить код Math-ориентированного решения (1) здесь
import Foundation
extension CountableRange where Bound : BinaryInteger {
var random: Bound {
return rand(lowerBound-1, upperBound)
}
}
extension CountableClosedRange where Bound : BinaryInteger {
var random: Bound {
return rand[lowerBound, upperBound]
}
}
let x = (-8..<2).random // x = [-8, -7, -6, -5, -4, -3, -2, -1, 0, 1]
let x = (0..<10).random // x = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
let x = (-10 ... -2).random // x = [-10, -9, -8, -7, -6, -5, -4, -3, -2]
Не забудьте добавить коды решения (1) и решения (2) сюда
private func generateRandNums(closure:()->(Int)) {
var allNums = Set<Int>()
for _ in 0..<100 {
allNums.insert(closure())
}
print(allNums.sorted{ $0 < $1 })
}
generateRandNums {
(-8..<2).random
}
generateRandNums {
(0..<10).random
}
generateRandNums {
(-10 ... -2).random
}
generateRandNums {
rand(-5, 5)
}
generateRandNums {
rand[0, 10]
}