Wie überprüfe ich, ob eine Ganzzahl gerade oder ungerade ist? [geschlossen]


193

Wie kann ich überprüfen, ob eine bestimmte Zahl in C gerade oder ungerade ist?


5
Die Version, die bitweise und (&) verwendet, ist viel effizienter als die Modulo (%) -Version. Sie sollten die von Ihnen als richtige Antwort ausgewählte Antwort ändern.
Stefan Rusek

6
Unwahrscheinlich - Argument ist eine Konstante. Einfach für den Optimierer
MSalters

2
Auch hier spielt die Lesbarkeit eine Rolle.
Brian G

2
In eingebetteten Anwendungen (der Welt, in der ich den größten Teil meiner Programmierzeit verbringe) haben einige Prozessoren sehr primitive Recheneinheiten und können Divisions- / Moduloperationen nicht einfach ausführen. Aus diesem Grund verwende ich stattdessen normalerweise die bitweise Methode und. Auf der CPU eines modernen Desktops ist dies jedoch nicht der Fall.
bta

3
Ich habe nie festgestellt, dass die Moduloperation einfacher zu verstehen ist. Als ich zum ersten Mal gerade oder ungerade bestimmen musste, fiel mir als erstes die bitweise Maske ein. Es ist etwas natürlich, da wir dies in der Regel von Hand tun, indem wir auf die niedrigstwertige Ziffer schauen, um festzustellen, ob sie in {0 2 4 6 8} oder {1 3 5 7 9} steht. Das bedeutet direkt, dass man sich das niedrigstwertige Bit ansieht, um zu sehen, ob es 0 oder 1 ist.
P Daddy

Antworten:


449

Verwenden Sie den Modulo (%) -Operator, um zu überprüfen, ob beim Teilen durch 2 ein Rest vorhanden ist:

if (x % 2) { /* x is odd */ }

Einige Leute haben meine obige Antwort kritisiert und festgestellt, dass die Verwendung von x & 1 "schneller" oder "effizienter" ist. Ich glaube nicht, dass dies der Fall ist.

Aus Neugier habe ich zwei triviale Testfallprogramme erstellt:

/* modulo.c */
#include <stdio.h>

int main(void)
{
    int x;
    for (x = 0; x < 10; x++)
        if (x % 2)
            printf("%d is odd\n", x);
    return 0;
}

/* and.c */
#include <stdio.h>

int main(void)
{
    int x;
    for (x = 0; x < 10; x++)
        if (x & 1)
            printf("%d is odd\n", x);
    return 0;
}

Ich habe diese dann mit gcc 4.1.3 auf einer meiner Maschinen 5 verschiedene Male kompiliert:

  • Ohne Optimierungsflags.
  • Mit -O
  • Mit -Os
  • Mit -O2
  • Mit -O3

Ich untersuchte die Assembly-Ausgabe jeder Kompilierung (unter Verwendung von gcc -S) und stellte fest, dass die Ausgabe für and.c und modulo.c jeweils identisch war (beide verwendeten den Befehl andl $ 1,% eax). Ich bezweifle, dass dies eine "neue" Funktion ist, und ich vermute, dass sie auf alte Versionen zurückgeht. Ich bezweifle auch, dass einem modernen (in den letzten 20 Jahren hergestellten) nicht-arkanen Compiler, kommerziell oder Open Source, eine solche Optimierung fehlt. Ich würde auf anderen Compilern testen, habe aber momentan keine zur Verfügung.

Wenn jemand anderes andere Compiler und / oder Plattformziele testen möchte und ein anderes Ergebnis erzielt, wäre ich sehr interessiert zu wissen.

Schließlich garantiert der Standard , dass die Modulo-Version unabhängig davon funktioniert, ob die Ganzzahl vorzeichenbehaftete Ganzzahlen darstellt, unabhängig davon, ob die Ganzzahl positiv, negativ oder null ist. Die bitweise und Version ist nicht. Ja, mir ist klar, dass die Ergänzung von zwei etwas allgegenwärtig ist, also ist dies nicht wirklich ein Problem.


11
In der Frage wurde speziell gefragt, wie es in C gemacht werden soll, also habe ich es in C beantwortet, obwohl Chustar erwähnte, dass sie nicht herausfinden konnten, wie man es in Java macht. Ich habe nicht behauptet oder impliziert, dass dies eine Java-Antwort war, ich kenne Java nicht. Ich glaube, ich habe gerade meine erste Ablehnung erhalten und bin verwirrt, warum. Naja.
Chris Young

33
Ich würde sagen, wenn (x% 2! = 0) {/ * x ungerade * /} ist, aber wer weiß. Ich kenne Java auch nicht.
Eugensk

9
Es gibt viele positive Stimmen, um es von den bitweisen Operator-Idioten zu unterscheiden, ohne dass wir unser Karma dafür ausgeben müssen, sie abzustimmen.
wnoise

13
Ich bin mit allem einverstanden, außer einer Sache: Ich mag es, Ganzzahlen und Wahrheitswerte konzeptionell getrennt zu halten, deshalb schreibe ich lieber "if (x% 2 == 1)". Für den Compiler ist es dasselbe, für den Menschen vielleicht etwas klarer. Außerdem können Sie denselben Code in Sprachen verwenden, die Nicht-Null nicht als wahr interpretieren.
Thomas Padron-McCarthy

46
Mein Benchmark? Welcher Benchmark? Ich habe kein Benchmarking durchgeführt. Ich habe die generierte Assemblersprache untersucht. Das hat absolut nichts mit printf zu tun.
Chris Young

207

Ihr seid waaaaaaaay zu effizient. Was Sie wirklich wollen, ist:

public boolean isOdd(int num) {
  int i = 0;
  boolean odd = false;

  while (i != num) {
    odd = !odd;
    i = i + 1;
  }

  return odd;
}

Wiederholen für isEven.

Bei negativen Zahlen funktioniert das natürlich nicht. Aber mit der Brillanz kommt das Opfer ...


17
Wenn Sie eine Argumentausnahme für negative Werte ausgelöst und in der Dokumentation festgestellt haben, dass diese Funktion O (N) ist, würde ich damit einverstanden sein.
Jeffrey L Whitledge

7
Die Unternehmensversion müsste XML verwenden. Natürlich hätten Sie heutzutage einen Webdienst, den Sie abfragen könnten
Martin Beckett

58
Sie sollten dies mit einer Nachschlagetabelle optimieren.
Weeble

1
Ich bin so ein Mönch, musste Ihre 6.999 Wiederholungen in ein neues Jahrtausend +1 bringen
Eran Medan

7
Das ist brilliant! Mein Chef sagte mir, wir hätten einen Kunden, der wütend sei, weil er der Meinung sei, dass seine Unternehmenslizenz nicht mehr als die Standardlizenz gebe. Jetzt haben wir diese Funktion in unser Programm aufgenommen, und nur weil sie langsamer ausgeführt wird, glaubt er, dass seine Software viel mehr Arbeit leistet !!!
Phil

97

Verwenden Sie die Bitarithmetik:

if((x & 1) == 0)
    printf("EVEN!\n");
else
    printf("ODD!\n");

Dies ist schneller als die Verwendung von Division oder Modul.


43
Ich denke nicht, dass es fair ist zu sagen, dass es schneller ist als die Verwendung von Division oder Modul. Der C-Standard sagt nichts über die Leistung von Operatoren aus, und jeder anständige Compiler erzeugt für beide einen schnellen Code. Ich würde persönlich die Redewendung wählen, die meine Absicht kommuniziert, und% scheint hier angemessener zu sein
Chris Young

21
Ich mag (x & 1) besser, weil es prüft, ob die Zahl gerade ist wie die Leute: prüfe, ob die letzte Ziffer gerade oder ungerade ist. Meiner Meinung nach kommuniziert es seine Absicht mehr als die Modulo-Methode. (Nicht, dass es viel
ausmacht

2
Du hast recht, ich denke es ist subjektiv. Obwohl die übliche Definition von "gerade" "Ganzzahl, die durch 2 teilbar ist" ist, ist nicht "Ganzzahl, die mit 0, 2, 4, 6 oder 8 endet". :-)
Chris Young

4
@TraumaPony - für ANSI-Standard C und frühes Java, abhängig vom Computersystem. Es ist nicht spezifiziert, welche Darstellung für vorzeichenbehaftete Zahlen verwendet wird - 2-Kompliment, 1-Kompliment, grau codiert usw. Aber Modul ist immer Modul
Aaron

9
Funktioniert nicht universell für negative Zahlen. Weitere Informationen finden Sie unter Überprüfen Sie diese Antwort: stackoverflow.com/questions/160930/… für weitere Details.
Andrew Edgecombe

36

[Witzmodus = "Ein"]

public enum Evenness
{
  Unknown = 0,
  Even = 1,
  Odd = 2
}

public static Evenness AnalyzeEvenness(object o)
{

  if (o == null)
    return Evenness.Unknown;

  string foo = o.ToString();

  if (String.IsNullOrEmpty(foo))
    return Evenness.Unknown;

  char bar = foo[foo.Length - 1];

  switch (bar)
  {
     case '0':
     case '2':
     case '4':
     case '6':
     case '8':
       return Evenness.Even;
     case '1':
     case '3':
     case '5':
     case '7':
     case '9':
       return Evenness.Odd;
     default:
       return Evenness.Unknown;
  }
}

[Witzmodus = "aus"]

BEARBEITEN: Der Aufzählung wurden verwirrende Werte hinzugefügt.


2
Wow ... das ist wahnsinniger als die SCdF-Lösung! Ein großes Lob! Keine Gegenstimme ... kann das nicht empfehlen. Aber danke für das lustige!
Wes P

1
Der Vorteil dieses Ansatzes ist, dass er mit mehr als nur Zahlen funktioniert. Wenn Sie diese Zeile ersetzen: char bar = foo [foo.Length - 1]; damit: double bar = Char.GetNumericValue (foo [foo.Length - 1]); Dann funktioniert es mit jedem Zahlensystem.
Jeffrey L Whitledge

5
Fehlerbericht: 14.65 wird als ungerade gemeldet, wenn es unbekannt sein sollte.
TheSoftwareJedi

4
Software Jedi, es ist eine "Funktion". ;)
Sklivvz

31
TheSoftwareJedi: 14.65 ist eine der merkwürdigsten ganzen Zahlen, die ich je gesehen habe.
Bruce Alderman

16

Als Antwort auf ffpf - Ich hatte vor Jahren genau das gleiche Argument mit einem Kollegen, und die Antwort lautet nein , es funktioniert nicht mit negativen Zahlen.

Der C-Standard sieht vor, dass negative Zahlen auf drei Arten dargestellt werden können:

  • 2's Ergänzung
  • 1's Ergänzung
  • Vorzeichen und Größe

So prüfen:

isEven = (x & 1);

funktioniert für das Komplement von 2 und die Darstellung von Vorzeichen und Größe, jedoch nicht für das Komplement von 1.

Ich glaube jedoch, dass Folgendes in allen Fällen funktionieren wird:

isEven = (x & 1) ^ ((-1 & 1) | ((x < 0) ? 0 : 1)));

Vielen Dank an ffpf für den Hinweis, dass das Textfeld alles nach meinem weniger als Charakter gegessen hat!


Ich denke, in Ihrem zweiten Codebeispiel fehlt Text.
Jeff Yates

3
Lassen Sie uns diese Zahlen beglückwünschen!
Thejh

14

Eine schöne ist:

/*forward declaration, C compiles in one pass*/
bool isOdd(unsigned int n);

bool isEven(unsigned int n)
{
  if (n == 0) 
    return true ;  // I know 0 is even
  else
    return isOdd(n-1) ; // n is even if n-1 is odd
}

bool isOdd(unsigned int n)
{
  if (n == 0)
    return false ;
  else
    return isEven(n-1) ; // n is odd if n-1 is even
}

Beachten Sie, dass diese Methode die Schwanzrekursion mit zwei Funktionen verwendet. Es kann effizient implementiert werden (in eine while / till-Schleife umgewandelt), wenn Ihr Compiler die Tail-Rekursion wie ein Scheme-Compiler unterstützt. In diesem Fall sollte der Stapel nicht überlaufen!


1
Dies behandelt isOdd (0) nicht gut.
Steve McLeod

1
Ich denke, Sie haben eine Endlosschleife (mit Schwanzrekursion) oder einen Stapelüberlauf (ohne Schwanzrekursion) für isOdd () mit geraden Werten oder isEven () mit ungeraden Werten. Es endet nur mit true. Es ist wieder das Problem des Stillstands.
Jeffrey L Whitledge

7
Oh, klar, repariere es ohne Kommentar und lass mich wie einen Idioten aussehen. Das ist gut.
Jeffrey L Whitledge

1
Jetzt haben Sie einen Kompilierungsfehler: In isEven geben nicht alle Codepfade einen Wert zurück. Nein, ich habe diesen Code noch nicht ausprobiert, es ist der Compiler in meinem Kopf, der sich beschwert.
Jeffrey L Whitledge

5
Kompilierungsfehler: Nicht alle Pfade geben einen Wert zurück, der Sie mit Fehlerkommentaren zu Ihrem Beispielcode bombardiert, aber was passiert, wenn Sie isEven (5)
Kevin

11

Eine Zahl ist gerade, wenn der Rest, wenn sie durch zwei geteilt wird, 0 ist. Eine Zahl ist ungerade, wenn der Rest, wenn sie durch 2 geteilt wird, 1 ist.

// Java
public static boolean isOdd(int num){
    return num % 2 != 0;
}

/* C */
int isOdd(int num){
    return num % 2;
}

Methoden sind großartig!


Ihre Java-Methode ist fehlerhaft, da num% 2 == -1 für negative ungerade Zahlen ist.
WMR

Hast du mich deshalb herabgestimmt?
jjnguy

3
Ich habe es abgelehnt, weil Ihre Funktion in C mehr Zeichen zum Eingeben benötigt als das, was sie tut. IE num% I besteht aus 7 Zeichen, einschließlich der Leerzeichen IsOdd (I) besteht aus 8 Zeichen. Warum sollten Sie eine Funktion erstellen, die länger ist als nur die Operation?
Kevin

13
@ Kevin wird meiner Meinung nach nicht anhand von Zeichen gemessen, sondern anhand der Zeit, die Sie zum Schreiben benötigen, einschließlich Think + Debug-Zeit. num% 2 dauert eine Millisekunde länger als isOdd. Fügen Sie jetzt die Zahlen global hinzu und Sie haben ein kollektives Jahr verloren. Außerdem kann isOdd getestet und verifiziert und schließlich fehlerfrei zertifiziert werden (z. B. Umgang mit negativen Zahlen), wobei einige Entwickler immer Zweifel haben und experimentieren. Guter Code ist Code, den Sie nicht schreiben, sondern nur wiederverwenden ... nur meine 2 Cent.
Eran Medan

2
@EranMedan, Die gleiche Logik würde für das Ersetzen von i ++ durch IncrementByOne (i) gelten, und es ist eine ebenso schlechte Idee. Wenn ein Entwickler Zweifel daran hat, was num% 2 tut, möchte ich ihn oder sie nicht in der Nähe meines Codes haben.
Kevin


7

Ich würde sagen, dividiere es einfach durch 2 und wenn es einen Rest von 0 gibt, ist es gerade, sonst ist es seltsam.

Die Verwendung des Moduls (%) macht dies einfach.

z.B. 4% 2 = 0, daher ist 4 gerade 5% 2 = 1, daher ist 5 ungerade


6

Noch eine Lösung für das Problem
(Kinder können gerne wählen)

bool isEven(unsigned int x)
{
  unsigned int half1 = 0, half2 = 0;
  while (x)
  {
     if (x) { half1++; x--; }
     if (x) { half2++; x--; }

  }
  return half1 == half2;
}

Nein, du bist nicht die Art von Kind, auf die ich mich verlassen habe :)
Eugensk

Ich wollte das positiv bewerten, aber bei negativen Zahlen ist es etwas langsam. :)
Chris Young

3
Alle Zahlen sind hell und positiv. Oder sind Sie gegen einige voreingenommen? :))
Eugensk

3
In Computern werden alle einmal negativen Zahlen schließlich positiv. Wir nennen es den Rollover of Happiness (gilt nicht für BIGNUMS, YMMY, nicht in allen Staaten gültig).
Will Hartung

@ WillHartung "Rollover des Glücks" ist großartig! : D
thejh

6

Ich würde eine Tabelle der Paritäten (0, wenn gerade 1, wenn ungerade) der ganzen Zahlen erstellen (damit man nachschlagen kann: D), aber gcc lässt mich keine Arrays mit solchen Größen erstellen:

typedef unsigned int uint;

char parity_uint [UINT_MAX];
char parity_sint_shifted [((uint) INT_MAX) + ((uint) abs (INT_MIN))];
char* parity_sint = parity_sint_shifted - INT_MIN;

void build_parity_tables () {
    char parity = 0;
    unsigned int ui;
    for (ui = 1; ui <= UINT_MAX; ++ui) {
        parity_uint [ui - 1] = parity;
        parity = !parity;
    }
    parity = 0;
    int si;
    for (si = 1; si <= INT_MAX; ++si) {
        parity_sint [si - 1] = parity;
        parity = !parity;
    }
    parity = 1;
    for (si = -1; si >= INT_MIN; --si) {
        parity_sint [si] = parity;
        parity = !parity;
    }
}

char uparity (unsigned int n) {
    if (n == 0) {
        return 0;
    }
    return parity_uint [n - 1];
}

char sparity (int n) {
    if (n == 0) {
        return 0;
    }
    if (n < 0) {
        ++n;
    }
    return parity_sint [n - 1];
}

Lassen Sie uns stattdessen auf die mathematische Definition von gerade und ungerade zurückgreifen.

Eine ganze Zahl n ist auch dann vorhanden, wenn eine ganze Zahl k existiert, so dass n = 2k ist.

Eine ganze Zahl n ist ungerade, wenn eine ganze Zahl k existiert, so dass n = 2k + 1 ist.

Hier ist der Code dafür:

char even (int n) {
    int k;
    for (k = INT_MIN; k <= INT_MAX; ++k) {
        if (n == 2 * k) {
            return 1;
        }
    }
    return 0;
}

char odd (int n) {
    int k;
    for (k = INT_MIN; k <= INT_MAX; ++k) {
        if (n == 2 * k + 1) {
            return 1;
        }
    }
    return 0;
}

C-Ganzzahlen bezeichnen die möglichen Werte inteiner gegebenen C-Zusammenstellung. (Beachten Sie, dass C-Ganzzahlen eine Teilmenge der Ganzzahlen sind.)

Nun könnte man befürchten, dass für ein gegebenes n in C-Ganzzahlen die entsprechende Ganzzahl k möglicherweise nicht in C-Ganzzahlen existiert. Mit einem kleinen Beweis kann jedoch gezeigt werden, dass für alle ganzen Zahlen n, | n | gilt <= | 2n | (*), wobei | n | ist "n wenn n positiv ist und -n sonst". Mit anderen Worten, für alle n in ganzen Zahlen gilt mindestens eine der folgenden Aussagen (genau entweder Fälle (1 und 2) oder Fälle (3 und 4), aber ich werde es hier nicht beweisen):

Fall 1: n <= 2n.

Fall 2: -n <= -2n.

Fall 3: -n <= 2n.

Fall 4: n <= -2n.

Nehmen Sie nun 2k = n. (Ein solches ak existiert, wenn n gerade ist, aber ich werde es hier nicht beweisen. Wenn n nicht gerade ist even, kehrt die Schleife ohnehin nicht früh zurück, also spielt es keine Rolle.) Aber dies impliziert k <n, wenn n nicht 0 durch (*) und die Tatsache (auch hier nicht bewiesen), dass für alle m z in ganzen Zahlen 2m = z impliziert, dass z ungleich m ist, wenn m gegeben ist, ist nicht 0. Im Fall n ist 0, 2 * 0 = 0 0 ist gerade, wir sind fertig (wenn n = 0, dann ist 0 in C-Ganzzahlen, weil n in der Funktion in C-Ganzzahl isteven , daher ist k = 0 in C-Ganzzahlen). Somit existiert ein solches ak in C-Ganzzahlen für n in C-Ganzzahlen, wenn n gerade ist.

Ein ähnliches Argument zeigt, dass wenn n ungerade ist, ak in C-ganzen Zahlen existiert, so dass n = 2k + 1 ist.

Daraus ergibt sich die Funktionen evenund oddhier präsentiert wird richtig für alle C-Zahlen arbeiten.


1
Ich meine keine Beleidigung, aber worum geht es bei dieser Antwort? i % 2ist viel kleiner und wahrscheinlich effizienter.
GManNickG

2
@ GMan: Aber das ist viel deterministischer! Dadurch werden alle Kantenfälle korrekt erkannt.
P Daddy

1
... UND (!!!) es ist richtig !!!
Thomas Eding

Ich kann nicht sagen, ob Sie scherzen oder nicht. : X %2funktioniert für alle ganzen Zahlen.
GManNickG

1
+1: Ich wollte "Gute Antwort" sagen, aber ich denke, "Interessante Antwort" ist angemessener.
James Webster

5
// C#
bool isEven = ((i % 2) == 0);

2
Was? Das ist nicht C #! Das ist reines C! :-P
Asterit

8
Ich werde ein WinForm darum werfen, um es zu reinem C # zu machen ...
Michael Petrotta

@mateusza: Normalerweise, wenn Sie "bool" in irgendeiner Groß- oder Kleinschreibung in C sehen, ist es ein typedefoder #defineoder etwas.
David Thornley

2
@mateusza @David Thornley In C99 ist Bool eine Standardfunktion ( en.wikipedia.org/wiki/Stdbool.h )
fortran

1
Sprechen Sie über enorm überflüssige Klammern ...
Thomas Eding

4

Hier ist eine Antwort in Java:

public static boolean isEven (Integer Number) {
    Pattern number = Pattern.compile("^.*?(?:[02]|8|(?:6|4))$");
    String num = Number.toString(Number);
    Boolean numbr = new Boolean(number.matcher(num).matches());
    return numbr.booleanValue();
}

4

Versuche dies: return (((a>>1)<<1) == a)

Beispiel:

a     =  10101011
-----------------
a>>1 --> 01010101
a<<1 --> 10101010

b     =  10011100
-----------------
b>>1 --> 01001110
b<<1 --> 10011100

Können Sie das bitte erklären? Ich bin sehr unbekannt mit bitweisen Operatoren
Abdul

Wenn Sie nach rechts und dann nach links verschieben, wird Ihr letztes Bit (das ganz rechte) auf Null gesetzt. Wenn die neue Nummer mit der ursprünglichen Nummer identisch ist, bedeutet dies, dass das letzte Bit der ursprünglichen Nummer 0 war. Es ist also gerade. Schauen Sie sich meine aktualisierte Antwort an.
Kiril Aleksandrov

Danke, ich
Abdul

Ich bin mir nicht sicher, welcher Ansatz schneller ist. Ich habe nicht versucht, sie zu vergleichen.
Kiril Aleksandrov

Macht dies nicht auch Ihr wichtigstes Stück auf Null? Ein Problem mit nicht signierten Ints in einigen Sprachen und negativen Ints in den meisten ...
Troyseph

4

Als ich diese ziemlich unterhaltsame Diskussion las, erinnerte ich mich daran, dass ich eine reale, zeitkritische Funktion hatte, die innerhalb der Hauptschleife auf ungerade und gerade Zahlen testete. Es handelt sich um eine ganzzahlige Potenzfunktion, die wie folgt an anderer Stelle in StackOverflow veröffentlicht wird. Die Benchmarks waren ziemlich überraschend. Zumindest in dieser realen Funktion ist Modulo langsamer und deutlich langsamer . Der Gewinner, der mit großem Abstand 67% der Zeit von Modulo benötigt, ist ein oder (|) Ansatz und ist an keiner anderen Stelle auf dieser Seite zu finden.

static dbl  IntPow(dbl st0, int x)  {
    UINT OrMask = UINT_MAX -1;
    dbl  st1=1.0;
    if(0==x) return (dbl)1.0;

    while(1 != x)   {
        if (UINT_MAX == (x|OrMask)) {     //  if LSB is 1...    
        //if(x & 1) {
        //if(x % 2) {
            st1 *= st0;
        }    
        x = x >> 1;  // shift x right 1 bit...  
        st0 *= st0;
    }
    return st1 * st0;
}

Für 300 Millionen Schleifen sind die Benchmark-Timings wie folgt.

3.962 die | und Maskenansatz

4.851 der & Ansatz

5.850 der% -Ansatz

Für Leute, die glauben, dass Theorie oder eine Auflistung der Assemblersprachen Argumente wie diese regelt, sollte dies eine warnende Geschichte sein. Es gibt mehr Dinge im Himmel und auf der Erde, Horatio, als in Ihrer Philosophie geträumt werden.


1
Besser zu verwenden unsigned xals x = x >> 1;das implementierungsdefinierte Verhalten, wenn x < 0. Unklar warum xund OrMaskunterscheiden sich in der Art. Einfach genug, um mit einem while(x)Test neu zu schreiben .
chux

2
Ich frage mich, welchen Compiler Sie zum Benchmarking verwendet haben, da die meisten Compiler intelligent genug sein sollten, um den % 2Fall bitweise zu kompilieren &. Ich habe dies gerade getestet und die Ergebnisse sind völlig gleich (VS2015, Release-Builds mit allen Optimierungen, sowohl x86 als auch x64). Die akzeptierte Antwort gibt dies auch für GCC an (geschrieben 2008).
Lou

2
Das Problem bei diesem Beitrag ist, dass die Annahme, dass ein Bitweiser orschneller als ein andBit ist, auf keiner Plattform / jedem Compiler höchst unwahrscheinlich ist. Selbst wenn es eine so seltsame Plattform / Compiler-Kombination gäbe (und Sie weder diesen noch den Code zur Durchführung des Benchmarks veröffentlicht haben), wäre es eine schlechte Optimierungswette, wenn sich andere Compiler gleich verhalten würden. Wie ich schrieb, frage ich mich, auf welcher Plattform / welchem ​​Compiler dies getestet wurde , da ich fast sicher bin, dass es nicht richtig gemessen wurde.
Lou

2
Sie nicht als Lügner bezeichnen, sondern mit hoher Sicherheit behaupten, dass Sie nicht richtig gemessen haben. Sie müssen mich noch nicht als LKW-Fahrer bezeichnen. Lesen Sie meinen ursprünglichen Kommentar: Ich habe einen Benchmark erstellt, und die Ergebnisse waren erwartungsgemäß in allen drei Fällen völlig gleich (Sicherheit von ~ 3 Sigma, nachdem jeder Test 10 Mal für 500.000 ausgeführt wurde .000 Iterationen). Wenn Sie wirklich eine lange, illustre Karriere haben, treten Sie einen Schritt zurück und überlegen Sie, ob Ihre Behauptungen sinnvoll sind. Veröffentlichen Sie dann den tatsächlichen Code, der für den Benchmark verwendet wurde. Ansonsten ist der Beitrag das, was ich glaube, nur ein Messfehler.
Lou

1

4

Dies ist eine Fortsetzung der Diskussion mit @RocketRoy über seine Antwort , aber es könnte für jeden nützlich sein, der diese Ergebnisse vergleichen möchte.

tl; dr was ich gesehen habe, ist Roys Ansatz ( (0xFFFFFFFF == (x | 0xFFFFFFFE)) nicht vollständig x & 1als der optimiertmod Ansatz , aber in der Praxis sollten die Laufzeiten in allen Fällen gleich ausfallen.

Also habe ich zuerst die kompilierte Ausgabe mit dem Compiler Explorer verglichen :

Getestete Funktionen:

int isOdd_mod(unsigned x) {
    return (x % 2);
}

int isOdd_and(unsigned x) {
    return (x & 1);
}

int isOdd_or(unsigned x) {
    return (0xFFFFFFFF == (x | 0xFFFFFFFE));
}   

CLang 3.9.0 mit -O3:

isOdd_mod(unsigned int):                          # @isOdd_mod(unsigned int)
        and     edi, 1
        mov     eax, edi
        ret

isOdd_and(unsigned int):                          # @isOdd_and(unsigned int)
        and     edi, 1
        mov     eax, edi
        ret

isOdd_or(unsigned int):                           # @isOdd_or(unsigned int)
        and     edi, 1
        mov     eax, edi
        ret

GCC 6.2 mit -O3:

isOdd_mod(unsigned int):
        mov     eax, edi
        and     eax, 1
        ret

isOdd_and(unsigned int):
        mov     eax, edi
        and     eax, 1
        ret

isOdd_or(unsigned int):
        or      edi, -2
        xor     eax, eax
        cmp     edi, -1
        sete    al
        ret

Hut ab vor CLang, es wurde klar, dass alle drei Fälle funktional gleich sind. Roys Ansatz ist jedoch in GCC nicht optimiert, so YMMV.

Ähnlich verhält es sich mit Visual Studio. Bei der Überprüfung der Demontage Release x64 (VS2015) auf diese drei Funktionen konnte ich feststellen, dass der Vergleichsteil für die Fälle "mod" und "und" gleich und für den Fall "Roy" oder "etwas größer" ist:

// x % 2
test bl,1  
je (some address) 

// x & 1
test bl,1  
je (some address) 

// Roy's bitwise or
mov eax,ebx  
or eax,0FFFFFFFEh  
cmp eax,0FFFFFFFFh  
jne (some address)

Nach dem Ausführen eines tatsächlichen Benchmarks zum Vergleichen dieser drei Optionen (einfacher Mod, bitweiser oder bitweiser und) waren die Ergebnisse jedoch vollständig gleich (wieder Visual Studio 2005 x86 / x64, Release Build, kein Debugger beigefügt).

Release Assembly verwendet die testAnweisung für andund modFälle, während Roys Fall die verwendetcmp eax,0FFFFFFFFh Ansatz verwendet, aber er ist stark abgerollt und optimiert, sodass es in der Praxis keinen Unterschied gibt.

Meine Ergebnisse nach 20 Läufen (i7 3610QM, Windows 10-Energieplan auf Hochleistung eingestellt):

[Test: Plain mod 2] DURCHSCHNITTLICHE ZEIT: 689,29 ms (Relativer Diff.: + 0,000%)
[Test: Bitweise oder] DURCHSCHNITTLICHE ZEIT: 689,63 ms (Relativer Diff.: + 0,048%)
[Test: Bitweise und] DURCHSCHNITTLICHE ZEIT: 687,80 ms (relativer Unterschied: -0,217%)

Der Unterschied zwischen diesen Optionen beträgt weniger als 0,3%, daher ist es ziemlich offensichtlich, dass die Baugruppe in allen Fällen gleich ist.

Hier ist der Code, wenn jemand es versuchen möchte, mit einer Einschränkung, dass ich ihn nur unter Windows getestet habe (überprüfen Sie die #if LINUXBedingung für die get_timeDefinition und implementieren Sie ihn bei Bedarf, entnommen aus dieser Antwort ).

#include <stdio.h>

#if LINUX
#include <sys/time.h>
#include <sys/resource.h>
double get_time()
{
    struct timeval t;
    struct timezone tzp;
    gettimeofday(&t, &tzp);
    return t.tv_sec + t.tv_usec*1e-6;
}
#else
#include <windows.h>
double get_time()
{
    LARGE_INTEGER t, f;
    QueryPerformanceCounter(&t);
    QueryPerformanceFrequency(&f);
    return (double)t.QuadPart / (double)f.QuadPart * 1000.0;
}
#endif

#define NUM_ITERATIONS (1000 * 1000 * 1000)

// using a macro to avoid function call overhead
#define Benchmark(accumulator, name, operation) { \
    double startTime = get_time(); \
    double dummySum = 0.0, elapsed; \
    int x; \
    for (x = 0; x < NUM_ITERATIONS; x++) { \
        if (operation) dummySum += x; \
    } \
    elapsed = get_time() - startTime; \
    accumulator += elapsed; \
    if (dummySum > 2000) \
        printf("[Test: %-12s] %0.2f ms\r\n", name, elapsed); \
}

void DumpAverage(char *test, double totalTime, double reference)
{
    printf("[Test: %-12s] AVERAGE TIME: %0.2f ms (Relative diff.: %+6.3f%%)\r\n",
        test, totalTime, (totalTime - reference) / reference * 100.0);
}

int main(void)
{
    int repeats = 20;
    double runningTimes[3] = { 0 };
    int k;

    for (k = 0; k < repeats; k++) {
        printf("Run %d of %d...\r\n", k + 1, repeats);
        Benchmark(runningTimes[0], "Plain mod 2", (x % 2));
        Benchmark(runningTimes[1], "Bitwise or", (0xFFFFFFFF == (x | 0xFFFFFFFE)));
        Benchmark(runningTimes[2], "Bitwise and", (x & 1));
    }

    {
        double reference = runningTimes[0] / repeats;
        printf("\r\n");
        DumpAverage("Plain mod 2", runningTimes[0] / repeats, reference);
        DumpAverage("Bitwise or", runningTimes[1] / repeats, reference);
        DumpAverage("Bitwise and", runningTimes[2] / repeats, reference);
    }

    getchar();

    return 0;
}

Ich glaube, Sie haben die Hauptsünde des Benchmarking begangen. Erstellen eines solchen, das so spezifisch ist, dass es keine reale Umgebung darstellt. Sehen Sie sich Ihre Assemblersprache an und stellen Sie fest, wie wenige Register Sie verwenden. Gute Noten für Aufwand, aber diese Ergebnisse halten in der realen Verarbeitung nicht stand.

@RocketRoy: Da alle Ausgänge in allen drei Fällen genau gleich sind (in einem Fall etwas schlechter für Ihr Programm), ist es mir egal, wie viele Register verwendet wurden. Sie können jedoch auch hier ein Beispielprogramm / eine Beispielumgebung erstellen und veröffentlichen, die den Compiler verwirrt, in einem der Fälle eine optimierte Assembly zu erstellen, wobei alle anderen Faktoren gleich sind.
Lou

Ich mag immer übermütige Programmierer. Es ist eine gute Eigenschaft für einen Programmierer, aber in einem komplexeren, realen Programm ist meine Methode besser als Ihre, da der Compiler mehr Möglichkeiten hat, das Problem zu lösen, sodass sich Anweisungen (auf Intel-Architekturen) überlappen und bessere Ergebnisse erzielen . Nur sehr wenige erfahrene Programmierer mit guter Benchmarking-Erfahrung würden Ihren Benchmark bevorzugen, aber machen Sie weiter so und denken Sie daran, Ihre Benchmarks erneut auszuführen, wenn neue Chip-Releases herauskommen. Die Dinge ändern sich im Laufe der Zeit.

3

Ich weiß, dass dies nur syntaktischer Zucker ist und nur in .net anwendbar ist, aber was ist mit der Erweiterungsmethode ...

public static class RudiGroblerExtensions
{
    public static bool IsOdd(this int i)
    {
        return ((i % 2) != 0);
    }
}

Jetzt können Sie Folgendes tun

int i = 5;
if (i.IsOdd())
{
    // Do something...
}

1
Schöner Code. Schade, dass behauptet wird, dass 2 ungerade ist und 3 nicht.
Anthony

Ups, sorry ... meine Logik ist falsch
herum

3

In der Kategorie "kreativ aber verwirrend" biete ich an:

int isOdd(int n) { return n ^ n * n ? isOdd(n * n) : n; }

Eine für Microsoft C ++ spezifische Variante dieses Themas:

__declspec(naked) bool __fastcall isOdd(const int x)
{
    __asm
    {
        mov eax,ecx
        mul eax
        mul eax
        mul eax
        mul eax
        mul eax
        mul eax
        ret
    }
}

2

Die bitweise Methode hängt von der inneren Darstellung der Ganzzahl ab. Modulo funktioniert überall dort, wo es einen Modulo-Operator gibt. Beispielsweise verwenden einige Systeme tatsächlich die Low-Level-Bits zum Markieren (wie dynamische Sprachen), sodass das rohe x & 1 in diesem Fall nicht wirklich funktioniert.


2

IsOdd (int x) {return true; }}

Korrektheitsnachweis - Betrachten Sie die Menge aller positiven ganzen Zahlen und nehmen Sie an, dass es eine nicht leere Menge von ganzen Zahlen gibt, die nicht ungerade sind. Da positive ganze Zahlen gut geordnet sind, gibt es eine kleinste nicht ungerade Zahl, die an sich ziemlich ungerade ist, so dass diese Zahl eindeutig nicht in der Menge enthalten sein kann. Daher darf dieser Satz nicht leer sein. Wiederholen Sie diesen Vorgang für negative Ganzzahlen, außer suchen Sie nach der größten nicht ungeraden Zahl.


2

Tragbar:

i % 2 ? odd : even;

Unportable:

i & 1 ? odd : even;

i << (BITS_PER_INT - 1) ? odd : even;

2

Wie einige Leute geschrieben haben, gibt es zahlreiche Möglichkeiten, dies zu tun. Laut dieser Website ist der Moduloperator der schnellste Weg:

if (x % 2 == 0)
               total += 1; //even number
        else
               total -= 1; //odd number

Hier ist jedoch ein anderer Code, der als Benchmark markiert wurde vom Autor wurde und langsamer lief als die oben beschriebene übliche Moduloperation:

if ((x & 1) == 0)
               total += 1; //even number
        else
               total -= 1; //odd number

System.Math.DivRem((long)x, (long)2, out outvalue);
        if ( outvalue == 0)
               total += 1; //even number
        else
               total -= 1; //odd number

if (((x / 2) * 2) == x)
               total += 1; //even number
        else
               total -= 1; //odd number

if (((x >> 1) << 1) == x)
               total += 1; //even number
        else
               total -= 1; //odd number

        while (index > 1)
               index -= 2;
        if (index == 0)
               total += 1; //even number
        else
               total -= 1; //odd number

tempstr = x.ToString();
        index = tempstr.Length - 1;
        //this assumes base 10
        if (tempstr[index] == '0' || tempstr[index] == '2' || tempstr[index] == '4' || tempstr[index] == '6' || tempstr[index] == '8')
               total += 1; //even number
        else
               total -= 1; //odd number

Wie viele Leute wussten überhaupt von der Math.System.DivRem- Methode oder warum sollten sie sie verwenden?



1

Um die bitweise Operatormethode für diejenigen von uns näher zu erläutern, die während unseres Studiums nicht viel Boolesche Algebra durchgeführt haben, finden Sie hier eine Erklärung. Wahrscheinlich nicht sehr nützlich für das OP, aber ich wollte klarstellen, warum NUMBER & 1 funktioniert.

Bitte beachten Sie, dass die Darstellung negativer Zahlen, wie oben beantwortet, die Funktionsweise dieser Methode beeinträchtigen kann. Tatsächlich kann es sogar die Modulo-Operator-Methode brechen, da sich jede Sprache darin unterscheiden kann, wie sie mit negativen Operanden umgeht.

Wenn Sie jedoch wissen, dass NUMBER immer positiv ist, funktioniert dies gut.

Wie Tooony oben betonte, ist nur die letzte Ziffer in Binär (und Denar) wichtig.

Ein boolesches logisches UND-Gatter schreibt vor, dass beide Eingänge eine 1 (oder Hochspannung) sein müssen, damit 1 zurückgegeben wird.

1 & 0 = 0.

0 & 1 = 0.

0 & 0 = 0.

1 & 1 = 1.

Wenn Sie eine Zahl als Binärzahl darstellen (ich habe hier eine 8-Bit-Darstellung verwendet), haben ungerade Zahlen am Ende 1, gerade Zahlen 0.

Beispielsweise:

1 = 00000001

2 = 00000010

3 = 00000011

4 = 00000100

Wenn Sie eine beliebige Zahl nehmen und bitweise UND (& in Java) mit 1 verwenden, wird entweder 00000001 = 1 zurückgegeben, was bedeutet, dass die Zahl ungerade ist. Oder 00000000 = 0, was bedeutet, dass die Zahl gerade ist.

Z.B

Ist ungerade?

1 & 1 =

00000001 &

00000001 =

00000001 <- Ungerade

2 & 1 =

00000010 &

00000001 =

00000000 <- Gerade

54 & 1 =

00000001 &

00110110 =

00000000 <- Gerade

Deshalb funktioniert das:

if(number & 1){

   //Number is odd

} else {

   //Number is even
}

Entschuldigung, wenn dies überflüssig ist.


1

Zahl Null Parität | Null http://tinyurl.com/oexhr3k

Python-Codesequenz.

# defining function for number parity check
def parity(number):
    """Parity check function"""
    # if number is 0 (zero) return 'Zero neither ODD nor EVEN',
    # otherwise number&1, checking last bit, if 0, then EVEN, 
    # if 1, then ODD.
    return (number == 0 and 'Zero neither ODD nor EVEN') \
            or (number&1 and 'ODD' or 'EVEN')

# cycle trough numbers from 0 to 13 
for number in range(0, 14):
    print "{0:>4} : {0:08b} : {1:}".format(number, parity(number))

Ausgabe:

   0 : 00000000 : Zero neither ODD nor EVEN
   1 : 00000001 : ODD
   2 : 00000010 : EVEN
   3 : 00000011 : ODD
   4 : 00000100 : EVEN
   5 : 00000101 : ODD
   6 : 00000110 : EVEN
   7 : 00000111 : ODD
   8 : 00001000 : EVEN
   9 : 00001001 : ODD
  10 : 00001010 : EVEN
  11 : 00001011 : ODD
  12 : 00001100 : EVEN
  13 : 00001101 : ODD

@ el.pescado, danke. Wenn Null gerade ist, wie viele Paare hat es?

@ el.pescado, Ok, ich stimme dir zu. Wenn wir dann ein wenig nachdenken, warum teilen wir uns dann auf 2 (zwei)? Was wollen wir wissen, wenn wir uns in zwei teilen? Warum nicht auf 3 oder 5 usw. teilen?

@ el.pescado Dieser Wikipedia-Artikel Parity of Zero ist falsch. Viele Leute haben sich von diesem Artikel täuschen lassen. Denken Sie vor Wink nach.

1
Du hast recht. Jetzt, wo ich andere Antworten gelesen habe, fand ich deine am umfassendsten :)
el.pescado

@ el.pescado. Danke. :) Jetzt bist du Zeros bester Freund. (Umarmung)

1
I execute this code for ODD & EVEN:

#include <stdio.h>
int main()
{
    int number;
    printf("Enter an integer: ");
    scanf("%d", &number);

    if(number % 2 == 0)
        printf("%d is even.", number);
    else
        printf("%d is odd.", number);
}

0

Zur Diskussion ...

Sie müssen nur die letzte Ziffer einer bestimmten Zahl betrachten, um festzustellen, ob sie gerade oder ungerade ist. Signiert, nicht signiert, positiv, negativ - diesbezüglich sind sie alle gleich. Das sollte also rundum funktionieren: -

void tellMeIfItIsAnOddNumberPlease(int iToTest){
  int iLastDigit;
  iLastDigit = iToTest - (iToTest / 10 * 10);
  if (iLastDigit % 2 == 0){
    printf("The number %d is even!\n", iToTest);
  } else {
    printf("The number %d is odd!\n", iToTest);
  }
}

Der Schlüssel hier befindet sich in der dritten Codezeile. Der Divisionsoperator führt eine ganzzahlige Division durch, sodass dem Ergebnis der Bruchteil des Ergebnisses fehlt. So ergibt beispielsweise 222/10 22. Dann multiplizieren Sie es erneut mit 10 und Sie haben 220. Subtrahieren Sie das vom Original 222 und Sie erhalten 2, was magisch die gleiche Zahl wie die letzte Ziffer in der ursprünglichen Zahl ist. ;-) Die Klammern erinnern uns an die Reihenfolge, in der die Berechnung durchgeführt wird. Führen Sie zuerst die Division und die Multiplikation durch und subtrahieren Sie dann das Ergebnis von der ursprünglichen Zahl. Wir könnten sie weglassen, da die Priorität für Division und Multiplikation höher ist als für Subtraktion, aber dies gibt uns "besser lesbaren" Code.

Wir könnten alles völlig unlesbar machen, wenn wir wollten. Für einen modernen Compiler würde es keinen Unterschied machen:

printf("%d%s\n",iToTest,0==(iToTest-iToTest/10*10)%2?" is even":" is odd");

Aber es würde die Pflege des Codes in Zukunft erschweren. Stellen Sie sich vor, Sie möchten den Text für ungerade Zahlen in "ist nicht gerade" ändern. Dann möchte später jemand anderes herausfinden, welche Änderungen Sie vorgenommen haben, und einen SVN-Diff oder ähnliches durchführen ...

Wenn Sie sich nicht um die Portabilität, sondern um die Geschwindigkeit sorgen, können Sie sich das niedrigstwertige Bit ansehen. Wenn dieses Bit auf 1 gesetzt ist, ist es eine ungerade Zahl, wenn es 0 ist, ist es eine gerade Zahl. Auf einem kleinen Endian-System wie Intels x86-Architektur wäre es ungefähr so: -

if (iToTest & 1) {
  // Even
} else {
  // Odd
}

Was genau ist falsch daran, nur iToTest% 2 == 0 zu machen? Sie verschwenden eine Abteilung, die die letzte Ziffer extrahiert, sodass Ihre doppelt so langsam ist, wie sie sein muss.
Freiraum

@freespace: Ich verschwende mehr als das, nicht wahr? :-) Eine Multiplikation und auch eine Subtraktion. Aber was zwischen den beiden Lösungen am effizientesten ist, wage ich nicht zu sagen. Ich habe nie behauptet, dies sei die schnellste Lösung, ganz im Gegenteil, wenn Sie die erste Zeile meines Beitrags noch einmal lesen.
Tooony

@Tooony, ah, mein Humor Hut fiel ab. Es ist jetzt wieder formell: D Tut mir leid :)
Freespace

0

Wenn Sie effizient sein möchten, verwenden Sie bitweise Operatoren ( x & 1). Wenn Sie jedoch lesbar sein möchten, verwenden Sie modulo 2 ( x % 2).


-1: Wenn Sie effizient sein möchten, verwenden Sie eine der beiden. Wenn Sie möchten, dass es portabel ist, verwenden Sie %. Wenn Sie möchten, dass es lesbar ist, verwenden Sie %. Hmmm, ich sehe hier ein Muster.
Thomas Eding

@ Trinithis, es gibt kein Muster und diese Lösung ist viel besser als deine.
Subs

0

Gerade oder ungerade zu überprüfen ist eine einfache Aufgabe.

Wir wissen, dass jede Zahl, die genau durch 2 teilbar ist, eine gerade Zahl ist, sonst eine ungerade.

Wir müssen nur die Teilbarkeit einer beliebigen Zahl überprüfen und zur Überprüfung der Teilbarkeit verwenden wir den %Operator

Überprüfen Sie gerade ungerade mit, wenn sonst

if(num%2 ==0)  
{
    printf("Even");
}
else
{
    printf("Odd");
}

C-Programm, um gerade oder ungerade zu überprüfen, wenn sonst

Verwenden des Bedingten / Ternären Operators

(num%2 ==0) printf("Even") : printf("Odd");

C-Programm zum Überprüfen von geraden oder ungeraden Werten mit dem bedingten Operator .

Verwenden des bitweisen Operators

if(num & 1)  
{
    printf("Odd");
}
else 
{
    printf("Even");
}

und wo genau ist der ternäre Operator?
Beyondo

0

+ 66% schneller>!(i%2) / i%2 == 0

int isOdd(int n)
{
    return n & 1;
}

Der Code überprüft das letzte Bit der Ganzzahl, wenn es in Binär 1 ist

Erläuterung

Binary  :   Decimal
-------------------
0000    =   0
0001    =   1
0010    =   2
0011    =   3
0100    =   4
0101    =   5
0110    =   6
0111    =   7
1000    =   8
1001    =   9
and so on...

Beachten Sie, dass das Bit ganz rechts für ungerade Zahlen immer 1 ist.

Der Operator & bitwise AND überprüft das Bit ganz rechts in unserer Rückgabezeile , wenn es 1 ist

Betrachten Sie es als wahr und falsch

Wenn wir n mit 1 vergleichen , bedeutet dies 0001binär (die Anzahl der Nullen spielt keine Rolle).
Stellen wir uns dann vor, wir hätten die ganze Zahl n mit einer Größe von 1 Byte.

Es würde durch 8-Bit / 8-Binärziffern dargestellt.

Wenn der int n war 7 und wir vergleichen es mit 1 , Es ist wie

7 (1-byte int)|    0  0  0  0    0  1  1  1
       &
1 (1-byte int)|    0  0  0  0    0  0  0  1
********************************************
Result        |    F  F  F  F    F  F  F  T

Welches F steht für falsch und T für wahr.

Es wird nur das am weitesten rechts stehende Bit verglichen, wenn beide wahr sind. Also, automatisch 7 & 1ist T rue.

Was ist, wenn ich das Bit ganz rechts überprüfen möchte?

Ändern Sie einfach n & 1, n & 2welche 2 darstellt0010 in Binär darstellt und so weiter.

Ich schlage vor, die hexadezimale Notation zu verwenden, wenn Sie Anfänger in bitweisen Operationen sind
return n & 1;>> return n & 0x01;.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.