快速搜索随机数_随机数如何快速工作

快速搜索随机数

Before Swift 4.2, generating random numbers involved using raw C APIs. With the addition of the RandomNumberGeneratorprotocol in Swift 4.2, developers were graced with an easy way to generate random numbers. Let’s see how to use the new APIs, but most importantly, how they are implemented inside the Swift compiler.

在Swift 4.2之前,使用原始C API生成随机数。 通过在Swift 4.2中添加RandomNumberGenerator协议,开发人员可以轻松地生成随机数。 让我们看看如何使用新的API,但最重要的是, 如何在Swift编译器中实现它们

RandomNumberGenerator (RandomNumberGenerator)

Deep down, the generation of random numbers in Swift still works exactly like before. The difference is that Swift 4.2 introduced some nice abstractions on top of it in the form of protocols — allowing you to create your own randomization algorithms if you need to.

纵深来看,在Swift中随机数的生成仍然像以前一样工作。 区别在于Swift 4.2以协议的形式在其之上引入了一些不错的抽象-允许您根据需要创建自己的随机算法。

The generation of random numbers in Swift begins with RandomNumberGenerator — a protocol that does nothing but generate random UInt64 values:

在Swift中随机数的生成从RandomNumberGenerator开始-该协议除了生成随机的UInt64值外什么都不做:

public protocol RandomNumberGenerator {
mutating func next() -> UInt64
}

Types that can access/generate random values are meant to receive an implementation of this protocol, generate a (possibly very big) random number and use it to determine which value to return (for example, by calculating randomNumber % range.upperBound).

可以访问/生成随机值的类型旨在接收该协议的实现,生成(可能非常大)随机数,并使用它来确定要返回的值(例如,通过计算randomNumber range.upperBound )。

The Swift Standard Library provides an all-purpose implementation of RandomNumberGenerator called SystemRandomNumberGenerator, which pulls a random number from the innards of the compiler.

雨燕标准库提供了一个通用的实现RandomNumberGenerator称为SystemRandomNumberGenerator ,从而牵引来自编译器的内部结构的随机数。

public struct SystemRandomNumberGenerator: RandomNumberGenerator {
public mutating func next() -> UInt64 {
var random: UInt64 = 0
swift_stdlib_random(&random, MemoryLayout<UInt64>.size)
return random
}
}

The actual random mechanism used differs by platform, but is guaranteed to be type-safe in all of them. For Apple platforms, the compiler uses arc4random_buf (whose functionality requires a separate article):

实际使用的随机机制因平台而异,但在所有平台中都保证是类型安全的。 对于Apple平台,编译器使用arc4random_buf (其功能需要单独的文章):

#if defined(__APPLE__)SWIFT_RUNTIME_STDLIB_API
void swift::swift_stdlib_random(void *buf, __swift_size_t nbytes) {
arc4random_buf(buf, nbytes);
}

Swift随机API (Swift Random APIs)

From Swift 4.2, numeric types like Int, Float and Bool can generate random numbers through the new .random(in:) APIs:

从Swift 4.2开始,数值类型(如IntFloatBool可以通过新的.random(in:) API生成随机数:

let randomInt = Int.random(in: 1..<5)
let randomFloat = Float.random(in: 1..<10)
let randomBool = Bool.random()

All of these APIs support taking a RandomNumberGenerator argument, but you’re allowed to not provide one, as the compiler is rigged to use SystemRandomNumberGenerator by default. For example, here’s how the default API for Bool is defined:

所有这些API都支持采用RandomNumberGenerator参数,但是您不能提供任何参数,因为默认情况下,编译器会尝试使用SystemRandomNumberGenerator 。 例如,以下是Bool的默认API的定义方式:

public static func random() -> Bool {
var g = SystemRandomNumberGenerator()
return Bool.random(using: &g)
}

Bool.random() (Bool.random())

In the case of Bool, the full implementation of the API will get the raw UInt64value from the generator, bit shift it to the right 17 times, and return true if the first bit of the resulting value is 0.

对于Bool ,API的完整实现将从生成器获取原始UInt64值,将其右移17次,如果结果值的第一位为0 ,则返回true。

public static func random<T: RandomNumberGenerator>(
using generator: inout T
) -> Bool {
return (generator.next() >> 17) & 1 == 0
}

The reason the value is shifted exactly 17 times is that (some) weak RNGs have better randomness properties in the middle bits over the low/high bits, and the Swift team felt like protecting us from APIs that decided to use these RNGs instead of the default SystemRandomNumberGenerator. Before the PR that implemented this improvement, Bool.random() used to simply return generator.next() % 2 == 0.

值被精确地偏移17次的原因是(一些)弱RNG在中间位上具有比低/高位更好的随机性,Swift团队感觉像是在保护我们免受那些决定使用这些RNG而不是RNG的API的侵害。默认为SystemRandomNumberGenerator 。 在实现此改进的PR之前, Bool.random()曾经简单地返回generator.next() % 2 == 0

Int.random()和其他 (Int.random() and others)

The implementation of the random API for other numeric types is similar to Bool, with the difference being how the value is pre/post-processed. For example, for ..< Int ranges, Swift calculates the distances between the bounds, generates a value, makes sure it’s less than upperBoundand adds lowerBoundto it, resulting in a random value inside the requested range.

其他数字类型的随机API的实现与Bool相似,不同之处在于该值是如何进行前/后处理的。 例如, for ..< Int范围,Swift计算边界之间的距离,生成一个值,确保其小于upperBound并向其添加lowerBound ,从而在请求的范围内生成随机值。

// Note: The actual implementation is slightly different than this
// because it treats compiler edge cases and uses some different types.
let delta = range.upperBound &- range.lowerBound
return range.lowerBound + generator.next(upperBound: delta)

An interesting note is that generator.next(upperBound: delta) isn’t simply a value % delta calculation — it uses Daniel Lemire’s “Fast Random Integer Generation in Interval” algorithm, which produces a higher quality result (for example, it avoids module bias).

有趣的是, generator.next(upperBound: delta)并不是简单的%delta值计算-它使用了Daniel Lemire的“间隔中的快速随机整数生成”算法 ,该算法产生更高质量的结果(例如,避免使用模块偏压)。

public mutating func next<T: FixedWidthInteger & UnsignedInteger>(
upperBound: T
) -> T {
var random: T = next()
var m = random.multipliedFullWidth(by: upperBound)
if m.low < upperBound {
let t = (0 &- upperBound) % upperBound
while m.low < t {
random = next()
m = random.multipliedFullWidth(by: upperBound)
}
}
return m.high
}

A funny aspect of this algorithm is that it can, in theory, take forever to run as it has a while loop that continuously discards what it considers to be “low quality” values, but in reality, the odds of it being anything slower than “pretty much instant” are so low that you should never consider this.

该算法一个有趣的方面是,从理论上讲,它可以永久运行,因为它有一个while循环,该循环不断丢弃它认为是“低质量”值的东西,但实际上,它的发生几率比“几乎立即”太低了,您永远都不要考虑这一点。

randomElement() (randomElement())

To top it off, let’s take a look at another API added in Swift 4.2: The randomElement() method for Collections, which returns a random element from the collection:

最重要的是,让我们看一下Swift 4.2中添加的另一个API: CollectionsrandomElement()方法,该方法从Collections中返回一个随机元素:

let string = ["Swift", "Rocks", "by", "Bruno", "Rocha"].randomElement()
// Bruno

randomElement() is defined as a Collectionextension, and simply grabs a random index through the Int.random(in:) method that we spelunked previously.

randomElement()被定义为Collection扩展,它只是通过我们之前介绍的Int.random(in:)方法Int.random(in:)了一个随机索引。

public func randomElement<T: RandomNumberGenerator>(
using generator: inout T
) -> Element? {
guard !isEmpty else { return nil }
let random = Int.random(in: 0 ..< count, using: &generator)
let idx = index(startIndex, offsetBy: random)
return self[idx]
}

Perhaps an interesting take here is that this is a global Collectionextension, which means that this will work for any type of collection (usually, you would see something like this only for RandomAccessCollections). This also means that the runtime complexity of this method will vary depending on the collection, as countis only O(1) in RandomAccessCollections. In other types of collections, countis O(n).

也许有趣的是,这是一个全局的Collection扩展,这意味着它适用于任何类型的collection(通常,您只会在RandomAccessCollections看到类似的内容)。 这也意味着此方法的运行时复杂度将根据集合而有所不同,因为RandomAccessCollections count仅为O(1)。 在其他类型的集合中, count为O(n)。

翻译自: https://medium.com/swlh/how-random-numbers-work-in-swift-67e9fce63079

快速搜索随机数

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值