Zdaję sobie sprawę, że książka Swift zapewnia implementację generatora liczb losowych. Czy najlepszą praktyką jest kopiowanie i wklejanie tej implementacji we własnym programie? A może jest biblioteka, z której możemy teraz korzystać?
Zdaję sobie sprawę, że książka Swift zapewnia implementację generatora liczb losowych. Czy najlepszą praktyką jest kopiowanie i wklejanie tej implementacji we własnym programie? A może jest biblioteka, z której możemy teraz korzystać?
Odpowiedzi:
Swift 4.2+
Swift 4.2 dostarczany z Xcode 10 wprowadza nowe łatwe w użyciu losowe funkcje dla wielu typów danych. Możesz wywołać tę random()metodę dla typów numerycznych.
let randomInt = Int.random(in: 0..<6)
let randomDouble = Double.random(in: 2.71828...3.14159)
let randomBool = Bool.random()
Użyj arc4random_uniform(n)dla losowej liczby całkowitej od 0 do n-1.
let diceRoll = Int(arc4random_uniform(6) + 1)
Rzuć wynik na Int, abyś nie musiał jawnie wpisywać swoich warów jako UInt32(co wydaje się nie-Swifty).
0. W twoim kodzie diceRollmoże być 0. Tylko mówię ...
Int(arc4random_uniform(6)+1).
arc3random_uniform(n)a, UInt32(n)jeśli używasz wartości, która nie jest jeszcze tego typu.
Edycja: Zaktualizowano dla Swift 3.0
arc4randomdziała dobrze w Swift, ale podstawowe funkcje są ograniczone do 32-bitowych liczb całkowitych ( Int64-bit na iPhone 5S i współczesnych komputerach Mac). Oto ogólna funkcja dla losowej liczby typu wyrażanego literałami całkowitymi:
public func arc4random<T: ExpressibleByIntegerLiteral>(_ type: T.Type) -> T {
var r: T = 0
arc4random_buf(&r, MemoryLayout<T>.size)
return r
}
Możemy użyć tej nowej funkcji ogólnej do rozszerzenia UInt64, dodania argumentów granicznych i złagodzenia błędu modulo. (To jest podnoszone prosto z 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
}
}
Dzięki temu możemy przedłużyć Int64 o te same argumenty, zajmując się przepełnieniem:
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
}
}
}
Aby uzupełnić rodzinę ...
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
}
}
}
Po tym wszystkim możemy wreszcie zrobić coś takiego:
let diceRoll = UInt64.random(lower: 1, upper: 7)
var r = arc4random(UInt64). Proszę o poradę, co miałeś na myśli?
arc4random(zdefiniowanej w pierwszym bloku kodu) z argumentem, UInt64który jest Type.
arc4random_buf. Celem tych rozszerzeń jest robienie dokładnie tego, co arc4random_uniformrobi (łagodzenie błędu modulo), z wyjątkiem typów 64-bitowych.
Edycja dla Swift 4.2
Począwszy od Swift 4.2, zamiast korzystać z importowanej funkcji C arc4random_uniform (), możesz teraz używać własnych funkcji natywnych Swift.
// Generates integers starting with 0 up to, and including, 10
Int.random(in: 0 ... 10)
Możesz użyć random(in:) aby uzyskać losowe wartości również dla innych pierwotnych wartości; takie jak Int, Double, Float, a nawet Bool.
Szybkie wersje <4.2
Ta metoda wygeneruje losową Intwartość między podanym minimum a maksimum
func randomInt(min: Int, max: Int) -> Int {
return min + Int(arc4random_uniform(UInt32(max - min + 1)))
}
Użyłem tego kodu:
var k: Int = random() % 10;
random is unavailable in Swift: Use arc4random instead.
Począwszy od iOS 9, możesz używać nowych klas GameplayKit do generowania liczb losowych na różne sposoby.
Masz do wyboru cztery typy źródeł: ogólne losowe źródło (bez nazwy, aż do systemu, który wybiera, co robi), liniowy congruential, ARC4 i Mersenne Twister. Mogą generować losowe liczby całkowite, zmiennoprzecinkowe i boole.
Na najprostszym poziomie możesz wygenerować losową liczbę z wbudowanego losowego źródła systemu, jak to:
GKRandomSource.sharedRandom().nextInt()
To generuje liczbę między -2 147 483 648 a 2 147 483 647. Jeśli chcesz liczbę z zakresu od 0 do górnej granicy (wyłącznie), użyj tego:
GKRandomSource.sharedRandom().nextIntWithUpperBound(6)
GameplayKit ma wbudowane kilka wygodnych konstruktorów do pracy z kostkami. Na przykład możesz rzucić sześciościenną kostką w następujący sposób:
let d6 = GKRandomDistribution.d6()
d6.nextInt()
Ponadto możesz kształtować losowy rozkład, używając rzeczy takich jak GKShuffledDistribution. To wymaga trochę więcej wyjaśnienia, ale jeśli jesteś zainteresowany, możesz przeczytać mój poradnik na temat losowych liczb GameplayKit .
import GameplayKit. Swift 3 zmienił składnię naGKRandomSource.sharedRandom().nextInt(upperBound: 6)
Możesz to zrobić w taki sam sposób, jak w C:
let randomNumber = arc4random()
randomNumberjest wywnioskowane, że jest typu UInt32(32-bitowa liczba całkowita bez znaku)
rand, arc4random, drand48i przyjaciele są w Darwinmodule. Jest już zaimportowany, jeśli budujesz aplikację Cocoa, UIKit lub Foundation, ale musisz to zrobić import Darwinna placach zabaw.
arc4random_uniform()Stosowanie:
arc4random_uniform(someNumber: UInt32) -> UInt32
Daje to losowe liczby całkowite z zakresu 0do someNumber - 1.
Maksymalna wartość UInt32wynosi 4 294 967 295 (to znaczy, 2^32 - 1).
Przykłady:
Rzut monetą
let flip = arc4random_uniform(2) // 0 or 1Rzut kostką
let roll = arc4random_uniform(6) + 1 // 1...6Losowy dzień w październiku
let day = arc4random_uniform(31) + 1 // 1...31Losowy rok w latach 90
let year = 1990 + arc4random_uniform(10)Ogólna forma:
let number = min + arc4random_uniform(max - min + 1)
gdzie number, maxi minsą UInt32.
arc4random ()
Możesz także uzyskać losową liczbę, używając arc4random(), co daje UInt32od 0 do 2 ^ 32-1. Tak więc, aby uzyskać losową liczbę między 0i x-1, możesz ją podzielić przez xi wziąć resztę. Innymi słowy, użyj Remainder Operator (%) :
let number = arc4random() % 5 // 0...4
Powoduje to jednak nieznaczne odchylenie modulo (patrz także tutaj i tutaj ), dlatego arc4random_uniform()jest zalecane.
Konwersja do i z Int
Zwykle dobrze byłoby zrobić coś takiego, aby przekonwertować między Inti UInt32:
let number: Int = 10
let random = Int(arc4random_uniform(UInt32(number)))
Problem polega jednak na tym, że Intma on zasięg w -2,147,483,648...2,147,483,647systemach 32-bitowych i zakres w -9,223,372,036,854,775,808...9,223,372,036,854,775,807systemach 64-bitowych. Porównaj to z UInt32zakresem 0...4,294,967,295. UZ UInt32pomocą unsigned .
Rozważ następujące błędy:
UInt32(-1) // negative numbers cause integer overflow error
UInt32(4294967296) // numbers greater than 4,294,967,295 cause integer overflow error
Musisz tylko upewnić się, że parametry wejściowe mieszczą się w UInt32zakresie i że nie potrzebujesz również wyjścia, które jest poza tym zakresem.
Byłem w stanie po prostu użyć rand()losowego CInt. Możesz zrobić z tego Int, używając czegoś takiego:
let myVar: Int = Int(rand())
Możesz użyć swojej ulubionej funkcji losowej C. W razie potrzeby wystarczy przekonwertować wartość na Int.
random(), który zwraca Intraczej niż UInt32- i jak wspomniano @SomeGuy, po prostu zadzwoń srandom(arc4random())raz w dowolnym miejscu przed użyciem, aby upewnić się, że ma on inne, losowe źródło dla każdego wykonania twojego programu.
Odpowiedź @ jstn jest dobra, ale nieco gadatliwa. Swift jest znany jako język zorientowany na protokół, dzięki czemu możemy osiągnąć ten sam wynik bez konieczności implementowania kodu typu Booster dla każdej klasy w rodzinie liczb całkowitych, dodając domyślną implementację rozszerzenia protokołu.
public extension ExpressibleByIntegerLiteral {
public static func arc4random() -> Self {
var r: Self = 0
arc4random_buf(&r, MemoryLayout<Self>.size)
return r
}
}
Teraz możemy zrobić:
let i = Int.arc4random()
let j = UInt32.arc4random()
i wszystkie inne klasy całkowite są w porządku.
W Swift 4.2 możesz generować losowe liczby, wywołując random()metodę na dowolnym typie liczbowym, podając zakres, z którym chcesz pracować. Na przykład generuje losową liczbę z zakresu od 1 do 9, włącznie po obu stronach
let randInt = Int.random(in: 1..<10)
Również z innymi typami
let randFloat = Float.random(in: 1..<20)
let randDouble = Double.random(in: 1...30)
let randCGFloat = CGFloat.random(in: 1...40)
Oto biblioteka, która dobrze wykonuje to zadanie 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);
}
Chciałbym dodać do istniejących odpowiedzi, że przykład generatora liczb losowych w książce Swift to Generator liniowej zbieżności (LCG), jest on mocno ograniczony i nie powinien być wyjątkiem trywialnych przykładów, w których jakość losowości nie to nie ma znaczenia. I LCG nigdy nie powinien być używany do celów kryptograficznych .
arc4random()jest znacznie lepszy i może być używany do większości celów, ale ponownie nie powinien być wykorzystywany do celów kryptograficznych.
Jeśli chcesz mieć coś, co gwarantuje bezpieczeństwo kryptograficzne, użyj SecCopyRandomBytes(). Zauważ, że jeśli w coś wbudujesz generator liczb losowych, ktoś inny może (nie) użyć go do celów kryptograficznych (takich jak generowanie hasła, klucza lub soli), powinieneś rozważyć użycie go SecCopyRandomBytes(), nawet jeśli twoja potrzeba tego nie robi. całkiem tego wymaga.
Istnieje nowy zestaw interfejsów API:
let randomIntFrom0To10 = Int.random(in: 0 ..< 10)
let randomDouble = Double.random(in: 1 ... 10)
Wszystkie typy numeryczne mają teraz wybraną random(in:)metodę range.
Zwraca liczbę równomiernie rozmieszczoną w tym zakresie.
TL; DR
Musisz użyć importowanych interfejsów API C (różnią się one między platformami) .
A ponadto ...
Co jeśli powiem ci, że los nie jest taki losowy?
Jeśli używasz arc4random() (aby obliczyć resztę) jak arc4random() % aNumber, wynik nie jest równomiernie rozłożone pomiędzy 0i aNumber. Istnieje problem zwany stronniczością Modulo .
Odchylenie modulo
Zwykle funkcja generuje losową liczbę między 0i MAX (zależy od typu itp . ) . Aby zrobić szybki, łatwy przykład, załóżmy, że maksymalna liczba to 7i zależy Ci na losowej liczbie w zakresie 0 ..< 2 (lub odstępie [0, 3), jeśli wolisz) .
Te prawdopodobieństwa dla poszczególnych numerów są:
Innymi słowy, bardziej prawdopodobne jest, że skończysz z 0 lub 1 niż 2 . Oczywiście należy pamiętać, że jest to bardzo uproszczone, a liczba MAX jest znacznie wyższa, dzięki czemu jest bardziej „sprawiedliwa”.
Ten problem rozwiązano w SE-0202 - Losowe zjednoczenie w Swift 4.2
Bez arc4Random_uniform () w niektórych wersjach Xcode (w 7.1 działa, ale nie wykonuje autouzupełniania). Możesz to zrobić zamiast tego.
Aby wygenerować losową liczbę od 0-5. Pierwszy
import GameplayKit
Następnie
let diceRoll = GKRandomSource.sharedRandom().nextIntWithUpperBound(6)
var randomNumber = Int(arc4random_uniform(UInt32(5)))
Tutaj 5 upewni się, że liczba losowa zostanie wygenerowana od zera do czterech. Możesz odpowiednio ustawić wartość.
Szybki 4.2
Pa pa, aby zaimportować libellę Foundation C. 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()
Poniższy kod wygeneruje bezpieczną liczbę losową z przedziału od 0 do 255:
extension UInt8 {
public static var random: UInt8 {
var number: UInt8 = 0
_ = SecRandomCopyBytes(kSecRandomDefault, 1, &number)
return number
}
}
Nazywasz to tak:
print(UInt8.random)
W przypadku większych liczb staje się to bardziej skomplikowane.
To najlepsze, co mogłem wymyślić:
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) }
}
}
W tych metodach używa się dodatkowej liczby losowej, aby określić, ile UInt8s będzie używanych do utworzenia liczby losowej. Ostatnia linia zamienia [UInt8]się UInt16lubUInt32 .
Nie wiem, czy dwa ostatnie nadal liczą się jako naprawdę losowe, ale możesz je dostosować do własnych upodobań :)
Szybki 4.2
Swift 4.2 zawiera natywny i dość w pełni funkcjonalny interfejs API liczb losowych w standardowej bibliotece. ( Propozycja Swift Evolution SE-0202 )
let intBetween0to9 = Int.random(in: 0...9)
let doubleBetween0to1 = Double.random(in: 0...1)
Wszystkie typy liczb mają statyczne losowe (w :), które przyjmuje zakres i zwraca liczbę losową z podanego zakresu
Swift 4.2, Xcode 10.1 .
W systemach iOS, macOS i tvOS można używać losowego źródła systemowego w środowisku Xcode GameKit. Tutaj możesz znaleźć GKRandomSourceklasę za pomocą jej sharedRandom()metody klasowej:
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()
Lub po prostu użyj randomElement()metody, która zwraca losowy element kolekcji:
let number: [Int] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
let randomNumber = number.randomElement()!
print(randomNumber)
Możesz użyć w GeneratorOften sposób:
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())
Używam tego kodu, aby wygenerować losową liczbę:
//
// 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]
Nie zapomnij dodać tutaj kodu rozwiązania zorientowanego na matematykę (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]
Nie zapomnij dodać tutaj kodów rozwiązania (1) i rozwiązania (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]
}