Hilfe bei Zufallszahlen

Diese Seite verwendet Cookies. Durch die Nutzung unserer Seite erklären Sie sich damit einverstanden, dass wir Cookies setzen. Weitere Informationen

  • Hilfe bei Zufallszahlen

    Hallo an alle hier im Forum,

    ich bin Neuling in der Swift Programmierung und bräuchte mal eure Hilfe. Ich mache schon einige Stunden rum, komme aber auf keinen grünen Zweig. Ich möchte wie in dem Code zu sehen ist per Zufall 4 Texte in einem Label ausgeben welche sich nicht wiederholen sollen. Wenn alle 4 Texte in zufälliger Reihenfolge ausgegeben wurden, wird der Zähler gelöscht und es soll von vorne losgehen. In meinem Code habe ich einen String Array "Nachricht" declariert von Text 1 - 4. Beim betätigen des Buttons wird die Funktion "Nachricht ausgeben" aktiviert. Der globale INT Array "Zähler" wird per append mit den generierten Zufallszahlen gefüttert. In einer repeat Schleife soll abgefragt werden ob die aktuelle Zufallszahl bereits vorhanden ist. Wenn ja wird innerhalb der Schleife eine neue Zufallszahl generiert, wenn sie nicht im Zähler vorkommt wird sie im Label angezeigt. Leider funktioniert es aber nicht. Das löschen des Zählers bei der vierten Ausgabe des Textes funktioniert, aber trotzdem ist ein Fehler drin weil die generierten Zufallszahlen doppelt auftauchen. Es wäre super wenn mir jemand helfen könnte.

    Liebe Grüße, Nicolai

    Quellcode

    1. import UIKit
    2. // Globale Variable für Zähler
    3. var zähler = [Int] ()
    4. class ViewController: UIViewController {
    5. override func viewDidLoad() {
    6. super.viewDidLoad()
    7. }
    8. @IBOutlet weak var label: UILabel!
    9. @IBAction func Button(_ sender: Any) {
    10. // Ausgabe der Nachricht bei klick auf den Button
    11. self.NachrichtAusgeben ()
    12. }
    13. // Funktion für die zufällige Ausgabe der Nachricht
    14. func NachrichtAusgeben () {
    15. var nachricht = ["Text 1"]
    16. nachricht.append ("Text 2")
    17. nachricht.append ("Text 3")
    18. nachricht.append ("Text 4")
    19. let anzahl = (nachricht.count) // Anzahl der Nachrichten
    20. var zufall = Int.random(in: 0...(anzahl-1)) // Zufallszahl ermitteln
    21. zähler.append (zufall) // dem Zähler die Zufallszahl hinzufügen
    22. let anzahlZähler = (zähler.count) // Anzahl der gespeicherten Zufälle im Zähler
    23. print (zähler)
    24. print (anzahlZähler)
    25. //for a in 0...(anzahlZähler-1) {
    26. var a = Int()
    27. repeat {
    28. if zähler[a] == zufall {
    29. zufall = Int.random(in: 0...(anzahl-1))
    30. } else {
    31. label.text = nachricht[zufall] }
    32. //print (zähler[a])
    33. a += 1
    34. if anzahlZähler == anzahl {
    35. zähler = []
    36. print ("Ende")
    37. }
    38. } while a <= (zähler.count)-1
    39. }
    40. }
    Alles anzeigen
  • Du willst in der Schleife prüfen, ob die neue Zufallszahl bereits im Vektor zähler vorhanden ist, vergleichst aber nur das jeweils letzte Element. Du müsstest den Vergleich in einer geschachtelten Schleife durchführen.

    Anderer Ansatz: Du variierst den Wertebereich der Zufallszahlen und entfernst immer den selektierten Text aus der Grundmenge:
    • Zufallszahl entsprechend der Anzahl Elemente in nachricht
    • Entsprechendes Element dem Label hinzufügen unf aus nachricht entfernen
    • Wiederholen, bis nur noch ein Element vorhanden, dieses anhängen und Ende
    So sparst Du Dir die Vergleiche auf Existenz ... erkaufst es Dir aber mit Vektoroperationen.

    Oder Du speicherst die Texte in einem temporären Array, durchläufst dieses und tauschst immer das i-te Element mit einem zufällig ausgewählten. Auch dann kann es keine doppelten Texte geben.

    Oder, oder, oder ... viele Wege führen nach Rom

    Mattes
    Diese Seite bleibt aus technischen Gründen unbedruckt.
  • Vielen Dank für die Antwort. Eine verschachtelte If Anweisung..... ok. Könntest du mir vielleicht grob auflisten wie ich „was“ an welchem Punkt abfragen soll. Muss kein fertiger Code sein, nur eine Punkt für Punkt Liste, ich steh immer noch auf dem Schlauch . Ich will erstmal bei der angefangen Variante bleiben. Der Array mit den gespeicherten Zufallszahlen möchte ich, wenn alles funktioniert, in UserDefaults speichern. Es wird nämlich nicht bei 4 Einträgen bleiben. Könnten letztendlich ca. 50 werden.
  • Ich kann Dir nachher mal den Pseudocode schreiben, aber bei vielen Einträgen würde ich einen der anderen Ansätze wählen: Sonst ist die Wahrscheinlichkeit der bereits benutzen Zahlen einfach zu hoch und Du brauchst zu viele Versuche. Das Laufzeitverhalten Deines Algorithmus ist nicht vorhersagbar und er könnte sogar endlos laufen...

    Mattes
    Diese Seite bleibt aus technischen Gründen unbedruckt.
  • MyMattes schrieb:

    Anderer Ansatz: Du variierst den Wertebereich der Zufallszahlen und entfernst immer den selektierten Text aus der Grundmenge:

    • Zufallszahl entsprechend der Anzahl Elemente in nachricht
    • Entsprechendes Element dem Label hinzufügen unf aus nachricht entfernen
    • Wiederholen, bis nur noch ein Element vorhanden, dieses anhängen und Ende

    da du viel mehr Ahnung hast wie ich, werde ich diesen Ansatz versuchen. Möchte ja nicht das sich meine App in einer Endlosschleife aufhängt . Es wäre aber wirklich super wenn du mir einen Pseudocode schreiben könntest. Wie ich das mit meinem aktuellen Wissen umsetzen soll...... keine Ahnung ?

    vielen Dank für Deine Hilfe

    Gruss Nicolai
  • NicolaiK schrieb:

    da du viel mehr Ahnung hast wie ich, werde ich diesen Ansatz versuchen. Möchte ja nicht das sich meine App in einer Endlosschleife aufhängt . Es wäre aber wirklich super wenn du mir einen Pseudocode schreiben könntest. Wie ich das mit meinem aktuellen Wissen umsetzen soll...... keine Ahnung ?
    Also der Ansatz mit dem Entfernen eines Elementes ist (wahrscheinlich) für den Rechner etwas aufwändiger als das Vertauschen, ich finde es aber schön anschaulich ... und man soll seinen Code ja auch noch nach ein paar Jahren verstehen. Das Prinzip wäre "Ich suche aus einer Menge an Elementen nacheinander zufällig ein Teil aus und lege es geordnet zur Seite":

    Quellcode

    1. zielArray = <leeres Array von Strings>
    2. DO WHILE <Elemente in ausgangsArray>
    3. zufall = <Zufallszahl zwischen 0 und <Elementanzahl ausgangsArray - 1>
    4. element(zufall) zu zielArray hinzufügen
    5. element(zufall) aus ausgangsArray entfernen
    6. WEND
    Für die Array-Funktionen gibt es Methoden von NSMutableArray.

    Der Ansatz mit dem Vertauschen ist sehr ähnlich, er nutzt nur kein (anfangs leeres) Ziel-Array. Er durchläuft auch alle Elemente und sucht sich aus dem jeweiligen Rest des Arrays (inkl. des aktuellen Elementes) ein zweites zufälligig aus und tauscht diese beiden Elemente aus.

    Mattes
    Diese Seite bleibt aus technischen Gründen unbedruckt.
  • NicolaiK schrieb:

    Ok. Grundsätzlich würde ich den Ansatz mit dem entfernen gerne versuchen. Aber wie realisiere ich dann das in UserDefaults die bereits benutzten Texte gespeichert werden ? Das ist wichtig, sollte die App geschlossen werden möchte ich realisieren das es da weiter geht wo die App zuletzt benutzt wurde.
    Hierzu könntest Du z. B. die beiden Arrays (ausgangsArray und zielArray) speichern. Damit wäre eindeutig klar, wie weit Dein Auswahlalgorithmus gekommen ist und welche Elemente noch zur Auswahl stehen ... schliesslich ist ausgangsArray noch nicht leer.

    Allerdings gibt es einen "Apple-Weg", das Thema "State Restoration" zu lösen, in welchem ein NSCoder-Objekt verwendet wird. Ich würde diesen statt der Eigenlösung mittels NSUserDefaults implementieren: Zum einen nutzt Du dann Standard-Verfahren, zum anderen brauchst Du Dich nicht um z. B. das Verwerfen dieser Defaults im Falle einer Inkompatibilität zu kümmern. Letzteres müsstest Du sonst z. B. mit einen Schalter in den App-Einstellungen (aka "Settings-Bundle") selber lösen. BTDT.

    Mattes
    Diese Seite bleibt aus technischen Gründen unbedruckt.
  • Moin Nikolai,

    wenn ich dich recht verstanden habe, hast du 4 Texte, welche du alle ausgeben möchtest, mit einer zufälligen Reihenfolge (ohne Zurücklegen)?

    1) Dann ist es das einfachste, du speicherst dir die Texte in ein Dictionary. Die Schlüssel speicherst Dir dazu in ein Array, z.b. eine laufende Zahl von 0 bis letzter Text.

    2) Im Anschluss mischt Du dir die Schlüssel, Schlüssel.shuffle()

    3) dann gehst du einfach über dein Schlüssel Array (for...) und gibst zu den Schüsseln, den passenden Text aus.

    4) willst einen neuen Versuch, mischt du dir einfach die Schlüssel neu und gibt es neu aus ;)

    Grüsse
    Wolf
  • Alles gut, ich bin ja im Prinzip auch vom alten Schlag. Ich hab vor 28 Jahren die Programmiersprache BASIC gelernt auf einem Commodore C128D. Hab mich aber dann leider nicht weiter entwickelt. Vor ca. 3 Monaten kam ich dann auf die „fixe“ Idee ich könnte ja mal wieder einsteigen. Hab mich dann etwas in Visual Basic versucht. Kleinere Projekte sind möglich. Hab aber gemerkt das ich ganz schön eingerostet bin. Es hat sich halt im Vergleich zum Ur BASIC einiges geändert. Wenn man früher mit dem Goto Befehl in eine bestimmte Zeile gesprungen ist, nutzt man heute Funktionen die dann aufgerufen werden. Boolsche Werte wie sie heute benutzt werden gab es früher auch nicht. Da wurde eine INT Variable auf den Wert 1 gesetzt, das war dann True und bei einer bestimmten Aktion wieder auf 0 für False. Naja, vielleicht steige ich ja irgendwann wenigstens so durch das es für meine Bedürfnisse reicht. Und bei Problemen ein bisschen Hilfestellung von hier, dann passt das. Vielen Dank nochmal. Bin schon gespannt es auszuprobieren wenn ich wieder etwas mehr Zeit habe.
  • Wolf schrieb:

    Moin Nikolai,

    wenn ich dich recht verstanden habe, hast du 4 Texte, welche du alle ausgeben möchtest, mit einer zufälligen Reihenfolge (ohne Zurücklegen)?

    1) Dann ist es das einfachste, du speicherst dir die Texte in ein Dictionary. Die Schlüssel speicherst Dir dazu in ein Array, z.b. eine laufende Zahl von 0 bis letzter Text.

    2) Im Anschluss mischt Du dir die Schlüssel, Schlüssel.shuffle()

    3) dann gehst du einfach über dein Schlüssel Array (for...) und gibst zu den Schüsseln, den passenden Text aus.

    4) willst einen neuen Versuch, mischt du dir einfach die Schlüssel neu und gibt es neu aus ;)

    Grüsse
    Wolf
    ..... aber da bekomme ich ja auch doppelte Werte zurück oder ?
  • NicolaiK schrieb:


    ..... aber da bekomme ich ja auch doppelte Werte zurück oder ?
    Nein, weil keine doppelten Werte in der Liste vorhanden sind.

    Es gibt halt 2 grosse Wege das Problem zu lösen,

    • beim prozessieren der Daten (was Ihr als altmodisch bezeichnet)
    • über eine separate Liste


    da gibt es dann auch zwei Möglichkeiten, a) die Systemfunktion... oder b) man bastelt es sich halt schnell selbst zusammen. Braucht man beispielsweise, wenn man Spezialthemen prozessieren möchte, wie mit oder ohne zurücklegen. Beim einen geht man einfach über die Liste, holt sich der Werte willkürlich raus und stellt sie in eine Liste, beim anderen nimmt man zum Beispiel ein Set (was doppelte Einträge gleich unterdrückt). Dann kann man auch noch Indexe nehmen, wie häufig ein bestimmter Wert in einer Liste auftauchen darf, muss man dann halt mit verwalten.

    Schöne Grüsse
    Wolf
  • Hallo Wolf und Mattes,

    ich danke euch beiden für eure tolle Hilfe. Ich habe mich für die shuffle() Variante von Wolf entschieden. Mit der NSMutableArray Variante komme ich nicht so ganz zurecht. Vielleicht übersteigt das noch etwas mein Horizont. Es lassen sich auch nicht wirklich brauchbare Code Beispiele finden um das zu verstehen (zumindest habe ich keine gefunden). Das speichern über UserDefaults habe ich auch bereits eingebaut. Mal sehen ob es funktioniert mit ca. 50 Textinhalten. Hier mein fertiger Code, bin stolz, auch wenn ich es nicht alleine geschafft habe :)

    Guten Rutsch und ein frohes neues 2020 wünsche ich euch :thumbsup:

    Gruß Nicolai

    Quellcode

    1. import UIKit
    2. // Globale Variablen
    3. var i = 0
    4. var nachricht = ["Text 1", "Text 2", "Text 3", "Text 4"]
    5. class ViewController: UIViewController {
    6. override func viewDidLoad() {
    7. super.viewDidLoad()
    8. // prüfen ob userDefaults vorhanden ist
    9. let userdefaults = UserDefaults.standard
    10. if userdefaults.string(forKey: "Saved-zähler") != nil{
    11. print("userDefaults ist vorhanden")
    12. self.load()
    13. } else {
    14. print("Keine Einträge in userDefaults")
    15. i = 0
    16. // umsortieren der Nachrichten wenn userDefaults leer ist
    17. nachricht.shuffle()
    18. print (i)
    19. print (nachricht)
    20. }
    21. }
    22. @IBOutlet weak var label: UILabel!
    23. @IBAction func Button(_ sender: Any) {
    24. // Ausgabe der Nachricht bei klick auf den Button
    25. self.NachrichtAusgeben ()
    26. }
    27. // Funktion für die zufällige Ausgabe der Nachricht
    28. func NachrichtAusgeben () {
    29. i += 1
    30. if i == (nachricht.count) {
    31. // wenn Zähler gleich Anzahl Text neu sortieren und Zähler auf 0 setzen !!
    32. i = 0
    33. nachricht.shuffle()
    34. }
    35. label.text = (nachricht [i])
    36. print (nachricht[i])
    37. self.save()
    38. }
    39. func save() {
    40. let defaults = UserDefaults.standard
    41. defaults.set(nachricht, forKey: "Saved-nachrichten")
    42. defaults.set(i, forKey: "Saved-zähler")
    43. print ("gespeichert")
    44. }
    45. func load () {
    46. let defaults = UserDefaults.standard
    47. let myarray = defaults.stringArray(forKey: "Saved-nachrichten") ?? [String]()
    48. let array = defaults.integer(forKey: "Saved-zähler")
    49. nachricht = myarray
    50. i = array
    51. print ("geladen")
    52. print (i)
    53. }
    54. }
    Alles anzeigen

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von NicolaiK ()

  • Hallo Nicolai,

    :)

    wenn Du das schon fertig hast, kannst ja einen Schritt weiter gehen und die Datenspeicherung statt über die UserDefaults (UD) über JSON Objekte vornehmen. Seit Swift 4.0 ist das fast so einfach wie über die UD, nur dass du da noch ein Verzeichnis angeben musst, in welches die Daten geschrieben werden.

    Als weitere Aufgabe könntest auch noch vom UIKit auf SwiftUI wechseln... :)

    Schöne Grüsse
    Wolf
  • Wolf schrieb:

    wenn Du das schon fertig hast, kannst ja einen Schritt weiter gehen und die Datenspeicherung statt über die UserDefaults (UD) über JSON Objekte vornehmen.
    Das sehe ich anders:
    1. UserDefaults haben implizite Funktionen, die man "im Alleingang" nachprogrammieren müsste. So lassen sich z. B. innerhalb der Applikation Standardwerte registrieren, falls entsprechende Schlüssel in den UserDefaults nicht enthalten sind. Ausserdem werden Schlüssel mit diesen Standardwerten automatisch nicht gespeichert bzw. entfernt. Das ganze Housekeeping macht also das Framework für Dich.
    2. In @NicolaiKs Anwendungsfalls ("App State Restoration") würde ich - wie oben geschrieben - sogar von der Nutzung der UserDefaults abraten, sondern in allen Objekten das NSCoding-Protokoll implementieren und über die AppDelegate-Methoden eine Restoration unterstützen. Dann erhält man quasi nebenbei auch eine saubere Wiederherstellung von View-Controllern, Navigation-Stack usw.
    Nichts gegen JSONs wo sie Sinn machen und ich habe vollstes Verständnis, wenn man als Anfänger die genannten Themen erst nach und nach adressiert. Aber als Empfehlung für den "nächsten Schritt" halte ich dieses Vorgehen für ungünstig...

    Mattes
    Diese Seite bleibt aus technischen Gründen unbedruckt.

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von MyMattes ()

  • Ganz grob beschrieben:
    1. Deine App speichert beim Wechsel in den Hintergrund alle Informationen, die sie zum Herstellen des aktuellen Standes benötigt: ViewController machen dies "automatisch", wenn man ihnen - z. B. im Storyboard - einen Restoration-Identifier gibt. So merkt sich ein UITableViewController die Scrollposition, ein UIScrollViewController mit den Zoomlevel und sichtbaren Ausschnitt, ein UINavigationViewController den Navigation-Stack und natürlich viel mehr. Deine individuellen Informationen kannst Du in entsprechenden Methoden der ViewController speichern. Hierzu bekommst Du ein Encoder-Objekt zur Verfügung gestellt, in das andere Objekte gespeichert werden können. Bei eigenen Klassen am besten, indem Du bestimmte Methoden, definiert im NSCoding-Protokoll, implementierst.
    2. Das Laden funktioniert analog: Die ViewController bzw. alle beteiligten Objekte (auch Deine eigenen Klassen) bekommen ein Decoder-Objekt, aus denen die oben gespeicherten Infos extrahiert werden. Aus diesen stellst Du z. B. beim Instanzieren oder beim Laden / Anzeigen von Views wieder den letzten Status her.
    3. Um die Persistierung des Status kümmert sich das Framework.
    4. Crasht Deine App bei der State Restoration (warum auch immer) wird der Status nach dem dritten (?) Versuch verworfen, die App startet dann "normal"
    5. Es ist eine gute Idee, in den "Opt-In"-Methoden des AppDelegates die App-Version des gespeicherten Status zu prüfen und eine Wiederherstellung nur bei Gleichheit mit der aktuellen App-Version durchzuführen. Dies verhindert potentielle Probleme mit Inkompatibilitäten.
    Bei Apple findest Du einige Informationen dazu.

    Mattes
    Diese Seite bleibt aus technischen Gründen unbedruckt.