【问题标题】:Generate list of unique random numbers in Swift from range从范围生成 Swift 中唯一随机数的列表
【发布时间】:2018-04-02 16:08:06
【问题描述】:

我想写一个返回整数列表的函数。整数列表大小和最大随机数将由用户定义,用户将通过函数参数。我的特殊要求是,列表中的整数不能是多余的。
我使用Array的方法:

func getRandomNumbers(maxNumber: Int, listSize: Int)-> [Int]{
    var randomNumbers = [Int]()
    for _ in 1...listSize{
        let randomNumber = Int(arc4random_uniform(UInt32(listSize)))
        randomNumbers.append(randomNumber)
    }
    return randomNumbers
}

问题:有时我用这种方法得到重复的值。
我知道 swift Set 没有重复值。

我使用Set的方法:

func getRandomNumbers(maxNumber: Int, listSize: Int)-> Set<Int>{
    var randomNumbers = Set<Int>()
    for _ in 1...listSize{
        let randomNumber = Int(arc4random_uniform(UInt32(listSize)))
        randomNumbers.insert(randomNumber)
    }
    return randomNumbers
} 

问题:有时Set 的大小小于用户定义的大小。

【问题讨论】:

    标签: swift random arc4random


    【解决方案1】:

    Swift 5 版本实现

    extension Int {
    
        static func getUniqueRandomNumbers(min: Int, max: Int, count: Int) -> [Int] {
            var set = Set<Int>()
            while set.count < count {
                set.insert(Int.random(in: min...max))
            }
            return Array(set)
        }
    
    }
    

    例如:

    let uniqueNumbers = Int.getUniqueRandomNumbers(min: 1000, max: 1500, count: 10)
    

    示例结果:

    [1454, 1105, 1305, 1176, 1498, 1127, 1310, 1209, 1373, 1198]
    

    【讨论】:

      【解决方案2】:

      这里有两个问题:

      1. 您没有生成足够的数字。您需要不断生成随机数,直到您的集合足够大:

        func getRandomNumbers(maxNumber: Int, listSize: Int)-> [Int] {
            var randomNumbers = Set<Int>()
            while randomNumbers.count < listSize {
                let randomNumber = Int(arc4random_uniform(UInt32(maxNumber+1)))
                randomNumbers.insert(randomNumber)
            }
            return randomNumbers
        }
        
      2. 您通过将随机数放入排序集选择中来使它们产生偏差,这是高度可预测的。您应该将您的数字附加到一个数组中(以保留它们以便生成它们),同时仍然并行利用一组,以实现快速重复数据删除:

        func getRandomNumbers(maxNumber: Int, listSize: Int)-> [Int] {
            precondition(listSize < maxNumber, "Cannot generate a list of \(listSize) unique numbers, if they all have to be less than \(maxNumber)")
        
            var randomNumbers = (array: [Int](), set: Set<Int>())
        
            while randomNumbers.set.count < listSize {
                let randomNumber = Int(arc4random_uniform(UInt32(maxNumber+1)))
                if randomNumbers.set.insert(randomNumber).inserted { // If the number is unique
                    randomNumbers.array.append(randomNumber) // then also add it to the arary
                }
            }
        
            return randomNumbers.array
        }
        

      【讨论】:

      • 正是@Alexander 先生,我在提出问题后想出了这个在 set 时使用来解决我的问题。你的第二个选择很棒。非常感谢。
      • 您可能还应该检查 maxNumber 是否小于 listSize,因此您不要尝试生成 0 到 10 范围内的 100 个唯一随机数。
      • 有效点,但在这里我假设 maxNumber 总是大于 listSize。这就是为什么不做任何检查。
      • @KaziAbdullahAlMamun 假设使 ume 成为一个 ass
      • 插入检查我的功能,不再假设。
      【解决方案3】:

      当您获得重复的随机数时会发生这种情况。您可以检查 randomNumbers.count 并生成随机数,直到它达到 listSize,而不是生成 listSize 随机数。

      【讨论】:

        【解决方案4】:

        您只需要使用 while count is less than listSize 而不是 for 循环,而不是您需要将 maxNumber 加一而不是 listSize 传递给 arc4random_uniform:

        func getRandomNumbers(maxNumber: Int, listSize: Int)-> Set<Int> {
            var randomNumbers = Set<Int>()
            while randomNumbers.count < listSize {
                let randomNumber = Int(arc4random_uniform(UInt32(maxNumber+1)))
                randomNumbers.insert(randomNumber)
            }
            return randomNumbers
        }
        
        getRandomNumbers(maxNumber: 10, listSize: 3)  // {5, 7, 0}
        

        【讨论】:

        • 非常感谢先生。
        【解决方案5】:

        这里还有另一种可能性。我不认为这是最好的,但在我看来 IndexSet 可能是解决问题的好方法(基本上,这个想法书呆子狙击了我,我必须找到答案),这就是我想出了。

        func getRandomNumbers(maxNumber: Int, listSize: Int)-> [Int]{
            guard maxNumber < listSize else { return [] }
            var indexSet = IndexSet(integersIn: 0...listSize)
            var randomInts = [Int]()
        
            while randomInts.count < maxNumber {
                guard let currentInt = indexSet.integerLessThanOrEqualTo(Int(arc4random_uniform(UInt32(listSize)))) else {
                    continue
                }
                randomInts.append(currentInt)
                indexSet.remove(currentInt)
            }
            return randomInts
        }
        

        这个想法是创建一个 IndexSet 覆盖 0 到请求的最大值,然后从该集合中随机删除元素,直到你有足够的元素。它通过在使用整数时从集合中删除整数来避免重复。 IndexSet 实际上不是 Set,它是一种存储一堆唯一整数的有效方式,因此它获得了 Set 的唯一性,而无需实际创建包含范围内所有整数的 Set

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2013-12-14
          • 2011-08-02
          • 2013-04-22
          • 1970-01-01
          • 2015-12-22
          • 1970-01-01
          • 1970-01-01
          • 2012-08-19
          相关资源
          最近更新 更多