生成介于0和1之间的随机浮点数。

84

我想生成一个在0到1之间的随机数。我一直在阅读关于arc4random()的内容,但没有关于如何从中获取浮点数的信息。我该怎么办?

我正在尝试生成介于0到1之间的随机数字。我一直读到了有关arc4random()的文章,但是没有任何关于如何从中获取浮点数的信息。请问应该如何操作?


1
这是与浮点数相关的唯一问题,没有重复。 - P i
12个回答

141

生成 [0, 1) 区间内(包括0但不包括1)的随机值:

double val = ((double)arc4random() / UINT32_MAX);

更多细节在这里

实际范围为[0,0.999999999767169356],因为上限是(double)0xFFFFFFFF / 0x100000000。


3
“ARC4RANDOM_MAX”需要手动定义,这很奇怪,但是“0x100000000”等于4294967296,即“ULONG_MAX + 1”。有没有记录“arc4random()”的最大值是“ULONG_MAX”的地方?请提供相关文档。 - bobobobo
@bobobobo,来自这里:https://developer.apple.com/library/mac/#documentation/Darwin/Reference/ManPages/man3/arc4random.3.html arc4random()函数返回0到(2**32)-1范围内的伪随机数。 - Vladimir
8
实际上,该值应为0xFFFFFFFF,即(2的32次方减1)等于0x100000000 - 0x1 == 0xFFFFFFFF,正如Jörg Bühmann在链接中所述,这也是UINT32_MAX的当前状态。 - Josejulio
@Vladimir,ARC4RANDOM_MAX是什么类型?我想将其定义为一个变量。 - Iulian Onofrei
1
以这种方式生成的值不能保证是均匀分布的! - kelin
显示剩余4条评论

107
// Seed (only once)
srand48(time(0));

double x = drand48();

// Swift version
// Seed (only once)
srand48(Int(Date().timeIntervalSince1970))

let x = drand48()

drand48() 和 erand48() 函数返回非负的双精度浮点值,均匀分布于区间 [0.0 , 1.0]。


19
这应该是最佳答案。 - John Riselvato
你可能需要稍微更新一下答案,我在 NSHipster 上读到 drand48 需要使用种子初始化一次。Apple 文档 也建议进行初始化。 - dulaccc
1
需要种子是因为它为这个随机生成器提供了一个计算的起点。如果没有它,随机序列在应用程序启动时总是相同的。 - Jovan Stankovic
这是最好的答案。 - sabiland
这个答案对于double数据类型并不具有最优精度。请查看https://dev59.com/oW435IYBdhLWcg3w20B8#34765674,了解如何实现最佳精度。 - Cœur
我也认为drand48()是所有版本中最快的。 - sabiland

15

查看Swift 4.2以上版本的内容,请参见:https://dev59.com/oW435IYBdhLWcg3w20B8#50733095


以下是ObjC和Swift 4.1的正确统一性和最佳精度建议。

32位精度(Float的最佳选择)

[0, 1]范围内产生具有32位精度(包括0.0和1.0)的均匀随机值:

Obj-C:

float val = (float)arc4random() / UINT32_MAX;

Swift:


Swift是一种由苹果公司开发的编程语言,可用于开发iOS、macOS和其他Apple产品的应用程序。它旨在提供更加快捷、安全和易于维护的编码方式,并且具有现代化的语法和功能。
let val = Float(arc4random()) / Float(UInt32.max)

最优选择是:

  • 一个 Float(或 Float32),它的尾数精度为 24 位

48 位精度(不推荐)

使用 drand48在内部使用 arc4random_buf)可以轻松实现 48 位精度。但请注意,由于种子需求以及随机化所有双精度尾数 52 位的不足之处,此方法存在缺陷。

[0, 1] 范围内的均匀随机值,精度为 48 位:

Swift

// seed (only needed once)
srand48(Int(Date.timeIntervalSinceReferenceDate))
// random Double value
let val = drand48()

64位精度(适用于 DoubleFloat80

[0, 1] 区间内生成均匀随机数(包括0.0和1.0),最高达到64位精度:

Swift,使用两次 arc4random 调用:

let arc4random64 = UInt64(arc4random()) << 32 &+ UInt64(arc4random())
let val = Float80(arc4random64) / Float80(UInt64.max)

Swift, 通过一次调用arc4random_buf函数实现:

var arc4random64: UInt64 = 0
arc4random_buf(&arc4random64, MemoryLayout.size(ofValue: arc4random64))
let val = Float80(arc4random64) / Float80(UInt64.max)

这是最优的选择:

  • 一个 Double(或者 Float64)其底数精度为 52 位
  • 一个 Float80 其底数精度为 64 位

注解

与其他方法的比较

当范围不包括边界(0 或 1)时,答案可能会受到均匀性偏差的影响,应该避免使用。

  • 使用 arc4random(),最佳精度为 1 / 0xFFFFFFFF (UINT32_MAX)
  • 使用 arc4random_uniform(),最佳精度为 1 / 0xFFFFFFFE (UINT32_MAX-1)
  • 使用 rand() (秘密使用 arc4random),最佳精度为 1 / 0x7FFFFFFF (RAND_MAX)
  • 使用 random() (秘密使用 arc4random),最佳精度为 1 / 0x7FFFFFFF (RAND_MAX)

单次调用 arc4randomarc4random_uniformrandrandom 不可能获得超过 32 位的精度。因此,我们上述的 32 位和 64 位解决方案应该是我们可以实现的最佳结果。


"Float80" 在真实的 iOS 设备上不可用(仅在模拟器上工作)。 - Cœur

10

这个函数同样适用于负浮点数范围:

float randomFloat(float Min, float Max){
    return ((arc4random()%RAND_MAX)/(RAND_MAX*1.0))*(Max-Min)+Min;
}

4
外来模数运算。改用 Min+(Max-Min)*((float)arc4random())/ULONG_MAX(float) 转换只是谨慎起见。 - bobobobo
@bobobobo 虽然我同意多余的取模,但我建议除以 UINT32_MAX(始终为 4294967295)而不是 ULONG_MAX(64 位上为 18446744073709551615)。 - Cœur

7

Swift 4.2+

Swift 4.2新增对范围内随机值的本地支持:

let x = Float.random(in: 0.0...1.0)
let y = Double.random(in: 0.0...1.0)
let z = Float80.random(in: 0.0...1.0)

文档:


1

Swift 4.2

Swift 4.2在标准库中包含了一个本地且相当完整的随机数API。(Swift进化提案SE-0202)

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

所有数字类型都有静态的random(in:)函数,该函数接受范围并返回给定范围内的随机数。

1

这是针对Swift 3.1的浮点随机数扩展

// MARK: Float Extension

public extension Float {

    /// Returns a random floating point number between 0.0 and 1.0, inclusive.
    public static var random: Float {
        return Float(arc4random()) / Float(UInt32.max))
    }

    /// Random float between 0 and n-1.
    ///
    /// - Parameter n:  Interval max
    /// - Returns:      Returns a random float point number between 0 and n max
    public static func random(min: Float, max: Float) -> Float {
        return Float.random * (max - min) + min
    }
}

@Cœur 谢谢你,我已经更改了。 - YannSteph

1
(float)rand() / RAND_MAX

之前的帖子声称仅使用"rand()"是不正确的。 以下是正确使用rand()的方法。

这将创建一个介于0-1之间的数字

BSD文档:

rand()函数计算0到RAND_MAX(由头文件"stdlib.h"定义)范围内的伪随机整数序列。


0

使用此方法可避免与arc4random()的上限问题

u_int32_t upper_bound = 1000000;

float r = arc4random_uniform(upper_bound)*1.0/upper_bound;

请注意,此适用于MAC_10_7,IPHONE_4_3及更高版本。

这里存在一个基本的范围问题。当您将upper_bound设置为10时,arc4random_uniform将返回0到9之间的值,最终结果将从0.0000到0.9000。您应该改为除以upper_bound-1。然而,由于upper_bound引起的任意低精度仍然存在,因此您应该将upper_bound增加到UINT32_MAX。使用arc4random()*1.0 / UINT32_MAX而不是arc4random_uniform(UINT32_MAX)*1.0 / (UINT32_MAX-1)可以获得更好的精度。 - Cœur

-1

arc4random 的范围高达0x100000000(4294967296)

这是另一个生成0到1之间随机数的好选择:

srand48(time(0));      // pseudo-random number initializer.
double r = drand48();

arc4random的最大值为0xFFFFFFFF。 - Cœur
感谢@Cœur ..max val 0xFFFFFFFF,我之前不知道。 - Gurunatha Dogi
你的解决方案与Jovan Stankovic的答案完全相同。 - Cœur
哦,我没有看到@Cœur的消息。 - Gurunatha Dogi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接