2016-03-27 17 views
2

Dieser Code wählt eine zufällige Farbe aus einer Reihe von voreingestellten Farben aus. Wie mache ich es so, dass die gleiche Farbe nicht mehr als einmal ausgewählt wird?Zufälliges Auswählen eines Elements aus einem Swift-Array ohne Wiederholung

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

Sie könnten eine Reihe von ausgewählten Farben speichern oder Indexe und wenn die Farbe oder der Index im Array überprüft wird, wenn die Schaltfläche berührt wird. – kye

+0

gibt es viele schöne und klare Lösungen. manchmal ist das Einfachste das Beste. Ich empfehle Ihnen Achis Lösung. Der Grund, warum diese Arbeit ist, dass arc4random_uniform Strom der Zahlen nicht nur zufällig, sondern auch gleichmäßig erzeugt. – user3441734

Antwort

6

Erstellen Sie ein Array von Indizes. Entfernen Sie einen der Indizes aus dem Array und verwenden Sie diesen, um eine Farbe zu holen.

Etwas wie folgt aus:

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

Der obige Code erstellt ein Array indexes. Die Funktion randomItem prüft, ob indexes leer ist. Wenn dies der Fall ist, füllt es sie mit Indexwerten von 0 bis colorArray.count - 1.

Es nimmt dann eine Zufallszahl im indexes Array entfernt der Wert bei diesem Index in dem Array indexes, und verwendet sie, um ein Objekt aus dem colorArray zu holen und zurückzusenden. (Es entfernt nicht Objekte aus den colorArray. Es indirection verwendet, und entfernt Objekte aus der indexesArray, die zunächst einen Indexwert in für jeden Eintrag enthält Ihr colorArray.

Der ein Fehler in der oben ist, dass, nachdem Sie holen das letzte Element von indexArray, füllen Sie es mit einem vollständigen Satz von Indizes, und es ist möglich, dass die nächste Farbe, die Sie aus dem neu bevölkerten Array erhalten wird die gleiche wie die letzte sein wird

Es ist möglich, extra hinzuzufügen Logik, um dies zu vermeiden

+0

Fehler, muss sein 'Rückkehr FarbeArray [anIndex] .0' – dimpiax

+0

Ich denke, du hast recht, es ist eine Reihe von Tupeln, nicht wahr? Ich habe mir die Datenstruktur der OP nicht genau angesehen. Ich schrieb meinen Code, um ein zufälliges, sich nicht wiederholendes Element aus einem Array von SOMETHING zu holen. Es könnte generalisiert werden zu einem Array von * alles * mit Generika. –

+0

In diesem Fall muss diese Funktion abstrakt sein. Und ich würde für die Lesbarkeit 'stride' verwenden, um für Array anstelle von Bereich zu füllen. – dimpiax

-1

Wie wäre es mit einer While-Schleife mit der Bedingung:

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

Dieser Vorgang wird fortgesetzt, bis eine neue zufällige Farbe ausgewählt wurde.

bearbeiten

Zusätzlicher Hinweis: Der Punkt war die while-Schleife. Es gibt Möglichkeiten, um vor einer Endlosschleife zu schützen, es liegt am Codierer, die richtige Lösung zu finden. Ich glaube nicht, dass SO ein Ort ist, an dem man anderen Code schreiben kann, sondern stattdessen Vorschläge machen kann. Meins ist ein Anfang.

Aber da meine Antwort so negativ bewertet wurde, werde ich statt in die richtige Richtung schieben.

Die anderen Antworten sind unnötig aufgebläht. Und? Die eine, die ich oben angeboten habe, bietet eine weniger als wünschenswerte Zeitkomplexität. So, hier ist meine neue Antwort (in Meta-Code):

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

Während diese Lösung funktionieren kann, hat sie die Möglichkeit, für immer zu schleifen, jedoch unwahrscheinlich. Es ist auch möglicherweise langsam, wenn Sie eine Reihe von schlechten Randoms bekommen. – ColGraff

+1

@KennethBruno mit arc4random_uniform, die Möglichkeit der Schleife für immer ist NULL, wenn die Anzahl der Farben> 2. Mehr Farben, weniger Potenzial, langsam zu sein ... In der Praxis ist diese Lösung sehr effizient und perfekt OP Bedürfnisse. Du kannst es gerne testen :-) – user3441734

+0

Aus Interesse habe ich die While-Schleife ausprobiert, weil ich anfangs dachte, das könnte so sein. Dies ist, wie ich es verwendet habe, aber es gibt eine EXC_BAD_ACCESS, wenn eine Farbe zweimal hochkommen will. So habe ich es versucht. –

2

Füllen Sie einen Array mit den Farben und mischt es mit einem Fisher-Yates shuffle. Verwenden Sie dann das Element an einem Ende, entfernen Sie es und fügen Sie es an einer zufälligen Position mindestens n Positionen vom Ende ein.

Sagen Sie zum Beispiel, mein Array hat 10 Elemente. Ich mische es und nehme das letzte. Ich möchte, dass mindestens 2 Werte gewählt werden, bevor ich sie wieder sehe, also erzeuge ich eine zufällige Position im Bereich 0...8 und lege sie dort ein.

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)))) 
} 
0

Ein Fall, hier beschrieben: https://github.com/dimpiax/GenericSequenceType

Ein weiterer Grund ist funktional:

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 
1

basiert auf der Tatsache, dass nicht nur zufällig arc4random_uniform erzeugen, sondern auch Zahlen gleichmäßig verteilt

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

einfacher Test für verschiedene k und Stream-Länge von an e milion

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

druckt

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 

finaly, hier ist mehr Swifty und funktionelle Ansatz basiert auf der gleichen Idee

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