How does one generate a random number in Apple'

2018-12-31 13:07发布

I realize the Swift book provided an implementation of a random number generator. Is the best practice to copy and paste this implementation in one's own program? Or is there a library that does this that we can use now?

标签: swift random
25条回答
琉璃瓶的回忆
2楼-- · 2018-12-31 13:26

Example for random number in between 10 (0-9);

import UIKit

let randomNumber = Int(arc4random_uniform(10))

Very easy code - simple and short.

查看更多
美炸的是我
3楼-- · 2018-12-31 13:29

The following code will produce a secure random number between 0 and 255:

extension UInt8 {
  public static var random: UInt8 {
    var number: UInt8 = 0
    _ = SecRandomCopyBytes(kSecRandomDefault, 1, &number)
    return number
  }
}

You call it like this:

print(UInt8.random)

For bigger numbers it becomes more complicated.
This is the best I could come up with:

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

These methods use an extra random number to determine how many UInt8s are going to be used to create the random number. The last line converts the [UInt8] to UInt16 or UInt32.

I don't know if the last two still count as truly random, but you can tweak it to your likings :)

查看更多
临风纵饮
4楼-- · 2018-12-31 13:30
var randomNumber = Int(arc4random_uniform(UInt32(**5**)))

Here 5 will make sure that the random number is generated though zero to five. You can set the value accordingly.

查看更多
听够珍惜
5楼-- · 2018-12-31 13:32

I used this code:

var k: Int = random() % 10;
查看更多
其实,你不懂
6楼-- · 2018-12-31 13:32

Since Swift 4.2

There is a new set of APIs:

let randomIntFrom0To10 = Int.random(in: 0 ..< 10)
let randomDouble = Double.random(in: 1 ... 10)
  • All numeric types now have the random(in:) method that takes range.

  • It returns a number uniformly distributed in that range.


TL;DR

Well, what is wrong with the "good" old way?

  1. You have to use imported C APIs (They are different between platforms).

  2. And moreover...

What if I told you that the random is not that random?

If you use arc4random() (to calculate the remainder) like arc4random() % aNumber, the result is not uniformly distributed between the 0 and aNumber. There is a problem called the Modulo bias.

Modulo bias

Normally, the function generates a random number between 0 and MAX (depends on the type etc.). To make a quick, easy example, let's say the max number is 7 and you care about a random number in the range 0 ..< 2 (or the interval [0, 3) if you prefer that).

The probabilities for individual numbers are:

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

In other words, you are more likely to end up with 0 or 1 than 2. Of course, bare in mind that this is extremely simplified and the MAX number is much higher, making it more "fair".

This problem is addressed by SE-0202 - Random unification in Swift 4.2

查看更多
低头抚发
7楼-- · 2018-12-31 13:33

Details

xCode 9.1, Swift 4

Math oriented solution (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
}

Usage of solution (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]

Programmers oriented solution (2)

Do not forget to add Math oriented solution (1) code here

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

Usage of solution (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]

Full Sample

Do not forget to add solution (1) and solution (2) codes here

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

Sample result

enter image description here

查看更多
登录 后发表回答