在Swift中生成随机数

7

@TroyT:实际上,这段代码中没有涉及到强制类型转换。OP只是从Int值创建了一个UInt32。 - Luca Angeletti
@TroyT:看一下我的扩展。它为Int添加了一个初始化器,该初始化器接收一个范围并生成该范围内的一个Int - Luca Angeletti
1
@appzYourLife 我点赞了 :) 比我的回答更具可重用性。 - tktsubota
3
不奇怪,它是类型安全的。这是很大的区别。 - Sulthan
6个回答

7

这取决于你想要避免多少类型转换。你可以将其简单地包装在一个函数中:

func random(max maxNumber: Int) -> Int {
    return Int(arc4random_uniform(UInt32(maxNumber)))
}

那么你只需要进行一次繁琐的转换即可。无论何处您想要一个最大数字的随机数:

let r = random(max: _names.count)
let name: String = _names[r]

顺带一提,因为这是Swift语言,你的属性名在前面不需要加上_


你的 random()函数无法编译。你必须强制转换arc4random_uniform()的返回值。 - Martin R
@MartinR 哎呀,我会修复它的。 - tktsubota

3
我非常喜欢使用这个扩展程序。
extension Int {
    init(random range: Range<Int>) {

        let offset: Int
        if range.startIndex < 0 {
            offset = abs(range.startIndex)
        } else {
            offset = 0
        }

        let min = UInt32(range.startIndex + offset)
        let max = UInt32(range.endIndex   + offset)

        self = Int(min + arc4random_uniform(max - min)) - offset
    }
}

现在您可以生成一个随机的Int,表示范围。
let a = Int(random: 1...10) // 3
let b = Int(random: 0..<10) // 6
let c = Int(random: 0...100) // 31
let d = Int(random: -10...3) // -4

你可以直接扩展范围本身。https://dev59.com/HJLea4cB1Zd3GeqP7-F8#34712601 - Leo Dabus
1
@LeoDabus:非常好! - Luca Angeletti

2

Swift 4.2及以上版本

您可以初始化一个随机元素:

let randomInt = Int.random(in: 0...1)
let randomDouble = Double.random(in: 0...1)

您可以从集合中随机选择一个元素,例如:
let randomInt = (0...9).randomElement()

还有另一种方便的方法可以用于对数组进行洗牌:

let shuffled = [1, 2, 3, 4].shuffled()

1
你可以使用GameplayKit。
let random = GKRandomDistribution(lowestValue: 0, highestValue: 100)
let r = random.nextInt()

2
这取决于使用情况,苹果文档中指出:GameplayKit提供的随机化服务适用于可靠地创建确定性的伪随机游戏机制,但不具备密码学的强韧性。对于密码学、混淆或密码使用,请使用安全框架,详见密码服务指南。 arc4random()arc4random_uniform() 具备密码学的强韧性。 - zaph

1

这是由Luca修改后以Swift 4编写的扩展答案。

/// Returns random number within given range, upper bound included, eg. -1...0 = [-1, 0, 1]
extension CountableClosedRange where Bound == Int
{
    var random: Int
    {
        let range = self
        let offset: Int = range.lowerBound < 0 ? abs(range.lowerBound) : 0
        let min = UInt32(range.lowerBound + offset)
        let max = UInt32(range.upperBound + offset)
        let randomNumber = Int(min + arc4random_uniform(max - min + 1)) - offset

        return randomNumber
    }
}

/// Returns random number within given range, upper bound not included, eg. -1...0 = [-1, 0]
extension CountableRange where Bound == Int
{
    var random: Int
    {
        let range = self
        let offset: Int = range.lowerBound < 0 ? abs(range.lowerBound) : 0
        let min = UInt32(range.lowerBound + offset)
        let max = UInt32(range.upperBound + offset)
        let randomNumber = Int(min + arc4random_uniform(max - min)) - offset

        return randomNumber
    }
}

例子:

示例:

(0...10).random
(0..<10).random

0

或者你可以使用

 let name : String = _names[ Int(arc4random()) % _names.count ]

OP正在从列表中选择一个名称。这几乎不足以成为模数偏差可以产生任何相关差异的有效场景。在列表中有4096个名称时,偏差将在一半的条目上为0.000096%。 - Alain T.
为什么使用 arc4random()arc4random_uniform() 更好?这个问题与 arc4random_uniform 的强制转换有关。 - zaph
1
这并不是更好,只是写起来更短,因此考虑到OP的目标,它是一个可行的替代方案。 - Alain T.
2
我们不应该为了“更短的代码”而牺牲正确性,编写代码是我们的职责。当我们在编写代码时,我们不应该妥协于更差的代码(即使只有一点点)来节省输入 UInt32() 的时间。也许这就是为什么我们还不是真正的专业人士,因为我们太懒或者不够关心,不愿意多输入八个字符。 - zaph

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