如何在不使用 while 循环的情况下生成不包括一个的随机数?

How do I generate a random number not including one without using a while loop?

假设我想生成一个介于 1 和 100 之间的随机数,但我不想包括 42。如果不重复随机方法直到它不是 42,我将如何做到这一点。

appzYourLife 有一些很棒的通用解决方案,但我想以轻量级的方式解决特定问题。

这两种方法的工作方式大致相同:缩小随机数生成器的范围以删除不可能的答案(99 个答案而不是 100 个),然后映射结果,使其不是非法值。

这两种方法都没有增加一个结果相对于另一个结果的概率。也就是说,假设您的随机数函数是完全随机的,结果仍然是随机的(例如,43 相对于 5 没有 2 倍的机会)。

方法 1:加法。

从 1 到 99 中获取一个随机数。如果它大于或等于您要避免的数字,请将其加一。

func approach1()->Int {
    var number = Int(arc4random_uniform(99)+1)
    if number >= 42 {
        number = number + 1
    }
    return number
}

例如,尝试从 1-5 生成一个不是 3 的随机数,取一个从 1 到 4 的随机数,如果它大于或等于 3,则加一。

  • rand(1..4) 产生 1, +0, = 1
  • rand(1..4) 产生 2, +0, = 2
  • rand(1..4) 产生 3, +1, = 4
  • rand(1..4) 产生 4, +1, = 5

方法二:回避。

另一种简单的方法是获取 1 到 99 之间的数字。如果它恰好等于您要避免的数字,请将其改为 100。

func approach2()->Int {
    var number = Int(arc4random_uniform(99)+1)
    if number == 42 {
        number = 100
    }
    return number
}

使用这个算法并再次将范围缩小到 1-5(同时避免 3),我们得到这些可能的结果:

  • rand(1..4) 产生 1;允许,所以 Result = 1
  • rand(1..4) 产生 2,允许,所以 Result = 2
  • rand(1..4) 产生 3;不允许,所以 Result = 5
  • rand(1..4) 产生 4,允许,所以 Result = 4

Updated for Swift 5.1

排除 1 个值

var nums = [Int](1...100)
nums.remove(at: 42)

let random = Int(arc4random_uniform(UInt32(nums.count)))
print(nums[random])

排除多个值

当您想排除超过 1 个值时,Range 的这个扩展确实提供了一个解决方案。

extension ClosedRange where Element: Hashable {
    func random(without excluded:[Element]) -> Element {
        let valid = Set(self).subtracting(Set(excluded))
        let random = Int(arc4random_uniform(UInt32(valid.count)))
        return Array(valid)[random]
    }
}

示例

(1...100).random(without: [40,50,60])

我相信第二个解决方案的计算复杂度是 O(n),其中 n 是范围中包含的元素数。

这里假设调用者提供的排除值不超过 n 个。