kostenloser Webspace werbefrei: lima-city


rand() mit Ausnahmen von Zahlen

lima-cityForumProgrammiersprachenPHP, MySQL & .htaccess

  1. Wieso sollte das OT sein? Wir haben doch nur zwei Funktionen auf deinen Bedarf zugeschneidert und um das zu optimieren, einen Benchmark gemacht. :D
  2. Diskutiere mit und stelle Fragen: Jetzt kostenlos anmelden!

    lima-city: Gratis werbefreier Webspace für deine eigene Homepage


  3. Wieso sollte das OT sein? Wir haben doch nur zwei Funktionen auf deinen Bedarf zugeschneidert und um das zu optimieren, einen Benchmark gemacht. :D


    Genau! Und das ist sogar sehr wichtig, wenn man perfekt sein will :biggrin::P.
    Ich bin sicher, dass das die Programmierer der php-Sprache auch machen :thumb:.

    mfg,
    hr
  4. Autor dieses Themas

    sneppa

    Kostenloser Webspace von sneppa

    sneppa hat kostenlosen Webspace.



    Wieso sollte das OT sein? Wir haben doch nur zwei Funktionen auf deinen Bedarf zugeschneidert und um das zu optimieren, einen Benchmark gemacht. :D


    Genau! Und das ist sogar sehr wichtig, wenn man perfekt sein will :biggrin::P.
    Ich bin sicher, dass das die Programmierer der php-Sprache auch machen :thumb:.

    mfg,
    hr

    ich hab dir ne PN geschrieben, weil ich wissen wollte welche Variable die mit den nicht-werten sind und wie ich das Ergebnis ausgebe
  5. n****x


    Hi,

    also, ich habe die Geschindigkeiten der beiden Funktionen gemessen.
    Das Resultat:
    rand_n(): 0.0002411009
    mt_rand_n(): 0.0001652344

    Interpretiertes Resultat:
    Heavyraptor gewinnt!! :biggrin:


    Mooooment -- nicht so voreilig. Die folgende Funktion ist im Durchschnitt 2 Mal so schnell:

    function foxy_rand_exclusive(
      $min,
      $max,
      $ex   // ARRAY
    ) {
      $ex = array_flip($ex);
      
      //* sanity check
      if( count($ex) > $max - $min + 2) {
        for($i = $min; $i <= $max + 1; $i++) if(!isset($ex[$i])) break;
        if($i > $max) return(foxy()->error_return(E_WARNING, 'Bist du blöd oder was!?'));
      }
      //*/
      
      do {
        $rnd = mt_rand($min, $max);
      } while(isset($ex[$rnd]));
      return($rnd);
    }


    Wenn man garantieren kann, dass das Array mit den auszuschließenden Elementen nicht alle Zahlen im Wertebereich von mt_rand()
    enthält, kann man die Überprüfung inklusive Fehlermeldung nach "// sanity check" auch weglassen. Dann wird es noch ein wenig schneller.

    Übrigens produziert mt_rand_n() in dem speziellen Fall einen "Undefined index"-Fehler ...

    "Gebenchmarkt" hab ich das so:

    $repeats = 200;
    $min = 0;
    $max = 10;
    $ex = array(0, 1, 2, 3, 4, 5, 6, 7, 8, 9); 
    
    
    $fre = foxy()->calculate_runtime();
    for($i = 0; $i < $repeats; $i++) {
      foxy_rand_exclusive($min, $max, $ex);
      foxy_rand_exclusive($min, $max, $ex);
      foxy_rand_exclusive($min, $max, $ex);
      foxy_rand_exclusive($min, $max, $ex);
      foxy_rand_exclusive($min, $max, $ex);
    }
    $fre = foxy()->calculate_runtime($fre);
    
    $rn = foxy()->calculate_runtime();
    for($i = 0; $i < $repeats; $i++) {
      rand_n($min, $max, $ex);
      rand_n($min, $max, $ex);
      rand_n($min, $max, $ex);
      rand_n($min, $max, $ex);
      rand_n($min, $max, $ex);
    }
    $rn = foxy()->calculate_runtime($rn);
    
    $mrn = foxy()->calculate_runtime();
    for($i = 0; $i < $repeats; $i++) {
      mt_rand_n($min, $max, $ex);
      mt_rand_n($min, $max, $ex);
      mt_rand_n($min, $max, $ex);
      mt_rand_n($min, $max, $ex);
      mt_rand_n($min, $max, $ex);
    }
    $mrn = foxy()->calculate_runtime($mrn);
    
    printf(
      "<pre>\n%s foxy_rnd_ex\n%s rand_n\n%s mt_rand_n</pre>",
      $fre,
      $rn,
      $mrn
    );


    foxy()->calculate_runtime() ist dabei eine Funktion, die ohne Parameter aufgerufen, den aktuellen Timestamp als Gleitkommazahl (Sekunden.Mikrosekunden) zurückgibt -- ähnlich micro_time(). Wird sie mit Parameter aufgerufen, zieht sie vom aktuellen Timestamp den Parameter ab. Sie gibt also beim zweiten Mal die verbratene Zeit zurück.

    foxy()->error_return() kann man durch trigger_error() ersetzen.

    Mit den angegebenen Einstellungen wird jede Funktion 1000 Mal aufgerufen. Das gibt wesentlich genauere Ergebnisse als ein einmaliger Aufruf. Trotzdem können die Zahlen schwanken -- vor allem bei meiner Funktion, da sie im Extremfall alle Einträge im Array $ex abklappern müsste -- tut sie aber selten.

    Eines meiner Ergebnisse:
    0.035632858276367 foxy_rnd_ex
    0.17526698112488 rand_n
    0.074187040328979 mt_rand_n

    Lustig wirds mit anderen Einstellungen, da verringert sich der Abstand von rand_n() zu mt_rand_n(). Und foxy_rnd_ex() ist 3,5 Mal so schnell:
    $repeats = 200;
    $min = 0;
    $max = 100;
    $ex = array(0, 1, 2, 3, 4, 5, 6, 7, 8, 9);

    0.025051095962524 foxy_rnd_ex
    0.83115720748901 rand_n
    0.83430600166321 mt_rand_n
  6. Respekt!
    Aber je mehr Zahlen man ausschließen will, desto langsamer wird die Funktion wahrscheinlich und sie ist ja auch nich wirklich gleichschnell. Vor allem bei kleinen Bereichen kann es ja mal passieren, dass mehrmals der gleiche Wert rauskommt, der aber ausgeschlossen werden soll.
    Trotzdem mein größten Respekt, um mich mal zu wiederholen. ;)
  7. n****x

    Ehrlich gesagt, hatte ich nicht erwartet, dass dieser eigentlich ziemlich stumpfsinnige Ansatz (also meine Version) schneller sein würde. Das es mehrfache Durchläufe der Schleife geben würde, war mir schon bewusst. Aber anscheinend ist das gar nicht so schlimm. Ich hab das mal mit $min = 0, $max = 10 und $ex = array(0, 1, 2, 3, 5, 5, 6, 7, 8, 9); durchgespielt. Trotzdem war sie meistens immer noch schneller als eure schnellste Variante. Wenn du Langeweile hast, kannst du ja mal mit anderen Parametern auf Schwachpunkte testen. Allerdings scheinen die Laufzeiten eurer Funktionen auch starkt von der Parameter-Wahl abzuhängen ...

    Prinzipiell gäbe es da auch noch eine andere Variante:

    //
    function foxy_rand_ex_2(
      $min,
      $max,
      $ex
    ) {
      $ex = array_flip($ex);
      $r = array();
      for($i = $min; $i <= $max; $i++) {
        if(isset($ex[$i])) continue;
        $r[$i] = TRUE;
      }
      return(array_rand($r));
    }

    Ist "ein wenig" speicherintensiver, aber müsste auch ziemlich schnell sein ...
  8. r*****r

    schade, meine funktion ist nur bei mehreren durchgängen schneller als deine FuX.


    aber naja, das liegt im zehntausendstel bereich, was meine bei einem durchgang langsamer ist :biggrin:

    hier meine Lösung, jetzt haste genug auswahl ;)

    function rand_d($min, $max, $d=0) {
    	if(is_array($d)) {
    		$return = rand($min,$max);
    		while(in_array($return, $d)) {
    			$return = rand($min,$max);
    		}
    	}else{
    		$return = rand($min,$max);
    	}
    	return $return;
    }


    Beitrag geaendert: 14.12.2006 19:20:45 von ruehrer
  9. n****x

    Was ich schon immer mal wissen wollte:

    //
    while(in_array($return, $d))


    Wird das langsamer, wenn man bei in_array() noch den STRICT-Parameter hinzunimmt?
    Wenn ja: Wie viel langsamer?
    Oder kann man das kaum messen?
  10. r*****r


    Was ich schon immer mal wissen wollte:

    //
    while(in_array($return, $d))


    Wird das langsamer, wenn man bei in_array() noch den STRICT-Parameter hinzunimmt?
    Wenn ja: Wie viel langsamer?
    Oder kann man das kaum messen?


    minimal, ohne liegt die durchschnittszeit bei 0.000021 bis 0.000022
    und wenn mit ist, bei 0.000023 bis 0.000025
  11. Hi,

    der Vorschlag mit der while-Methode ist ja schonmal aufgekommen. Es war mir berreits klar, dass dies wohl die einfachste und praktischste Methode ist, jedoch tritt ein Grosses Problem auf, wenn man z.B. aus 10000 verschiedenen ZAhlen nur 3 Wählen will. Eventuell könnte es mehrere Sekunden dauern, bis ein gültiger Schlüssel gezogen wird. Deswgeen ist meine und Spackes Version "sicherer".
    Trotzdem gefällt mir die While-Methode fast besser als meine :biggrin:. Kompliment meinerseits.

    mfg,
    hr
  12. Hi,

    haha, ich hab noch eine schnellere Version :biggrin:.
    foxy_rand_exclusive(): 0.0000144005
    mt_rand_exclusive(): 0.0000141025

    Die Funktion:
    function mt_rand_exclusive($min,$max,$ex) {
      $ex = (array) $ex;
    
      while (true) {
        if (!in_array($rand = mt_rand($min,$max),$ex))
    	 return $rand;
      }
    }


    Zwar ist manchmal foxy_rand_exclusive() schneller, aber in den MEisten Fällen ist mt_rand_exclusive() schneller.
    Es ist ja eigentlich das gleiche prinzig wie zuvor, nur noch ein bischen schneller :biggrin:. Und danke, dass das Wort "exclusive" erwähnt wurde :P.

    mfg,
    hr
  13. n****x

    Könnte das dann noch schneller sein?

    (Bezieht sich auf die Verwendung von array_flip() und isset() statt
    in_array(), nicht auf mt_rand() statt rand(), was ja auch schneller
    sein soll ...)

    //
    function rand_d_mod($min, $max, $d=0) {
        if(is_array($d)) {
            $d = array_flip($d);
            $return = mt_rand($min,$max);
            while(isset($d[$return])) $return = mt_rand($min,$max);
        }
        else{
            $return = rand($min,$max);
        }
        return $return;
    }

  14. Könnte das dann noch schneller sein?

    (Bezieht sich auf die Verwendung von array_flip() und isset() statt
    in_array(), nicht auf mt_rand() statt rand(), was ja auch schneller
    sein soll ...)

    //
    function rand_d_mod($min, $max, $d=0) {
        if(is_array($d)) {
            $d = array_flip($d);
            $return = mt_rand($min,$max);
            while(isset($d[$return])) $return = mt_rand($min,$max);
        }
        else{
            $return = rand($min,$max);
        }
        return $return;
    }



    Hi,

    :eek: Ja, deine Funktion (rand_d_mod) ist noch ein Stück schneller :biggrin:. Du scheinst wohl der Gewinner zu sein - aber mal sehen, vielleicht finde ich noch eine schnellere Methode! :biggrin:

    mfg,
    hr

    Beitrag geaendert: 14.12.2006 20:12:59 von heavyraptor
  15. Ich habe jetzt mal alle Funktionen getestet mit dem Benchmarkscript von hr. Folgendes Ergebnis kommt in dem Verhältnis fast immer zu Stande:
    //?>
    -----------------------------------
    mt_rand_n():           0.0001310258
    rand_n():              0.0002052891
    spacke_rand_ex():      0.0001269137
    foxy_rand_exclusive(): 0.0000124318
    foxy_rand_ex_2():      0.0000283290
    rand_d():              0.0000116017
    mt_rand_exclusive():   0.0000126461
    rand_d_mod():          0.0000125288
    -----------------------------------


    Edit: foxy_rand_exclusive() ohne Abfragen natürlich.

    Beitrag geaendert: 14.12.2006 21:03:21 von i-spacke
  16. r*****r

    also, bei mir hat diese funktion die niedrigsten werte, nachdme ich grad mal eben eine neue geschrieben habe ;):

    /**/
    function rand_d_2($min, $max, $ex) {
    	$ex = array_flip($ex);
    	do{ $r = mt_rand($min, $max); }while(isset($ex[$r]));
    	return $r;
    }
    /**/


    Beitrag geaendert: 14.12.2006 23:47:30 von ruehrer

    @nachposter, ups, darauf hab ich garnicht geachtet

    Beitrag geaendert: 15.12.2006 14:37:11 von ruehrer

  17. function foxy_rand_exclusive(
      $min,
      $max,
      $ex   // ARRAY
    ) {
      $ex = array_flip($ex);
      
      //* sanity check
      if( count($ex) > $max - $min + 2) {
        for($i = $min; $i <= $max + 1; $i++) if(!isset($ex[$i])) break;
        if($i > $max) return(foxy()->error_return(E_WARNING, 'Bist du blöd oder was!?'));
      }
      //*/
      
      do {
        $rnd = mt_rand($min, $max);
      } while(isset($ex[$rnd]));
      return($rnd);
    }


    Dir ist schon klar, dass deine rand_d_2-Funktion genau wie die erste von Alopex funktioniert, die bei meinem Benchmark fast am besten abgeschnitten hat?
  18. Diskutiere mit und stelle Fragen: Jetzt kostenlos anmelden!

    lima-city: Gratis werbefreier Webspace für deine eigene Homepage

Dir gefällt dieses Thema?

Über lima-city

Login zum Webhosting ohne Werbung!