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.