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

这个代码从一组预先设定的颜色中选取一个随机的颜色。 我该如何做到这一点,所以相同的颜色不会被挑选超过一次?

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()]) 

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

像这样的东西:

 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; } 

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

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

上面的一个缺陷是,当你从indexArray中获取最后一个项目时,你用一整套索引来填充它,而且你可能会从最近重新填充的数组中得到的下一个颜色和你最后一个得到。

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

用颜色填充arrays,并用Fisher-Yates随机播放 。 然后使用该元素的一端,将其删除,并将其插入至lessn个位置的随机位置。

例如,说我的数组有10个元素。 我洗牌,拿走最后一个。 我想再次看到它之前至less要select2个值,所以我生成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)))) } 

基于这个事实,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(rr, 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 */ 

简单的testing不同的K值和一千万的stream量

 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和function的方法

 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 */ 

一种情况,在这里描述: https : //github.com/dimpiax/GenericSequenceType

另一个function是:

 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 

如何运行一个while循环的条件:

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

这将保持循环,直到select一个新的随机颜色。

编辑

附加说明:该点是while循环。 有办法可以避免无限循环,编码人员需要find正确的解决scheme。 我不认为这是写一个其他代码的地方,而是提供build议。我的是一个开始。

但是,由于我的回答给了这样一个负面的评价,我会推动,而不是向正确的方向轻推。

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

 // 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)