
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.
Schlagwörter: ausgabe, gerade, PHP, teilbar, ungerade













Ralf sagt
am 8. August 2007 @ 21:32
Der Spruch lautet aber glaubich:
"There are 10 people, …"
Macht dann auch mehr Sinn
admin sagt
am 8. August 2007 @ 21:41
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.
Reeno sagt
am 8. August 2007 @ 21:44
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.
Ralf sagt
am 8. August 2007 @ 22:09
Ä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
Mark sagt
am 8. August 2007 @ 22:38
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.
Mark sagt
am 8. August 2007 @ 22:39
Sorry, ich hatte die Seite länger im Hintergrund offen und habe dann erst den Kommentar geschrieben …
admin sagt
am 9. August 2007 @ 07:53
Ah korrekt. Da war ich wohl ein wenig neben der Spur…
Chaospisser sagt
am 9. August 2007 @ 21:41
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!
GhostGambler sagt
am 10. August 2007 @ 14:25
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~
Neonomicus sagt
am 11. August 2007 @ 18:47
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
Neonomicus sagt
am 11. August 2007 @ 18:48
Kleiner Fehler ^^ muss $i=1 am Anfang.
admin sagt
am 11. August 2007 @ 19:36
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???
Neonomicus sagt
am 11. August 2007 @ 20:55
Bin mir nicht sicher …
Das bezog sich jetzt auch mehr auf dein Beispiel mit der Liste. Hier ist was in ausführbar:
Neonomicus sagt
am 11. August 2007 @ 20:56
Hm, hat den Code weggefiltert. Hier nochmal:
$c = 1;
for ($i=0; $i
Neonomicus sagt
am 11. August 2007 @ 21:01
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.
Holger sagt
am 20. August 2007 @ 22:10
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.
Holger sagt
am 20. August 2007 @ 22:11
Sorry, das Skript hat mir meinen Äquivalenzpfeil geklaut – zur Verdeutlichung:
"Zahl % 2^n"
… ist äqivalent zu …
"Zahl & n-1"
Holger sagt
am 20. August 2007 @ 22:58
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"
Silvio sagt
am 24. August 2007 @ 23:10
Wie wärs damit ^^
for($a=0;$a' : $a.' ungerade';
Silvio sagt
am 24. August 2007 @ 23:13
Anscheinend wird der Code hier nicht korrekt ausgegeben? Gibts ne Möglichkeit PHP-Code ohne eine Beschneidung anzeigen zu lassen?
admin sagt
am 25. August 2007 @ 09:12
Schreib es mal in ein <pre></pre>
admin sagt
am 25. August 2007 @ 09:14
Schreib es mal in ein PRE-Tag (<pre&gr;</pre>
Arno Nühm sagt
am 24. September 2007 @ 08:50
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!?
admin sagt
am 24. September 2007 @ 08:58
Korrekt, man kann per CSS 3.0 unterschiedlich farbige Tabellen-Zeilen machen. Allerdings unterstützen das die gängigen Browser noch nicht.
http://www.wssexpert.de/Style/Examples/007/evenodd.html
Michael sagt
am 9. Oktober 2007 @ 10:08
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:
Jetzt könnte man eine fiese Zählvariable einführen oder nutzt einfach eine Abart der ersten Variante:
Dazu noch zwei CSS Klassen .row0 und .row1 und gut ist. Sehe nicht, was daran Amateurhaft ist.
Michael sagt
am 9. Oktober 2007 @ 10:09
Dein WP hat ein paar < und > gekillt, die gehören da noch in den Code rein, um es zu veranschaulichen.
admin sagt
am 9. Oktober 2007 @ 10:30
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.
if...else if...else??? - AJAX (Asynchronous JavaScripting and XML) Forum sagt
am 10. Oktober 2007 @ 14:19
[...] kommt, dass die performance dieser schreibweise fragwürdig ist. in php ist diese variante nämlich langsamer, als normale if/else verschachtelungen. ob das in JS genau so [...]
CPI Blog » PHP: Zahl gerade oder ungerade? sagt
am 21. Oktober 2007 @ 17:20
[...] paar eigene Scripte programmieren zu können. Dabei fand ich gerade die verschiedenen Varianten zur Lösung des Problems ist die Zahl gerade oder ungerade interessant. Ich hätte ja auch gesagt, dass der Modulo-Operator der schnellste und einfachste [...]
ClausVB sagt
am 18. Dezember 2007 @ 11:38
Kleiner Tipp: Es würde die Performance erhöhen, wenn Du anstatt
echo ($i & 1)?"ungerade":"gerade";
folgendes
echo ($i & 1)?'ungerade':'gerade';
schreiben würdest. Das gilt auch für alle anderen Beispiele. So wird der String nicht nach Variablen durchsucht. Siehe auch:
http://www.phpbar.de/w/Code-Optimierung
Gruß
Claus
NimraX sagt
am 30. Juni 2009 @ 08:43
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.