Feststellen, ob eine Zahl ungerade / gerade ist

Oft möchte man in der täglichen Web-Programmier-Praxis feststellen, ob eine Zahl gerade oder ungerade ist (gerade = durch 2 ganzzahlig teilbar). Ich selbst gebrauche das gern, um Übersicht in Artikellisten zu schaffen, indem ich abwechselnd die Hintergrundfarbe mal heller und mal etwas dunkler darstelle. Das ist wesentlich übersichtlicher als die Variante alle mit gleichem Hintergrund aufzulisten. Wie nun aber prüft man möglichst performant, ob eine Zahl gerade oder ungerade ist?

Ich möchte kurz alle hier zu testenden Varianten vorstellen:

// Variante 1 - die Amateurhafte
$o = 0;
for($i=0;$i<1000;$i++) {
  echo $i." ist ";
  $o++;
  if($o==1) echo "ungerade";
  else {
    echo "gerade";
    $o=0;
  }
}
 
// Variante 2 - die Mathematische
for($i=0;$i<1000;$i++) {
  echo $i." ist ";
  if($i%2) echo "ungerade";
  else echo "gerade";
}
 
// Variante 3 - die Binäre
for($i=0;$i<1000;$i++) {
  echo $i." ist ";
  if($i & 1) echo "ungerade";
  else echo "gerade";
}
 
// Variante 4 - die Kurze
for($i=0;$i<1000;$i++) {
  echo $i." ist ";
  echo ($i & 1)?"ungerade":"gerade";
}

Die 1. Variante ist sicherlich nicht oft umgesetzt, wird aber oft von Anfängern als erster Vorschlag geliefert. In diesem Fall wird eine zweite Zählvariable mitgeführt, die zwischen 1 und 2 rangiert (sicherlich kommt sie auch auf 0, das ist aber nicht relevant für die Überprüfung, ob eine Zahl gerade ist). Aber das ständige Inkrementieren und Zurücksetzen kostet unnötig Zeit, zumal wir ja bereits eine Zählvariable von der for-Schleife haben – und die sollten wir dann auch nutzen.

In der 2. Variante wird die Modulo-Operation genutzt. Diese gibt den ganzzahligen Rest einer Division zurück. Es ist demzufolge klar, dass wenn bei der Division durch 2 ein Rest bleibt, dass die Zahl dann ungerade ist. Und weil in PHP true==1 kann man die if-Abfrage ohne Vergleich schreiben.

Die 3. Variante kann nur von einem Informatiker stammen 😉
Hierbei wird die binäre Operation & 1 ausgeführt. Dazu wird die letzte Stelle der Zählvariable in Binärschreibweise mit 1 und-verknüpft (wichtig hierbei: nicht && schreiben, da das lediglich Prüfen würde, ob $i > 0!). Ist die letzte Stelle eine 0, so ergibt 0 & 1 = 0. Ist sie eine 1, so ist 1 & 1 = 1 (0 steht hierbei für false und 1 für true). Und genau das ist das gewünschte Ergebnis, da die letzte Binärstelle für 20 zuständig ist und demzufolge exakt definiert, ob die Zahl gerade oder ungerade ist.
Diese Variante folgt wohl dem Motto

There are 10 types of people: those who understand binaries and those who don’t

😉

Die 4. Variante hat nur den Zweck den trinären Operator (=Konditionaloperator) zu testen. Die Logik ist die gleiche wie bei Variante 3, könnte aber genauso gut mit dem Modulo-Operator umgesetzt werden. Vorteil dieser Variante ist, dass der Code sehr schön kurz ist.

So, nun genug Theorie, hier sind die Ergebnisse.

Datei Gesamtlaufzeit durchschnittliche Laufzeit pro Durchlauf Verhältnis zur schnellsten Variante
ungerade_binary.php 24.785640 s 2.479 ms 100 %
ungerade_mod.php 25.486648 s 2.549 ms 103 % (+ 3%)
ungerade_binary_ trinaer.php 26.718419 s 2.672 ms 108 % (+ 8%)
ungerade_counter.php 26.838592 s 2.684 ms 108 % (+ 8%)

Am schnellsten in die Binär-Und-Verknüpfung. Das ist auch nicht weiter verwunderlich, da die (meisten) heutigen Rechner nunmal im Binärmodus arbeiten und somit damit auch am schnellsten zurechtkommen. Die Modulo-Operation ist etwas langsamer, aber noch akzeptabel.
Etwas überrascht hat mich die Performance des trinären Operators. Hätte gedacht, dass dieser nicht nur kurz sondern auch flott ist, aber dem ist nicht so. Werde dann also in Zukunft lieber „richtige“ if-else-Reihen aufbauen.
Und nicht überraschend ist die Langsamkeit der zusätzlichen Zählvariable. Auch wenn diese Variante genauso langsam ist wie mit dem Konditionaloperator, habe ich sie rot markiert, weil sie einfach anfängerhaft ist und in keinem Projekt vorkommen sollte. Dazu kommt noch, dass sie zusätzlichen Speicher verschwendet durch die zusätzliche Variable.

Zum Nachprüfen stelleich wie immer auch die Quellcodes sowie die Benchmark-Ergebnisse zur Verfügung.

Jan hat 152 Beiträge geschrieben

31 Kommentare zu “Feststellen, ob eine Zahl ungerade / gerade ist

  1. admin sagt:

    Verstehe nicht ganz. Wie geht der Spruch mit „10 people“ denn dann weiter? Dachte immer der Spruch heißt so, wie ich ihn geschrieben hab. Aber ist auch eigentlich wurscht.

  2. Reeno sagt:

    So wie oben: „There are 10 types of people: those who understand binaries and those who don’t.“
    Dann ist er auch wirklich nur von Informatikern zu verstehen.

  3. Ralf sagt:

    Ähm na der geht genau so weiter wie du es geschrieben hast… Also komplett:

    „There are 10 types of people: those who understand binaries and those who don’t.“

    Hint: 10 ist binär für 2 (two). Erst damit ist das lustig 😉

  4. Mark sagt:

    Der Spruch geht genauso wie bei dir weiter. Denn das ist ja gerade der Witz daran: Vorne steht „10“ (binär für 2), während hinten von der 2 die Rede ist. Mit der 2 vorne und hinten, ist der Satz ja etwas langweilig.

  5. Mark sagt:

    Sorry, ich hatte die Seite länger im Hintergrund offen und habe dann erst den Kommentar geschrieben …

  6. Chaospisser sagt:

    Hätte ich nicht gedacht ! Dachte immer Modulo wär das schnellste. Naja kann man mal sehen wie man falsch liegen kann 🙂 Super Seite, mach weiter so!

  7. GhostGambler sagt:

    Naja, 8% bei einer Zeile, sind aber nun wirklich nichts worauf man Rücksicht nehmen muss.
    Da gibt es in den meisten Applikationen doch weit größere bottlenecks.

    Da sollte man dann eher auf einfach zu verstehenden Code achten~

  8. Neonomicus sagt:

    Guter Artikel!
    Wie siehts denn mit dieser Variante aus:

    $i = 0;
    if ($i=!$i)
    echo „jo“;
    else
    echo „Nö“;

    Habs nicht getestet und dürfte auch langsamer als die Binary-Variante sein aber ich finds trotzdem interessant 🙂

    Gruß
    Neonomicus

  9. admin sagt:

    Die Lösung verstehe ich nicht. Vor allem mit größeren Zahlen. Wenn das funktioniert, dann glaube nur mit 1 und 0 (weil die in PHP für true und false stehen), oder?

    Kleines Bsp:
    $i=3;
    !$i = false bzw. 0
    Schon da komme ich auf keinen grünen Zweig. Oder hab ich nen Denkfehler grad???

  10. Neonomicus sagt:

    Schon wieder weggefiltert … naja.
    Aaalso ^^ Dadurch !-1 wird ja wieder zu 1. Damit kann man im Prinzip in einer Schleife abwechselnd false,true,false,true erhalten und so dann schauen welche Farbe man ausgibt. if ($c=!$c) macht dann aus true false und aus false true.

  11. Holger sagt:

    Weiterführender Tipp zu Variante #3:

    Dieses Prinzip funktioniert allgemein für Zweierpotenzen, d. h. auf Grund der Äquivalenz …


    Zahl % 2^n Zahl & n-1

    … kann man nun beispielsweise folgendes unter PHP (sowie allg.) machen:

    $zahl % 4 ersetzen durch $zahl & 3,
    sowie
    $zahl % 8 ersetzen durch $zahl & 7
    usw.

    Damit würde dann beispielsweise entsprechend jede vierte, achte, … Zelle gesondert behandelt.

  12. Holger sagt:

    Sorry, das Skript hat mir meinen Äquivalenzpfeil geklaut – zur Verdeutlichung:

    „Zahl % 2^n“
    … ist äqivalent zu …
    „Zahl & n-1“

  13. Holger sagt:

    Heute ist echt nicht mein Tag, man möge mir verzeihen (gerne auch die Beiträge zu einem zusammenfassen, wenn das geht).

    „Zahl % 2^n“ ist natürlich äquivalent zu „Zahl & n^2-1“

    🙁

  14. Silvio sagt:

    Anscheinend wird der Code hier nicht korrekt ausgegeben? Gibts ne Möglichkeit PHP-Code ohne eine Beschneidung anzeigen zu lassen?

  15. Arno Nühm sagt:

    mal eine andere lösung für das eigentlich dargestellte problem: war es nicht möglich auch völlig mittels CSS die Hintergrundfarbe wechseln zu lassen!?

  16. Michael sagt:

    Auch hier gilt: Die Denkweise ist zu einseitig. Stell Dir eine Iteration über eine Array vor mit Hilfe von Iteratoren, die aus vielen Gründen (Kapselung ist nur einer) empfehlenswert ist:

    foreach($foo as $bar)   echo "$bar";

    Jetzt könnte man eine fiese Zählvariable einführen oder nutzt einfach eine Abart der ersten Variante:

    $odd_or_even = 1; foreach($foo as $bar) {   $odd_or_even = 1 - $odd_or_even;   echo "$bar"; }

    Dazu noch zwei CSS Klassen .row0 und .row1 und gut ist. Sehe nicht, was daran Amateurhaft ist.

  17. admin sagt:

    Sicherlich geht das auch so, dann hast Du nur ne zusätzliche Variable. Wenn Du nämlich mal kein Array hast und foreach nicht benutzen kannst, ist es sinnvoll die Zählvariable zu benutzen.

  18. NimraX sagt:

    Wenns noch das letzte Quentchen Performance sein darf, dann nutzt ++$i anstelle $i++, wenn Ihr den „alten“ $i-Wert nicht als Rückgabewert braucht. Das bringt auch nochmal bissel Speed in die Schleife.

Eine Antwort schreiben

Ihre E-Mail-Adresse wird nicht veröffentlicht. Benötigte Felder sind markiert mit *

You may use these HTML tags and attributes: <a href=""> <blockquote cite=""> <pre lang=""> <b> <strong> <i> <em>