Simon Baumgartner: Zufallsauswahl ohne Wiederholung

Ich habe ein Array bestehend aus fünf Elementen. Nachdem ich aus dem Array zufällig ein Element ausgewählt habe, sollte dieses Element nicht mehr zur Auswahl stehen (Auswahl ohne Zurücklegen), sondern nur noch die verbleibenden vier, usw. Dies wird solange weitergeführt bis die Liste leer ist. Was ich bisher habe, ist die einfache Zufallsauswahl mit Zurücklegen. Wie muss ich mein Script abändern?

Gruss, Simon Baumgartner (Zürich)

<script language="javascript" type="text/javascript">
 // Fotoliste
 var foto = new Array();
 foto[0]="foto1.html"
 foto[1]="foto2.html"
 foto[2]="foto3.html"
 foto[3]="foto4.html"
 foto[4]="foto5.html"
 // Zufallszahl
 function randomNumber(n){
 number= Math.floor(Math.random() * (n+1));
 return number;
 }

window.location=foto[randomNumber(foto.length -1)];

</script>

  1. Sup!

    Naja, eigentlich ist das nicht schwer.
    Du legst einen weiteren Array an, der die Größe der Zahl der wählbaren Elemente hat, in dem Du die schon ausgewählten Elemente markieren kannst. MarkierungsArray[x] ist also 0 für ungewählte und 1 für schon gewählte Elemente aus dem eigentlichen Array BildArray[x]. Gleichzeitig zählst Du bei jedem Auswählen eines Elementes einen Zähler hoch. Wenn der Zähler die Zahl der Elemente überschreitet, wurden alle schon mal ausgewählt, Du musst dann die Liste der Markierungen löschen und dann normal weiterverfahren. Wenn der Zähler kleiner oder gleich ist, generierst Du eine Zufallszahl. Wenn das Markierungs-Array anzeigt, daß das Element schon mal gewählt wurde, suchst Du das nächste noch nicht gewählte Element im Markierungs-Array und nimmst das. Wenn das Markierungs-Array anzeigt, daß das Element noch nicht gewählt wurde, setzt Du die Markierung und nimmst das Element.

    Gruesse,

    Bio

    --
    Ich bin ein Mobber - mein Posting tut mir leid! EHRLICH!!!
  2. Hallo Simon,

    Dies wird solange weitergeführt bis die Liste leer ist. Was ich bisher habe, ist die einfache Zufallsauswahl mit Zurücklegen. Wie muss ich mein Script abändern?

    Ich kann dir zwar hier keinen gültigen Javascript-Code schreiben, weil ich es einfach nicht so gut kann, aber grundsätzlich könntest du so ansetzen, dass du einfach mit jedem mal, wo du ein Foto anzeigst, den Inhalt des entsprechenden Arrayelements löschst, und eben bevor du ein Foto anzeigst, überprüfst, ob überhaupt Inhalt im anzuzeigenden Element ist. Wenn ja, dann kann das Bild angezeigt werden, wenn nicht, dann generierst du die nächste Zufallszahl und probierst solange weiter, bis das nächste Element mit Inhalt auftaucht.

    In anderen Programmiersprachen gibt es wohl diese Funktion, dass keine Zufallszahlen mehrfach auftauchen, wie das in Javascript ist, weiß ich nicht. Ich würde es so wie oben beschrieben versuchen.

    Grüße aus Darmstadt,
    Benjamin

  3. Hallo Simon,

    ich verstehe zwar nicht ganz, wo genau dein Problem liegt (programmier halt einfach das, was du erreichen willst), aber mir ist vor kurzem eine IMHO sehr elegante Lösung zu diesem "Mischvorgang" begegnet. Die ist nämlich auch bei größeren Arrays sehr performant (z.B. beim Mischen von Karten):

    Du hast ein Array ("erstesArray"), in dem die ganzen Einträge stehen, die gemischt werden sollen. In einem zweiten Array ("zweitesArray") hast du nur Referenzen auf diese Einträge, nämlich die Indizis der zu mischenden Einträge. Dieses Array ist genauso groß wie das erste und enthält am Anfang einfach die Zahlen von 0 bis n-1. Es reicht, wenn du nur das zweite Array mischt, weil jedem Eintrag im zweiten Array eindeutig ein Eintrag im ersten Array zugeordnet ist und umgekehrt. Dann machst du folgendes:

    for (i=0; i<zweitesArray.length-1; i++) {
      zufallszahl = Zufallsgenerator(zweitesArray.length-i) + i;
      zwischenspeicher = zweitesArray[zufallszahl];
      zweitesArray[zufallszahl] = zweitesArray[i];
      zweitesArray[i] = zwischenspeicher;
    }

    Dann hast du im Array "zweitesArray" die Zahlen von 0 bis n-1 in gemischter Reihenfolge. Auf die eigentlichen Einträge kannst du zugreifen mit erstesArray[zweitesArray[index]].

    Viel Erfolg,
    Robert

  4. hi,

    sowas?

    hth + Gruesse  Joachim

    <html>
    <head>
    <script language="javascript1.2">
    var str     =  new Array("lala","blub","foo","bar","ops","fup");
    var tempArr =  str;
    function randomId (max) {
     return Math.floor(Math.random () * max);
    }
    function killEl(arr, id) {
     var temp = new Array();
     for(var i in arr) {
      if(i != id) temp[temp.length] = arr[i];
     }
     return temp;
    }
    function go() {
     if (tempArr.length > 0) {
      var id =  randomId(tempArr.length);
      document.feld.dpl.value =  tempArr[id];
      tempArr =  killEl(tempArr,id);
     } else {
      tempArr =  str;
      document.feld.dpl.value =  "fertig!";
     }
    }
    </script>
    </head>
    <body>
    <form Name="feld">
        <input type="text" name="dpl" size="30">
        <input type="button" value="go" onclick="go()">
    </form>
    </body>
    </html>

  5. Ich habe ein Array bestehend aus fünf Elementen. Nachdem ich aus dem Array zufällig ein Element ausgewählt habe, sollte dieses Element nicht mehr zur Auswahl stehen (Auswahl ohne Zurücklegen), sondern nur noch die verbleibenden vier, usw. Dies wird solange weitergeführt bis die Liste leer ist. Was ich bisher habe, ist die einfache Zufallsauswahl mit Zurücklegen. Wie muss ich mein Script abändern?

    Eine weitere Moeglichkeit ist, das entsprechende Element einfach zu loeschen, nachdem Du es nicht mehr brauchst.

    delete(foto[number]);

    Natuerlich ist das Array dann am Ende leer. Falls Du es mehrmals brauchst, muesstest Du also vorher eine Kopie anlegen und auf der arbeiten.

    function randomNumber(n){
    number= Math.floor(Math.random() * (n+1));
    return number;
    }

    window.location=foto[randomNumber(foto.length -1)];

    Warum hier unten minus 1 und oben wieder plus 1?

    So long

    --
    Bier trinken fetzt!!!
    1. Moin!

      Eine weitere Moeglichkeit ist, das entsprechende Element einfach zu loeschen, nachdem Du es nicht mehr brauchst.

      delete(foto[number]);

      Wenn du noch verraten würdest, was das für eine Funktion oder Methode sein soll, wäre ich zufrieden. Ich hab' sie jedenfalls in SelfHTML nicht unter Array oder objektunabhängigen Methoden gefunden. Entstammt sie gar deiner Phantasie?

      - Sven Rautenberg

      --
      "Bei einer Geschichte gibt es immer vier Seiten: Deine Seite, ihre Seite, die Wahrheit und das, was wirklich passiert ist." (Rousseau)
      1. Hi!

        Eine weitere Moeglichkeit ist, das entsprechende Element einfach zu loeschen, nachdem Du es nicht mehr brauchst.

        delete(foto[number]);

        Wenn du noch verraten würdest, was das für eine Funktion oder Methode sein soll, wäre ich zufrieden. Ich hab' sie jedenfalls in SelfHTML nicht unter Array oder objektunabhängigen Methoden gefunden. Entstammt sie gar deiner Phantasie?

        Ich hatte sogar extra nochmal nachgesehen, ob es den Operator (nicht Methode) nicht vielleicht wirklich nur in meiner getruebten Erinnerung gibt. Aber nicht in Selfhtml, sondern in der ECMA-262.3-Spec und in der alten JavaScript-1.3-Reference von Netscape. In ersterer findest Du delete im Kapitel 11.4.1, in der letzteren unter http://docserv.calocybe.dyndns.org/specs/NetscapeCommunications/JavaScript13ClientReference/ops.htm#1045837.

        So long

        --
        Bier trinken fetzt!!!
        1. Moin!

          Ich hatte sogar extra nochmal nachgesehen, ob es den Operator (nicht Methode) nicht vielleicht wirklich nur in meiner getruebten Erinnerung gibt. Aber nicht in Selfhtml, sondern in der ECMA-262.3-Spec und in der alten JavaScript-1.3-Reference von Netscape. In ersterer findest Du delete im Kapitel 11.4.1, in der letzteren unter http://docserv.calocybe.dyndns.org/specs/NetscapeCommunications/JavaScript13ClientReference/ops.htm#1045837.

          Ok, das gibts tatsächlich - aber es bringt leider nicht sonderlich viel. Das Zuweisen von einem Leerstring, undefined oder NULL würde gleiche Ergebnisse, zumindest aber die gleichen Handlungsoptionen bieten.

          Was interessant gewesen wäre, wäre ein "Löschen mit Aufrücken" der verbleibenden Elemente, so dass das Array immer kleiner wird. Dann kann man array.length abfragen, damit den Wertebereich der Zufallszahl beeinflussen, und kriegt garantiert ein Ergebnis raus. Damit ist sichergestellt, dass nach array.length Durchgängen alle Elemente des Arrays ausgewählt und verwurschtelt worden sind - eben echtes "Ziehen ohne Zurücklegen".

          Die Methode, die Wahl eines Elements zu speichern und im Wiederholungsfall einfach das nachfolgende Element zu nehmen, halte ich hingegen für nicht korrekt. Die Wahrscheinlichkeiten, dass dieses nachfolgende Element ausgewählt wird, verdoppeln sich schlicht bei solch einer Vorgehensweise.

          Und einfach nochmal eine neue Zufallszahl zu ziehen dürfte für das letzte Element ebenfalls ziemlich zeitaufwendig werden, wenn es z.B. das erste Element ist, einfach keine zufällige 1 entstehen will, und keine nachfolgenden Elemente mehr vorkommen, auf die man ausweichen kann.

          - Sven Rautenberg

          --
          "Bei einer Geschichte gibt es immer vier Seiten: Deine Seite, ihre Seite, die Wahrheit und das, was wirklich passiert ist." (Rousseau)
          1. Re!

            Was interessant gewesen wäre, wäre ein "Löschen mit Aufrücken" der verbleibenden Elemente, so dass das Array immer kleiner wird. Dann kann man array.length abfragen, damit den Wertebereich der Zufallszahl beeinflussen, und kriegt garantiert ein Ergebnis raus. Damit ist sichergestellt, dass nach array.length Durchgängen alle Elemente des Arrays ausgewählt und verwurschtelt worden sind - eben echtes "Ziehen ohne Zurücklegen".

            Genau darauf war ich eigentlich aus. Ich hab das delete wohl missverstanden. Jetzt merke ich erst, dass wenn in dem Netscape-Beispiel trees[3] nicht mehr existiert, die nachfolgenden Elemente eben *nicht* nachgerueckt sind. Somit ist eine Luecke im Array. An die Moeglichkeit hab ich gar nicht gedacht, weil das meiner Vorstellung eines Arrays voellig widerspricht. Nun ja, es gibt aber zum Glueck noch die Methode Array.splice(), die in Kapitel 15.4.4.12 von ECMA-262-3 und bei http://docserv.calocybe.dyndns.org/specs/NetscapeCommunications/JavaScript13ClientReference/array.htm#1193766 beschrieben ist. Mit

            foto.splice(number, 1);

            sollte das Ziehen ohne Zuruecklegen dann doch zu bewerkstelligen sein. Dummerweise gibt es bei pop(), push(), slice(), splice() und wahrscheinlich anderen immer wieder Probleme mit dem IE. Der hat es zumindest bis Version 5 nicht geschafft, diese zu implementieren. Ob es der 6er kann, weiss ich nicht. Aber auch dafuer gibt es Abhilfe - siehe http://forum.de.selfhtml.org/archiv/2001/5/24275/#m126404.

            Die Methode, die Wahl eines Elements zu speichern und im Wiederholungsfall einfach das nachfolgende Element zu nehmen, halte ich hingegen für nicht korrekt. Die Wahrscheinlichkeiten, dass dieses nachfolgende Element ausgewählt wird, verdoppeln sich schlicht bei solch einer Vorgehensweise.

            Yoh, aber ich glaube, es reicht in diesem, wenn es nur einigermaszen zufaellig *aussieht*.

            Und einfach nochmal eine neue Zufallszahl zu ziehen dürfte für das letzte Element ebenfalls ziemlich zeitaufwendig werden, wenn es z.B. das erste Element ist, einfach keine zufällige 1 entstehen will, und keine nachfolgenden Elemente mehr vorkommen, auf die man ausweichen kann.

            Richtig, das hat mich auch gestoert.

            So long

            --
            Falscher oder fehlender Kaffee. Benutzer angehalten.