Я розумію, що книга Свіфта забезпечила реалізацію генератора випадкових чисел. Чи найкраща практика копіювання та вставки цієї програми у власну програму? Або є бібліотека, яка робить це, що ми можемо використовувати зараз?
Я розумію, що книга Свіфта забезпечила реалізацію генератора випадкових чисел. Чи найкраща практика копіювання та вставки цієї програми у власну програму? Або є бібліотека, яка робить це, що ми можемо використовувати зараз?
Відповіді:
Швидкий 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
(що здається, не швидко).
0
. У вашому коді diceRoll
може бути 0
. Просто кажу ...
Int(arc4random_uniform(6)+1)
.
arc3random_uniform(n)
a, UInt32(n)
якщо ви використовуєте значення, яке ще не є такого типу.
Редагування: оновлено для Swift 3.0
arc4random
добре працює у Swift, але базові функції обмежені 32-бітовими цілими типами ( Int
це 64-розрядна версія для iPhone 5S та сучасних Macs). Ось загальна функція для випадкового числа типу, вираженого цілим літералом:
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.
Швидкі версії <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. Вони можуть генерувати випадкові вставки, поплавці та булі.
На найпростішому рівні ви можете генерувати випадкове число з вбудованого випадкового джерела системи таким чином:
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
модулі. Це вже імпорт для вас, якщо ви створюєте додаток для какао, 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()
, навіть якщо ваша потреба не означає ' t зовсім вимагають цього.
Існує новий набір 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 .
Модульне зміщення
Зазвичай функція генерує випадкове число між 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 переконається, що випадкове число генерується через нуль до чотирьох. Можна відповідно встановити значення.
Швидкий 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()
Наступний код створить захищене випадкове число від 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
.
Я не знаю, чи останні два вважаються справді випадковими, але ви можете налаштувати це на свій смак :)
Швидкий 4.2
Swift 4.2 включив в стандартну бібліотеку нативний і досить повнофункціональний API випадкових чисел. ( Пропозиція Swift Evolution SE-0202 )
let intBetween0to9 = Int.random(in: 0...9)
let doubleBetween0to1 = Double.random(in: 0...1)
Усі типи чисел мають статичне випадкове (в :), яке приймає діапазон і повертає випадкове число у заданому діапазоні
Швидкий 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]
Не забудьте додати сюди код, орієнтований на математику (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]
}