Я понимаю, что книга Swift представляет собой реализацию генератора случайных чисел. Лучше всего копировать и вставлять эту реализацию в одну собственную программу? Или есть библиотека, которая делает это, что мы можем использовать сейчас?
Используйте стандартные библиотечные функции для высококачественных случайных чисел: arc4random()
или arc4random_uniform()
, как и в Objective-C.
Они находятся в модуле Darwin
, поэтому, если вы не импортировали AppKit
, UIKit
или Foundation
(которые импортируют его для вас), вам нужно будет import Darwin
.
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, чтобы вам не приходилось явно вводить ваши vars как UInt32
(что кажется un-Swifty).
0
. В вашем коде diceRoll
может быть 0
. Просто говорю...
Int(arc4random_uniform(6)+1)
.
Изменить: Обновлено для 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
который является Type
.
Этот метод генерирует случайное значение Int
между заданным минимальным и максимальным
func randomInt(min: Int, max:Int) -> Int {
return min + Int(arc4random_uniform(UInt32(max - min + 1)))
}
Я использовал этот код:
var k: Int = random() % 10;
didFinishLaunchingWithOptions
?
Начиная с iOS 9, вы можете использовать новые классы GameplayKit для генерации случайных чисел различными способами.
У вас есть четыре типа источников на выбор: общий случайный источник (без названия, вплоть до системы, чтобы выбрать, что он делает), линейный конгруэнтный, ARC4 и Mersenne Twister. Они могут генерировать случайные ints, floats и 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 (т. UInt32
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
и взять остаток. Или, другими словами, используйте Оператор Remainder (%):
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
означает unsigned.
Рассмотрим следующие ошибки:
UInt32(-1) // negative numbers cause integer overflow error
UInt32(4294967296) // numbers greater than 4,294,967,295 cause integer overflow error
Поэтому вам просто нужно быть уверенным, что ваши входные параметры находятся в пределах диапазона UInt32
и что вам не нужен вывод, выходящий за пределы этого диапазона.
Пример случайного числа между 10 (0-9);
import UIKit
let randomNumber = Int(arc4random_uniform(10))
Очень простой код - простой и короткий.
@jstn answer является хорошим, но немного подробным. 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()
и все остальные целочисленные классы в порядке.
Я смог использовать rand()
для получения случайного CInt. Вы можете сделать это Int, используя что-то вроде этого:
let myVar: Int = Int(rand())
Вы можете использовать свою любимую случайную функцию C и просто конвертировать в значение в Int, если необходимо.
В 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);
}
Я хотел бы добавить к существующим ответам, что пример генератора случайных чисел в книге Swift представляет собой генератор линейных конгруэнций (LCG), он сильно ограничен и не должен быть исключающим тривиальные примеры, где качество случайности вообще не имеет значения. И LCG никогда не должен использоваться для криптографических целей.
arc4random()
намного лучше и может использоваться для большинства целей, но снова не следует использовать для криптографических целей.
Если вы хотите что-то, что гарантировано для криптографической защиты, используйте SecCopyRandomBytes()
. Обратите внимание: если вы создадите генератор случайных чисел во что-то, кто-то другой может оказаться (ошибочно), используя его для криптографических целей (например, создание пароля, генерации ключей или солей), тогда вам следует использовать SecCopyRandomBytes()
в любом случае, даже если ваш необходимость не требует этого.
Без arc4Random_uniform() в некоторых версиях Xcode (в 7.1 он запускается, но не автозаполняется для меня). Вы можете сделать это вместо этого.
Чтобы создать случайное число из 0-5. Первая
import GameplayKit
Тогда
let diceRoll = GKRandomSource.sharedRandom().nextIntWithUpperBound(6)
var randomNumber = Int(arc4random_uniform(UInt32(**5**)))
Здесь 5 будет гарантировать, что случайное число сгенерировано с нуля до пяти. Вы можете установить значение соответствующим образом.
Существует новый набор 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
. Существует проблема, называемая смещением Модуло.
Modulo bias
Обычно функция генерирует случайное число от 0
до MAX (зависит от типа и т.д.). Чтобы сделать быстрый, простой пример, скажем, максимальное число равно 7
и вы заботитесь о случайном числе в диапазоне 0..< 2
(или интервал [0, 3), если вы предпочитаете это).
Вероятности для отдельных чисел:
Другими словами, вы, скорее всего, получите 0 или 1, чем 2. Конечно, не забывайте, что это чрезвычайно упрощено, а число MAX намного выше, что делает его более "справедливым".
Эта проблема решается SE-0202 - Случайная унификация в Swift 4.2
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)
Все типы номеров имеют статическую случайную (в :), которая принимает диапазон и возвращает случайное число в заданном диапазоне
В следующем коде будет создано безопасное случайное число от 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
будут использоваться для создания случайного числа. Последняя строка преобразует [UInt8]
в UInt16
или UInt32
.
Я не знаю, считаются ли последние два по-настоящему случайными, но вы можете настроить их по своему вкусу:)
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]
}
Swift 4.2
До свидания, чтобы импортировать Foundation 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()
Для iOS, macOS и tvOS вы можете использовать общесистемный случайный источник в Xcode framework GameKit
. Здесь вы можете найти класс GKRandomSource
с помощью 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]
}
Это написано в Swift 4.2.
Я использую этот код для генерации случайного числа:
//
// 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]
}
}
Вы можете использовать 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())