从Swift数组中随机选择一个项目而不重复

时间:2016-03-27 23:28:50

标签: ios swift random

此代码从预设颜色数组中选择一种随机颜色。我如何制作它,以便不会多次挑选相同的颜色?

var colorArray = [(UIColor.redColor(), "red"), (UIColor.greenColor(), "green"), (UIColor.blueColor(), "blue"), (UIColor.yellowColor(), "yellow"), (UIColor.orangeColor(), "orange"), (UIColor.lightGrayColor(), "grey")]

var random = { () -> Int in
    return Int(arc4random_uniform(UInt32(colorArray.count)))
} // makes random number, you can make it more reusable


var (sourceColor, sourceName) = (colorArray[random()])

6 个答案:

答案 0 :(得分:7)

创建索引数组。从数组中删除其中一个索引,然后使用它来获取颜色。

这样的事情:

var colorArray = [
  (UIColor.redColor(), "red"), 
  (UIColor.greenColor(), "green"), 
  (UIColor.blueColor(), "blue"), 
  (UIColor.yellowColor(), "yellow"), 
  (UIColor.orangeColor(), "orange"), 
  (UIColor.lightGrayColor(), "grey")]

var indexes = [Int]();

func randomItem() -> UIColor
{
  if indexes.count == 0
  {
    print("Filling indexes array")
    indexes = Array(0..< colorArray.count)
  }
  let randomIndex = Int(arc4random_uniform(UInt32(indexes.count)))
  let anIndex = indexes.removeAtIndex(randomIndex)
  return colorArray[anIndex].0;
}

上面的代码创建了一个数组indexes。函数randomItem查看indexes是否为空。如果是,则用索引值填充它,范围从0到colorArray.count - 1

然后在indexes数组中选择一个随机索引,删除indexes数组中该索引处的值,并使用它从colorArray获取并返回一个对象。 (它不会从colorArray中删除对象。它使用间接,并从indicesArray中删除对象,indexArray最初包含colorArray中每个条目的索引值。

上面的一个缺陷是,从indexArray中获取最后一项后,用一整套索引填充它,并且从新重新填充的数组中获得的下一种颜色可能是你得到的最后一个。

可以添加额外的逻辑来防止这种情况发生。

答案 1 :(得分:2)

基于这个事实,arc4random_uniform不仅生成随机数,而且生成均匀分布的数字

import Foundation // arc4random_uniform

class Random {
    var r:UInt32
    let max: UInt32
    init(max: UInt32) {
        self.max = max
        r = arc4random_uniform(max)
    }
    var next: UInt32 {
        var ret: UInt32
        repeat {
            ret = arc4random_uniform(max)
        } while r == ret
        r = ret
        return r
    }
}
// usage example
let r = Random(max: 5)
for i in 0..<10 {
    print(r.r, r.next) // there will never be a pair of the same numbers in the
    // generated stream
}
/*
 2 4
 4 0
 0 3
 3 0
 0 3
 3 4
 4 1
 1 3
 3 4
 4 3
 */

简单测试不同的k和一个百万的流长度

class Random {
    var r:UInt32
    let max: UInt32
    init(max: UInt32) {
        self.max = max
        r = arc4random_uniform(max)
    }
    var next: (UInt32, Int) {
        var i = 0
        var ret: UInt32
        repeat {
            ret = arc4random_uniform(max)
            i += 1
        } while r == ret
        r = ret
        return (r,i)
    }
}
for k in 3..<16 {
    let r = Random(max: UInt32(k))
    var repetition = 0
    var sum = 0
    for i in 0..<1000000 {
        let j = r.next
        repetition = max(repetition, j.1)
        sum += j.1
    }
    print("maximum of while repetition for k:", k, "is", repetition, "with average of", Double(sum) / Double(1000000) )
}

打印

maximum of while repetition for k: 3 is 15 with average of 1.499832
maximum of while repetition for k: 4 is 12 with average of 1.334008
maximum of while repetition for k: 5 is 9 with average of 1.250487
maximum of while repetition for k: 6 is 8 with average of 1.199631
maximum of while repetition for k: 7 is 8 with average of 1.167501
maximum of while repetition for k: 8 is 7 with average of 1.142799
maximum of while repetition for k: 9 is 8 with average of 1.124096
maximum of while repetition for k: 10 is 6 with average of 1.111178
maximum of while repetition for k: 11 is 7 with average of 1.099815
maximum of while repetition for k: 12 is 7 with average of 1.091041
maximum of while repetition for k: 13 is 6 with average of 1.083582
maximum of while repetition for k: 14 is 6 with average of 1.076595
maximum of while repetition for k: 15 is 6 with average of 1.071965

最终,这是基于相同想法的更多Swifty和功能性方法

import Foundation

func random(max: Int)->()->Int {
    let max = UInt32(max)
    var last = arc4random_uniform(max)
    return {
        var r = arc4random_uniform(max)
        while r == last {
            r = arc4random_uniform(max)
        }
        last = r
        return Int(last)
    }
}

let r0 = random(8)
let r1 = random(4)
for i in 0..<20 {
    print(r0(), terminator: " ")
}
print("")
for i in 0..<20 {
    print(r1(), terminator: " ")
}

/*
 4 5 4 3 4 0 5 6 7 3 6 7 5 4 7 4 7 2 1 6
 0 3 0 1 0 2 3 1 2 0 1 0 1 0 1 3 0 3 0 2
 */

答案 2 :(得分:1)

使用颜色填充数组并使用Fisher-Yates shuffle对其进行随机播放。然后在末端使用元素,将其移除,并将其插入距离末端至少n个位置的随机位置。

例如,假设我的数组有10个元素。我洗了它,拿走了最后一个。我希望在再次看到它之前至少选择2个值,这样我就会在0...8范围内生成一个随机位置并将其插入那里。

var colorArray = [
  (UIColor.redColor()      , "red"   ),
  (UIColor.greenColor()    , "green" ),
  (UIColor.blueColor()     , "blue"  ),
  (UIColor.yellowColor()   , "yellow"),
  (UIColor.orangeColor()   , "orange"),
  (UIColor.lightGrayColor(), "grey"  )].shuffle() // shuffle() is from my link above

let spacing = 2 // Pick at least 2 colors before we see it again
if let randomColor = colorArray.popLast() {
  colorArray.insert(randomColor,
                    atIndex: Int(arc4random_uniform(UInt32(colorArray.count - spacing))))
}

答案 3 :(得分:0)

此处描述的一个案例:https://github.com/dimpiax/GenericSequenceType

另一个是功能性的:

func getRandomItem<T>(arr: [T]) -> (unique: Bool) -> T {
    var indexes: [Int]!

    return { value in
        let uniqIndex: Int

        if value {
            if indexes?.isEmpty != false {
                indexes = [Int](0.stride(to: arr.count, by: 1))
            }

            uniqIndex = indexes.removeAtIndex(Int(arc4random_uniform(UInt32(indexes.count))))
        }
        else {
            uniqIndex = Int(arc4random_uniform(UInt32(arr.count)))
        }
        return arr[uniqIndex]
    }
}

let generate = getRandomItem(colorArray)
generate(unique: true).0 // greenColor
generate(unique: true).0 // redColor
generate(unique: true).0 // lightGrayColor

答案 4 :(得分:0)

尝试一下,它对我有用,并且100%经过测试

let arrString = ["1","2","3","4","5","6"]
var selectedIndix = -1

@IBAction func btnClick(_ sender: Any) {

    let randomElementIndex = randomElementString()
}

单击按钮时调用此功能

func randomElementString() -> Int{

    let randomm = Int(arc4random_uniform(UInt32(arrString.count)))

    if selectedIndix == randomm{
        return randomElementString()
    }else{
        selectedIndix = randomm
        return randomm
    }
}

输出:-

5121242316513126

答案 5 :(得分:-2)

如何使用以下条件运行while循环:

while(self.source.backgroundColor == sourceColor) {
  // get a new random sourceColor
}

这将一直循环,直到选择了新的随机颜色。

修改

附加说明:重点是while循环。有一些方法可以防止无限循环,编码人员可以找到正确的解决方案。我不认为SO是一个编写其他代码的地方,而是提供建议。我的开始是一个开始。

但是,由于我的答案被给予了如此负面的评价,我会推动而不是朝着正确的方向轻推。

其他答案不必要地膨胀。和?我上面提到的那个提供了不太理想的时间复杂度。所以,这是我的新答案(在元代码中):

// array of all background colors
var arrayOfColors = [..]

// get a random index
var randomIndex = arc4random(size of arrayOfColors)

// select new background color
var newBGColor = arrayOfColors[randomIndex]

// old background color
var oldBGColor = self.source.backgroundColor

// remove new color from array (so that it's excluded from choices next time)
arrayOfColors.removeAtIndex(randomIndex)

// set the new color to the background
self.source.backgroundColor = newBGColor

// add current color back into the pool of potential colors
arrayOfColors.addObject(oldBGColor)