Saya menyadari buku Swift menyediakan implementasi generator angka acak. Apakah praktik terbaik untuk menyalin dan menempelkan implementasi ini dalam program sendiri? Atau ada perpustakaan yang melakukan ini yang bisa kita gunakan sekarang?
Saya menyadari buku Swift menyediakan implementasi generator angka acak. Apakah praktik terbaik untuk menyalin dan menempelkan implementasi ini dalam program sendiri? Atau ada perpustakaan yang melakukan ini yang bisa kita gunakan sekarang?
Jawaban:
Swift 4.2+
Swift 4.2 yang dikirimkan dengan Xcode 10 memperkenalkan fungsi acak baru yang mudah digunakan untuk banyak tipe data. Anda dapat memanggil random()
metode ini pada tipe numerik.
let randomInt = Int.random(in: 0..<6)
let randomDouble = Double.random(in: 2.71828...3.14159)
let randomBool = Bool.random()
Gunakan arc4random_uniform(n)
untuk bilangan bulat acak antara 0 dan n-1.
let diceRoll = Int(arc4random_uniform(6) + 1)
Keluarkan hasilnya ke Int sehingga Anda tidak perlu secara eksplisit mengetik vars Anda UInt32
(yang tampaknya tidak Swifty).
0
. Dalam kode Anda, diceRoll
bisa jadi 0
. Hanya mengatakan ...
Int(arc4random_uniform(6)+1)
.
arc3random_uniform(n)
dalam UInt32(n)
jika Anda menggunakan nilai yang belum dari tipe itu.
Sunting: Diperbarui untuk Swift 3.0
arc4random
berfungsi dengan baik di Swift, tetapi fungsi dasarnya terbatas pada tipe integer 32-bit ( Int
64-bit pada iPhone 5S dan Mac modern). Berikut adalah fungsi umum untuk nomor acak dari tipe yang dapat diekspresikan oleh literal integer:
public func arc4random<T: ExpressibleByIntegerLiteral>(_ type: T.Type) -> T {
var r: T = 0
arc4random_buf(&r, MemoryLayout<T>.size)
return r
}
Kita dapat menggunakan fungsi generik baru ini untuk memperluas UInt64
, menambahkan argumen batas dan mengurangi bias modulo. (Ini diangkat langsung dari 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
}
}
Dengan itu kita dapat memperluas Int64
argumen yang sama, menangani overflow:
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
}
}
}
Untuk melengkapi keluarga ...
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
}
}
}
Setelah semua itu, kita akhirnya bisa melakukan sesuatu seperti ini:
let diceRoll = UInt64.random(lower: 1, upper: 7)
var r = arc4random(UInt64)
. Mohon saran apa yang Anda maksud di sini?
arc4random
(didefinisikan dalam blok kode pertama) dengan argumen UInt64
yang adalah a Type
.
arc4random_buf
. Tujuan dari ekstensi ini adalah untuk melakukan apa yang arc4random_uniform
dilakukannya (mengurangi bias modulo) kecuali untuk tipe 64-bit.
Edit untuk Swift 4.2
Mulai dari Swift 4.2, alih-alih menggunakan fungsi C impor arc4random_uniform (), Anda sekarang dapat menggunakan fungsi asli Swift sendiri.
// Generates integers starting with 0 up to, and including, 10
Int.random(in: 0 ... 10)
Kamu bisa menggunakan random(in:)
untuk mendapatkan nilai acak untuk nilai primitif lainnya juga; seperti Int, Double, Float dan bahkan Bool.
Versi cepat <4.2
Metode ini akan menghasilkan Int
nilai acak antara minimum yang diberikan dan maksimum
func randomInt(min: Int, max: Int) -> Int {
return min + Int(arc4random_uniform(UInt32(max - min + 1)))
}
Saya menggunakan kode ini:
var k: Int = random() % 10;
random is unavailable in Swift: Use arc4random instead.
Pada iOS 9, Anda dapat menggunakan kelas GameplayKit baru untuk menghasilkan angka acak dalam berbagai cara.
Anda memiliki empat jenis sumber untuk dipilih: sumber acak umum (tanpa nama, ke sistem untuk memilih apa yang dilakukannya), linear congruential, ARC4 dan Mersenne Twister. Ini dapat menghasilkan int acak, float, dan bools.
Pada tingkat paling sederhana, Anda dapat menghasilkan angka acak dari sumber acak bawaan sistem seperti ini:
GKRandomSource.sharedRandom().nextInt()
Itu menghasilkan angka antara -2.147.483.648 dan 2.147.483.647. Jika Anda ingin angka antara 0 dan batas atas (eksklusif) Anda akan menggunakan ini:
GKRandomSource.sharedRandom().nextIntWithUpperBound(6)
GameplayKit memiliki beberapa konstruktor yang mudah digunakan untuk bekerja dengan dadu. Misalnya, Anda dapat menggulung dadu enam sisi seperti ini:
let d6 = GKRandomDistribution.d6()
d6.nextInt()
Plus, Anda dapat membentuk distribusi acak dengan menggunakan hal-hal seperti GKShuffledDistribution. Itu membutuhkan sedikit penjelasan, tetapi jika Anda tertarik, Anda dapat membaca tutorial saya tentang angka acak GameplayKit .
import GameplayKit
. Swift 3 mengubah sintaks keGKRandomSource.sharedRandom().nextInt(upperBound: 6)
Anda dapat melakukannya dengan cara yang sama seperti di C:
let randomNumber = arc4random()
randomNumber
disimpulkan sebagai tipe UInt32
(integer 32-bit unsigned)
rand
, arc4random
, drand48
dan teman-teman semua dalam Darwin
modul. Ini sudah diimpor untuk Anda jika Anda sedang membangun aplikasi Kakao, UIKit, atau Yayasan, tetapi Anda harus melakukannya import Darwin
di taman bermain.
arc4random_uniform()
Pemakaian:
arc4random_uniform(someNumber: UInt32) -> UInt32
Ini memberi Anda bilangan bulat acak dalam rentang 0
ke someNumber - 1
.
Nilai maksimum untuk UInt32
adalah 4.294.967.295 (yaitu,2^32 - 1
).
Contoh:
Balik koin
let flip = arc4random_uniform(2) // 0 or 1
Gulungan dadu
let roll = arc4random_uniform(6) + 1 // 1...6
Hari yang acak di bulan Oktober
let day = arc4random_uniform(31) + 1 // 1...31
Tahun acak di tahun 1990-an
let year = 1990 + arc4random_uniform(10)
Bentuk umum:
let number = min + arc4random_uniform(max - min + 1)
di mana number
, max
dan min
yangUInt32
.
arc4random ()
Anda juga bisa mendapatkan nomor acak dengan menggunakan arc4random()
, yang menghasilkan UInt32
antara 0 dan 2 ^ 32-1. Dengan demikian untuk mendapatkan nomor acak antara 0
dan x-1
, Anda dapat membaginya dengan x
dan mengambil sisanya. Atau dengan kata lain, gunakan Operator Sisa (%) :
let number = arc4random() % 5 // 0...4
Namun, ini menghasilkan sedikit bias modulo (lihat juga di sini dan di sini ), jadi itu sebabnyaarc4random_uniform()
direkomendasikan.
Konversi ke dan dari Int
Biasanya akan baik-baik saja untuk melakukan sesuatu seperti ini untuk mengubah bolak-balik antara Int
dan UInt32
:
let number: Int = 10
let random = Int(arc4random_uniform(UInt32(number)))
Masalahnya, bagaimanapun, adalah yang Int
memiliki kisaran -2,147,483,648...2,147,483,647
pada sistem 32 bit dan kisaran -9,223,372,036,854,775,808...9,223,372,036,854,775,807
pada sistem 64 bit. Bandingkan ini dengan UInt32
kisaran 0...4,294,967,295
. The U
dari UInt32
sarana unsigned .
Pertimbangkan kesalahan berikut:
UInt32(-1) // negative numbers cause integer overflow error
UInt32(4294967296) // numbers greater than 4,294,967,295 cause integer overflow error
Jadi, Anda hanya perlu memastikan bahwa parameter input Anda berada dalam UInt32
kisaran dan bahwa Anda tidak memerlukan output yang di luar kisaran itu juga.
Saya sudah bisa menggunakan hanya rand()
untuk mendapatkan CInt acak. Anda dapat menjadikannya Int dengan menggunakan sesuatu seperti ini:
let myVar: Int = Int(rand())
Anda dapat menggunakan fungsi acak C favorit Anda, dan hanya mengonversi ke nilai ke Int jika diperlukan.
random()
, yang mengembalikan Int
bukan UInt32
- dan seperti @SomeGuy disebutkan, cukup menelepon srandom(arc4random())
sekali di mana saja sebelum menggunakannya untuk memastikan ia memiliki benih acak yang berbeda untuk setiap pelaksanaan program Anda.
@ jstn jawaban baik, tapi agak bertele-tele. Swift dikenal sebagai bahasa berorientasi protokol, sehingga kami dapat mencapai hasil yang sama tanpa harus mengimplementasikan kode boilerplate untuk setiap kelas dalam keluarga integer, dengan menambahkan implementasi default untuk ekstensi protokol.
public extension ExpressibleByIntegerLiteral {
public static func arc4random() -> Self {
var r: Self = 0
arc4random_buf(&r, MemoryLayout<Self>.size)
return r
}
}
Sekarang kita bisa melakukan:
let i = Int.arc4random()
let j = UInt32.arc4random()
dan semua kelas integer lainnya ok.
Di Swift 4.2 Anda dapat menghasilkan angka acak dengan memanggil random()
metode pada tipe numerik apa pun yang Anda inginkan, memberikan kisaran yang ingin Anda gunakan. Misalnya, ini menghasilkan angka acak dalam kisaran 1 hingga 9, termasuk di kedua sisi
let randInt = Int.random(in: 1..<10)
Juga dengan tipe lain
let randFloat = Float.random(in: 1..<20)
let randDouble = Double.random(in: 1...30)
let randCGFloat = CGFloat.random(in: 1...40)
Berikut adalah perpustakaan yang melakukan pekerjaan dengan baik 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);
}
Saya ingin menambahkan jawaban yang ada bahwa contoh nomor acak generator dalam buku Swift adalah Linear Congruence Generator (LCG), itu adalah nomor yang sangat terbatas dan tidak boleh kecuali untuk contoh-contoh harus sepele, di mana kualitas acak tidak sama sekali tidak masalah. Dan LCG seharusnya tidak pernah digunakan untuk keperluan kriptografi .
arc4random()
jauh lebih baik dan dapat digunakan untuk sebagian besar tujuan, tetapi sekali lagi tidak boleh digunakan untuk tujuan kriptografi.
Jika Anda menginginkan sesuatu yang dijamin aman secara kriptografis, gunakan SecCopyRandomBytes()
. Perhatikan bahwa jika Anda membuat generator angka acak menjadi sesuatu, orang lain mungkin berakhir (salah) -menggunakannya untuk keperluan kriptografi (seperti kata sandi, kunci atau pembuatan garam), maka Anda harus mempertimbangkan untuk menggunakannya SecCopyRandomBytes()
, bahkan jika kebutuhan Anda tidak t cukup membutuhkan itu.
Ada satu set API baru:
let randomIntFrom0To10 = Int.random(in: 0 ..< 10)
let randomDouble = Double.random(in: 1 ... 10)
Semua tipe numerik sekarang memiliki random(in:)
metode yang dibutuhkan range
.
Ini mengembalikan nomor yang terdistribusi secara seragam dalam kisaran itu.
TL; DR
Anda harus menggunakan API C yang diimpor (Mereka berbeda antar platform) .
Dan terlebih lagi ...
Bagaimana jika saya katakan bahwa acak itu tidak acak?
Jika Anda menggunakan arc4random()
(untuk menghitung sisanya) seperti arc4random() % aNumber
, hasilnya tidak terdistribusi secara seragam antara 0
dan aNumber
. Ada masalah yang disebut Modulo bias .
Bias modulo
Biasanya, fungsi menghasilkan angka acak antara 0
dan MAX (tergantung pada jenis dll . ) . Untuk membuat contoh cepat dan mudah, katakanlah angka maksnya 7
dan Anda peduli dengan angka acak dalam kisaran 0 ..< 2
(atau interval [0, 3) jika Anda menginginkannya) .
The probabilitas untuk nomor individual adalah:
Dengan kata lain, Anda lebih cenderung berakhir dengan 0 atau 1 dari 2 . Tentu saja, ingatlah bahwa ini sangat disederhanakan dan angka MAX jauh lebih tinggi, menjadikannya lebih "adil".
Masalah ini diatasi oleh SE-0202 - Unifikasi acak di Swift 4.2
Tanpa arc4Random_uniform () di beberapa versi Xcode (di 7.1 berjalan tetapi tidak lengkap secara otomatis untuk saya). Anda bisa melakukan ini sebagai gantinya.
Untuk menghasilkan angka acak dari 0-5. Pertama
import GameplayKit
Kemudian
let diceRoll = GKRandomSource.sharedRandom().nextIntWithUpperBound(6)
var randomNumber = Int(arc4random_uniform(UInt32(5)))
Di sini 5 akan memastikan bahwa angka acak dihasilkan melalui nol hingga empat. Anda dapat mengatur nilainya sesuai.
Cepat 4.2
Bye bye untuk mengimpor 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()
Kode berikut akan menghasilkan angka acak aman antara 0 dan 255:
extension UInt8 {
public static var random: UInt8 {
var number: UInt8 = 0
_ = SecRandomCopyBytes(kSecRandomDefault, 1, &number)
return number
}
}
Anda menyebutnya seperti ini:
print(UInt8.random)
Untuk jumlah yang lebih besar menjadi lebih rumit.
Ini yang terbaik yang bisa saya pikirkan:
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) }
}
}
Metode ini menggunakan angka acak tambahan untuk menentukan berapa banyak UInt8
yang akan digunakan untuk membuat angka acak. Baris terakhir mengonversi [UInt8]
ke UInt16
atauUInt32
.
Saya tidak tahu apakah dua yang terakhir masih dihitung sebagai benar-benar acak, tetapi Anda dapat mengubahnya sesuai keinginan Anda :)
Cepat 4.2
Swift 4.2 telah memasukkan API angka acak asli dan berfitur lengkap di perpustakaan standar. ( Proposal Swift Evolution SE-0202 )
let intBetween0to9 = Int.random(in: 0...9)
let doubleBetween0to1 = Double.random(in: 0...1)
Semua tipe angka memiliki acak acak (dalam :) yang mengambil rentang dan mengembalikan nomor acak dalam rentang yang diberikan
Swift 4.2, Xcode 10.1 .
Untuk iOS, macOS dan tvOS Anda dapat menggunakan sumber acak seluruh sistem dalam kerangka kerja Xcode GameKit
. Di sini Anda dapat menemukan GKRandomSource
kelas dengan sharedRandom()
metode kelasnya:
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()
Atau cukup gunakan randomElement()
metode yang mengembalikan elemen acak dari koleksi:
let number: [Int] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
let randomNumber = number.randomElement()!
print(randomNumber)
Anda bisa menggunakan GeneratorOf
seperti ini:
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())
Saya menggunakan kode ini untuk menghasilkan nomor acak:
//
// 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]
Jangan lupa untuk menambahkan solusi berorientasi matematika (1) kode di sini
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]
Jangan lupa untuk menambahkan kode solusi (1) dan solusi (2) di sini
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]
}