【问题标题】:How to generate a random number in Swift without repeating the previous random number?如何在 Swift 中生成随机数而不重复之前的随机数?
【发布时间】:2014-12-18 07:19:44
【问题描述】:

我对 Swift 和编程逻辑很陌生,所以请耐心等待

如何在 Swift 中生成 0 到 9 之间的随机数而不重复上次生成的数字?因为同一个数字不会连续出现两次。

【问题讨论】:

  • 不,不重复
  • 你可以做那部分。通过设置一个简单的条件。
  • 是的,我知道,但我不知道该怎么做。这就是我问这个问题的原因。正如我所说,我对 Swift 很陌生
  • 这可以通过每次只生成一个随机数来完成。看我的回答。

标签: swift


【解决方案1】:

我的解决方案,我认为它很容易理解

var nums = [0,1,2,3,4,5,6,7,8,9]

while nums.count > 0 {

    // random key from array
    let arrayKey = Int(arc4random_uniform(UInt32(nums.count)))

    // your random number
    let randNum = nums[arrayKey] 

    // make sure the number isnt repeated
    nums.swapAt(arrayKey, nums.count-1)
    nums.removeLast()
}

【讨论】:

    【解决方案2】:

    将之前生成的数字存储在一个变量中,并将生成的数字与之前的数字进行比较。如果它们匹配,则生成一个新的随机数。重复生成新数字,直到它们不匹配为止。

    var previousNumber: UInt32? // used in randomNumber() 
    
    func randomNumber() -> UInt32 {
        var randomNumber = arc4random_uniform(10)
        while previousNumber == randomNumber {
            randomNumber = arc4random_uniform(10)
        }
        previousNumber = randomNumber
        return randomNumber
    }
    

    【讨论】:

    • ... 理论上,对于 randomNumber() 的任何调用都不能保证在 while 循环上允许的给定非无限迭代次数终止(即使它在练习,很快就会:))。对于确定性解决方案(w.r.t. 调用randomNumber() 中的程序流),see vacawama:s clever answer below。
    • 这个答案完全错误并且会崩溃。这是一个不确定的算法 - 一个非常基本的计算机科学错误。
    • @Fattie:你能详细说明一下吗?为什么是错的?在什么情况下会崩溃?
    • 我的错,崩溃是指进入无限循环
    • arc4random() 和新的Int.random(in:) 是经过充分测试的伪随机数生成器,它们不会无限频繁地返回相同的数字。在 10 个数字的情况下,k 次重复的概率是 1/10^k。
    【解决方案3】:

    Swift 5 更新

    这是一个很好的技巧,可以从先前选择的数字中进行平均选择。

    您有 10 个号码,但您只想从 9 个号码中进行选择(0 到 9,但不包括前一个号码)。 如果将范围减少 1,则可以从 9 个随机数中进行选择,然后将重复的数字替换为该范围的前一个顶部数字。这样每次只需要生成一个随机数就可以了。

    这可以实现为Int.random(in:excluding:),您将想要的值传递给exclude。

    extension Int {
        static func random(in range: ClosedRange<Int>, excluding x: Int) -> Int {
            if range.contains(x) {
                let r = Int.random(in: Range(uncheckedBounds: (range.lowerBound, range.upperBound)))
                return r == x ? range.upperBound : r
            } else {
                return Int.random(in: range)
            }
        }
    }
    

    示例:

    // Generate 30 numbers in the range 1...3 without repeating the
    // previous number  
    var r = Int.random(in: 1...3)
    for _ in 1...30 {
        r = Int.random(in: 1...3, excluding: r)
        print(r, terminator: " ")
    }
    print()
    

    1 3 2 1 2 1 3 2 1 3 1 3 2 3 1 2 3 2 1 3 2 1 3 1 2 3 2 1 2 1 3 2 3 2 1 3 1 2 1 2


    上一个答案:

    var previousNumber = arc4random_uniform(10)   // seed the previous number
    
    func randomNumber() -> UInt32 {
        var randomNumber = arc4random_uniform(9)  // generate 0...8
        if randomNumber == previousNumber {
            randomNumber = 9
        }
        previousNumber = randomNumber
        return randomNumber
    }
    

    【讨论】:

    • @vacawama 我现在看到了诀窍([0, 9] 范围内的 9 个整数的一致性,其中先前选择的数字已被排除),感谢您的解释!
    • 特别的是,撇开数组改组方法不谈,这是唯一一个保证在该线程的答案中“终止”的一次随机数方法(“终止“在此处的终止确定性解决方案中,这并不是一个有趣的术语,而是使用例如在接受的答案中使用的蛮力循环方法)。所以我留下这个额外的评论,指出我相信这应该是公认的答案。
    • 值得注意的是,这种方法非常适合在选择第一个数字后选择第二个数字,但当您想从 k 池中选择 n 个随机且唯一的数字时效果不佳数字。那时需要某种集合。我建议在这里查看我对类似问题的解决方案:stackoverflow.com/a/46029193/4273137
    • @Fattie,我检查了您的链接,您提供的算法与我的没有太大不同。两者都从 n-1 个选项中统一选择以避免重复。从逻辑上讲,我的算法所做的是将排除的值替换为第 n 个位置的值,然后从 n-1 个值中进行选择。
    • @Fattie,我的答案和你的一样好,可以选择随机值而不重复。如果你想争辩说做模运算比“如果”更快,我会给你。但这几乎不会让我的回答变得可怕。
    【解决方案4】:

    最简单的方法是使用repeat/while:

    let current = ...
    var next: Int
    
    repeat {
        next = Int(arc4random_uniform(9))
    } while current == next
    
    // Use `next`
    

    【讨论】:

      【解决方案5】:

      将你想要的所有值放入一个数组中,使用 arc4random_uniform(SIZEOFARRAY) 生成一个随机数并从数组中拉取随机值的索引,然后重复此过程直到数组为空。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2018-07-03
        • 2015-07-12
        • 1970-01-01
        • 2013-03-13
        • 2014-07-23
        • 1970-01-01
        • 2012-01-23
        相关资源
        最近更新 更多