Mehr zum Thema Schleifen

Aus TobisWiki
Wechseln zu: Navigation, Suche

Inhaltsverzeichnis

Mehr zum Thema Schleifen

Einführung

Die Geschwindigkeit der Codeausführung hängt entscheidend von der Wahl der Schleife ab. Dabei geht es nicht um die innere Schleife. Diese muss so oder so mit einem festen Inkrement des Zählers versehen werden. Es können dort minime Verbesserungen unter Verwendung einer for- anstatt einer while-Schleife erreicht werden. Diese liegen aber bei einem Bruchteil, dessen was eine while-Schleife aussen einsparen kann. Darum möchte ich folgenden zeigen, warum beim Sieb die äussere Schleife eine while-Schleife sein sollte.

Unterschiedliche Schleifen

foreach-Schleife

Diese Art der Schleife ist denkbar schlecht, da sie eine Kopie des Arrays übergeben bekommt. Daher "bekommt" die Schleife auch gar nichts mit von den Elementen die von der Funktion gelöscht wurden.

 
<?php
/*
$array ist das Zahlenarray der zu prüfenden Zahlen.
$ende_abs ist die Quadratwurzel der $zahl (Ende des zu prüfenden Bereichs) 
$ii zählt wie oft die äussere Schleife durchlaufen wurde
*/
foreach($array as $wert){
    $ii += 1;
    if($wert >= $ende_abs){
        break;
    }elseif($wert == 2){
        continue;
    }
    $ende = (int) ($zahl/$wert);
    $ende += 1;
    for($i=$wert;$i<$ende;$i+=2){
        unset($array[$i*$wert]);
    }
}
var_dump($ii);
?>
 

<runphp> $ii = 0; function inittt_array($zahl){

 $anfang = microtime(true);
 $temp = array();
 $temp[2] = 2;
 $temp[3] = 3;
 for($i=3;$i<=$zahl;$i+=2){
   ($i % 3) ? $temp[$i] = $i : 0;
 }
 $diff = microtime(true) - $anfang;
   return $temp;

} $zahl = 100000; $array = inittt_array($zahl); $ende_abs = sqrt($zahl); foreach($array as $wert){

   $ii += 1;
   if($wert >= $ende_abs){
       break;
   }elseif($wert == 2){
       continue;
   }
   $ende = (int) ($zahl/$wert);
   $ende += 1;
   for($i=$wert;$i<$ende;$i+=2){
       unset($array[$i*$wert]);
   }

} echo 'Die Schleife wurde '.$ii.'-mal durchlaufen '; </runphp>

for-Schleife

Die Problematik der for Schleife liegen ähnlich. Zwar kann sie auf den aktuellen Stand des Arrays zugreifen, durch das fixe Inkrement des Zählers kommt es jedoch dazu, dass Produkte berechnet werden, die im Array gar nicht mehr existieren. Also muss man mittels eines isset() erst prüfen, ob es das Element noch gibt, das man da löschen will. Sonst gibt's einen Fehler. Das kostet aber Zeit und das merkt man v.a. bei grossen Arrays doch deutlich. Bei meinem Test mit dieser Schleife ergab ein dump von $ii, dass dies 49-mal geschah.

 
<?php
/*
$z ist die erste Zahl von der die Vielfachen gestrichen werden müssen
$ende_abs ist die Quadratwurzel der $zahl (Ende des zu prüfenden Bereichs) 
$ii zählt wie oft die äussere Schleife durchlaufen wurde
*/
for($z=3;$z<=$ende_abs;$z+=2){
    $ii += 1;
    if($z == 2){
        continue;
    }
    $ende = (int) ($zahl/$z);
    $ende += 1;
    for($i=$z;$i<$ende;$i+=2){
        unset($array[$i*$z]);
    }
}
var_dump($ii);
?>
 

<runphp> $ii = 0; function initt_array($zahl){

 $anfang = microtime(true);
 $temp = array();
 $temp[2] = 2;
 $temp[3] = 3;
 for($i=3;$i<=$zahl;$i+=2){
   ($i % 3) ? $temp[$i] = $i : 0;
 }
 $diff = microtime(true) - $anfang;
   return $temp;

} $zahl = 100000; $array = initt_array($zahl); $ende_abs = sqrt($zahl);

for($z=3;$z<=$ende_abs;$z+=2){

   $ii += 1;
   if($z == 2){
       continue;
   }
   $ende = (int) ($zahl/$z);
   $ende += 1;
   for($i=$z;$i<$ende;$i+=2){
       unset($array[$i*$z]);
   }

} echo 'Die Schleife wurde '.$ii.'-mal durchlaufen '; </runphp>

while-Schleife

Das Optimum kann man mittels dieser Schleife herausholen. Diese braucht keinen Zähler, sondern holt sich jeweils das nächste Element. Daher kann die Schleife sehr gut auf die gelöschten Zahlen reagieren --> sie ruft sie gar nicht zur Prüfung auf !

 
<?php
/*
$array ist das Zahlenarray der zu prüfenden Zahlen.
$ende_abs ist die Quadratwurzel der $zahl (Ende des zu prüfenden Bereichs) 
$ii zählt wie oft die äussere Schleife durchlaufen wurde
*/
while($wert = each($array)){
    $ii += 1;
    if($wert>=$ende_abs){
          break;
    }elseif($wert == 2){
        continue;
    }
    $ende = (int) ($zahl/$wert);
    $ende += 1;
    for($i=$wert;$i<$ende;$i+=2){
        unset($array[$i*$wert]);
    }
}
var_dump($ii);
?> 
 

<runphp>

/* $array ist das Zahlenarray der zu prüfenden Zahlen. $ende_abs ist die Quadratwurzel der $zahl (Ende des zu prüfenden Bereichs) --> sqrt(10000)-->100 $ii zählt wie oft die äussere Schleife durchlaufen wurde

  • /

$ii = 0; function init_array($zahl){

 $anfang = microtime(true);
 $temp = array();
 $temp[2] = 2;
 $temp[3] = 3;
 for($i=3;$i<=$zahl;$i+=2){
   ($i % 3) ? $temp[$i] = $i : 0;
 }
 $diff = microtime(true) - $anfang;
   return $temp;

} $zahl = 100000; $array = init_array($zahl); $ende_abs = sqrt($zahl); while($wert = each($array)){

   $wert = $wert[0];
   $ii += 1;
   if($wert>=$ende_abs){
         break;
   }elseif($wert == 2){
       continue;
   }
   $ende = (int) ($zahl/$wert);
   $ende += 1;
   for($i=$wert;$i<$ende;$i+=2){
       unset($array[$i*$wert]);
   }

} echo 'Die Schleife wurde '.$ii.'-mal durchlaufen '; </runphp>

Meine Werkzeuge