私はSwiftブックが乱数ジェネレーターの実装を提供していることを理解しています。この実装を自分のプログラムにコピーして貼り付けるのがベストプラクティスですか?または、これを使用して現在使用できるライブラリはありますか?
私はSwiftブックが乱数ジェネレーターの実装を提供していることを理解しています。この実装を自分のプログラムにコピーして貼り付けるのがベストプラクティスですか?または、これを使用して現在使用できるライブラリはありますか?
回答:
Swift 4.2以降
Xcode 10に同梱されているSwift 4.2では、多くのデータ型に使用できる新しい使いやすいランダム関数が導入されています。random()
数値型に対してメソッドを呼び出すことができます。
let randomInt = Int.random(in: 0..<6)
let randomDouble = Double.random(in: 2.71828...3.14159)
let randomBool = Bool.random()
0〜n arc4random_uniform(n)
-1のランダムな整数に使用します。
let diceRoll = Int(arc4random_uniform(6) + 1)
結果をIntにキャストして、varを明示的に入力する必要がないようにしますUInt32
(Swiftyではないようです)。
0
。あなたのコードでは、diceRoll
可能性があり0
。ただ言う...
Int(arc4random_uniform(6)+1)
。
arc3random_uniform(n)
ているUInt32(n)
場合は、入力したパラメーターn をa にキャストすることを忘れないでください。
編集: Swift 3.0用に更新
arc4random
Swiftではうまく機能しますが、基本機能は32ビット整数型に制限されています(Int
iPhone 5Sおよび最新のMacでは64ビットです)。整数リテラルで表現可能な型の乱数の一般的な関数を次に示します。
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
。
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クラスを使用して、さまざまな方法で乱数を生成できます。
選択できるソースタイプは4つあります。一般的なランダムソース(名前なし、システムが選択するもの)、線形合同、ARC4、およびMersenne Twisterです。これらはランダムな整数、浮動小数点数、ブール値を生成できます。
最も単純なレベルでは、次のようにシステムの組み込みランダムソースから乱数を生成できます。
GKRandomSource.sharedRandom().nextInt()
これにより、-2,147,483,648〜2,147,483,647の数値が生成されます。0と上限(排他的)の間の数値が必要な場合は、次のようにします。
GKRandomSource.sharedRandom().nextIntWithUpperBound(6)
GameplayKitには、サイコロを操作するための便利なコンストラクタがいくつか組み込まれています。たとえば、次のように6面のサイコロを振ることができます。
let d6 = GKRandomDistribution.d6()
d6.nextInt()
さらに、GKShuffledDistributionなどを使用してランダム分布を形成できます。これにはもう少し説明が必要ですが、興味がある場合はGameplayKit乱数に関するチュートリアルを読むことができます。
import GameplayKit
。スウィフト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
10月のランダムな日
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()
また、0から2 ^ 32-1 arc4random()
を生成するを使用して乱数を取得することもできUInt32
ます。したがって、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()
を返すを使用することもできます。@ SomeGuyが言及したように、使用する前に1度だけ呼び出して、プログラムの実行ごとに異なるランダム化されたシードがあることを確認します。Int
UInt32
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()
他のすべての整数クラスは問題ありません。
これはうまく機能するライブラリです 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
}
}
Swiftブックの乱数ジェネレーターの例は線形合同ジェネレーター(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
。Moduloバイアスと呼ばれる問題があります。
モジュロバイアス
通常、関数は0
とMAXの 間の乱数を生成します(型などによって異なります)。簡単な例を簡単に説明すると、最大数はで7
あり、範囲内の乱数0 ..< 2
(または、間隔[0、3)を希望する場合)に関心があるとします。
個々の数値の確率は次のとおりです。
つまり、最終的には2よりも0または1になる可能性が高くなります。もちろん、これは非常に単純化されており、MAXの数値ははるかに高く、より「公平」であることに注意してください。
この問題は、によって対処されるランダム統一- SE-0202でスウィフト4.2
var randomNumber = Int(arc4random_uniform(UInt32(5)))
ここで5は、乱数が0から4まで生成されることを確認します。それに応じて値を設定できます。
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()
次のコードは、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
ます。
最後の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)
すべての数値タイプには、範囲を受け取り、指定された範囲の乱数を返す静的な乱数(in :)があります。
スウィフト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]
}