web-dev-qa-db-ja.com

AppleのSwift言語で乱数をどうやって生成するのですか?

私はSwiftの本が乱数ジェネレータの実装を提供したことを実感します。この実装をコピーして自分のプログラムに貼り付けるのがベストプラクティスですか?それとも、私たちが今使うことができるようなライブラリがありますか?

405

Objective-Cと同じように、高品質の乱数には標準ライブラリ関数arc4random()またはarc4random_uniform()を使用します。

それらはDarwinモジュールの中にあるので、もしあなたがAppKitUIKitFoundationのいずれかをインポートしていないのであれば、import Darwinを実行する必要があります。

スイフト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()
383
Catfish_Man

0とn-1の間のランダムな整数にはarc4random_uniform(n)を使います。

let diceRoll = Int(arc4random_uniform(6) + 1)

結果をIntにキャストすると、varを明示的にUInt32として入力する必要がなくなります(これはSwiftyには思えません)。

493
John Pavley

編集: 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)
115
jstn

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)))
}
70
Groot

私はこのコードを使いました:

var k: Int = random() % 10;
59
fockus

IOS 9以降、新しいGameplayKitクラスを使用してさまざまな方法で乱数を生成できます。

あなたは4つのソースタイプから選ぶことができます:一般的なランダムソース(名前なし、それがするものを選ぶためにシステムまで)、線形合同、ARC4とMersenne Twister。これらはランダムなint、float、boolを生成する可能性があります。

最も単純なレベルでは、システムに内蔵されているランダムなソースから次のように乱数を生成できます。

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乱数に関する私のチュートリアルを読んでください

32
TwoStraws

C言語と同じ方法で実行できます。

let randomNumber = arc4random()

randomNumberUInt32型(32ビット符号なし整数)であると推論されます

24
Dave DeLong

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)

numbermax、およびminUInt32です。

どうですか….

arc4random()

arc4random()を使って乱数を取得することもできます。これは0から2 ^ 32-1の間のUInt32を生成します。したがって、0x-1の間の乱数を取得するには、それをxで除算し、残りを取ります。または言い換えれば、 剰余演算子(%) を使用します。

let number = arc4random() % 5 // 0...4

しかし、これはわずかな モジュロバイアスhere および here も参照)を生成するため、arc4random_uniform()を推奨します。

Intとの間の変換

通常IntUInt32の間で行ったり来たりするためにこのようなことをするのが良いでしょう:

let number: Int = 10
let random = Int(arc4random_uniform(UInt32(number)))

ただし、問題は、 Int が32ビットシステムでは-2,147,483,648...2,147,483,647の範囲、64ビットシステムでは-9,223,372,036,854,775,808...9,223,372,036,854,775,807の範囲があることです。これをUInt320...4,294,967,295の範囲と比較してください。 UInt32Uunsignedを意味します。

以下のエラーを検討してください。

UInt32(-1) // negative numbers cause integer overflow error
UInt32(4294967296) // numbers greater than 4,294,967,295 cause integer overflow error

そのため、入力パラメータがUInt32の範囲内にあることと、その範囲外の出力も必要ないことを確認する必要があります。

22
Suragch

10(0-9)の間の乱数の例

import UIKit

let randomNumber = Int(arc4random_uniform(10))

非常に簡単なコード - シンプルで短い。

19
R.S

@ jstn's 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()

他のすべての整数クラスは問題ありません。

16
Ryne Wang

ランダムなCIntを取得するためにRand()を使うことができました。このようなものを使ってそれをIntにすることができます:

let myVar: Int = Int(Rand())

あなたの好きなCのランダム関数を使うことができ、必要ならばIntに値に変換するだけです。

16
Connor

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)
11
Sh_Khan

これはうまく動作するライブラリです 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
    }
}
9
demiculus
 let MAX : UInt32 = 9
 let MIN : UInt32 = 1

    func randomNumber()
{
    var random_number = Int(arc4random_uniform(MAX) + MIN)
    print ("random = ", random_number);
}
8
Rajesh Sharma

Swift本の乱数ジェネレーターの例は線形合同ジェネレーター(LCG)であるという既存の回答に追加したいと思います。これは厳しく制限されたものであり、必要不可欠な例を除きます。 、ランダム性の質はまったく重要ではありません。そして、LCGは暗号化の目的には決して使用しないでください

arc4random()ははるかに優れており、ほとんどの目的に使用できますが、ここでも暗号化の目的には使用しないでください。

暗号的に安全であることが保証されているものが必要な場合は、SecCopyRandomBytes()を使用してください。乱数ジェネレーターを何かに組み込むと、他の誰かが最終的に(誤って)暗号化目的(パスワード、キー、ソルト生成など)に使用する可能性があるため、とにかくSecCopyRandomBytes()あなたのニーズがそれを全く必要としないなら。

6

以来Swift 4.2

APIの新しいセットがあります。

let randomIntFrom0To10 = Int.random(in: 0 ..< 10)
let randomDouble = Double.random(in: 1 ... 10)
  • すべてのnumeric型には、rangeをとる random(in:) メソッドがあります。

  • その範囲に均一に分布した数値を返します。


TL; DR

さて、「良い」古い方法の何が問題になっていますか?

  1. インポートされたCAPI(プラットフォームによって異なります)を使用する必要があります

  2. そして更に...

ランダムがそれほどランダムではないと言ったらどうなるでしょうか?

arc4random()のようにarc4random() % aNumber(剰余を計算するために)を使用する場合、結果は均一ではありません0およびaNumberモジュロバイアスと呼ばれる問題があります。

モジュロバイアス

通常、関数は0MAXの乱数を生成します(タイプなどによって異なります) 。すばやく簡単な例を作るために、最大数が7で、0 ..< 2(または間隔[0、3)あなたがそれを好むなら)

個々の番号の確率は次のとおりです。

  • 0:3/8 = 37.5%
  • 1:3/8 = 37.5%
  • 2:2/8 = 25%

言い換えれば、あなたは可能性が高い最終的にまたは1より2。もちろん、これは非常に単純化されており、MAXの数値ははるかに高く、より「公平」であることを忘れないでください。

この問題は SE-0202-ランダム統合 inSwift 4.2で対処されています

6
Jakub Truhlář

Xcodeのバージョンによってはarc4Random_uniform()がないと(7.1では動作しますが、自動補完はされません)。代わりにこれを行うことができます。

0〜5の乱数を生成します。最初

import GameplayKit

それから

let diceRoll = GKRandomSource.sharedRandom().nextIntWithUpperBound(6)
5
brokenrhino
var randomNumber = Int(arc4random_uniform(UInt32(**5**)))

ここで5は0から5までの乱数が生成されることを確認します。それに応じて値を設定できます。

5
Taran Goel

次のコードは、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つがまだ本当にランダムに数えられるかどうか私は知らないが、あなたはそれをあなたの好みに合わせて微調整することができる:)

2
Zyphrax

Swift 4.2

Swift 4.2は標準ライブラリにネイティブでかなりフル装備の乱数APIを含んでいます。 ( スウィフトエボリューションプロポーザルSE-0202

let intBetween0to9 = Int.random(in: 0...9) 
let doubleBetween0to1 = Double.random(in: 0...1)

すべての数値型は静的な random(in :) を持ちます。これは範囲を取り、与えられた範囲の乱数を返します。

2
Suhit Patil

Swift 4.2

さようなら基礎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()
  1. 範囲からランダムな数字を生成するには、random(in :)を使用します。
  2. randomElement()は範囲が空の場合はnilを返すので、返されたIntのラップを解除しますか?とすれば。
  3. ランダムなDouble、Float、またはCGFloatを生成するにはrandom(in :)を使用し、ランダムなBoolを返すにはrandom()を使用します。

その他@ Official

2
iSrinivasan27

Swift 4.2、Xcode 10.1

IOS、macOS、tvOSの場合は、 システム全体のランダムソース XcodeのフレームワークGameKitで使用できます。ここでは、sharedRandom()クラスメソッドを使ってGKRandomSourceクラスを見つけることができます。

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)
1
ARGeo

詳細

xCode 9.1、Swift 4

数学指向のソリューション(1)

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
}

解決策の使い方(1)

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]

プログラマ向けソリューション(2)

数学指向のソリューション(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]
    }
}

ソリューションの使い方(2)

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]

フルサンプル

忘れないでください solution(1)とsolution(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]
}

サンプル結果

enter image description here

1

このコードを使って乱数を生成します。

//
//  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]
    }
}
0

このように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())
0
Durul Dalkanat