Ich muss XOR-Zahlen von 1 bis N, gibt es eine direkte Formel dafür?
Zum Beispiel wenn N = 6
dann 1^2^3^4^5^6 = 7
Ich möchte es tun, ohne eine Schleife zu verwenden, also brauche ich eine O (1) -Formel (falls vorhanden)
Debanjan
Ich muss XOR-Zahlen von 1 bis N, gibt es eine direkte Formel dafür?
Zum Beispiel wenn N = 6
dann 1^2^3^4^5^6 = 7
Ich möchte es tun, ohne eine Schleife zu verwenden, also brauche ich eine O (1) -Formel (falls vorhanden)
Alexej Malistow
Deine Formel ist N & (N % 2 ? 0 : ~0) | ( ((N & 2)>>1) ^ (N & 1) )
:
int main()
{
int S = 0;
for (int N = 0; N < 50; ++N) {
S = (S^N);
int check = N & (N % 2 ? 0 : ~0) | ( ((N & 2)>>1) ^ (N & 1) );
std::cout << "N = " << N << ": " << S << ", " << check << std::endl;
if (check != S) throw;
}
return 0;
}
Ausgabe:
N = 0: 0, 0 N = 1: 1, 1 N = 2: 3, 3
N = 3: 0, 0 N = 4: 4, 4 N = 5: 1, 1
N = 6: 7, 7 N = 7: 0, 0 N = 8: 8, 8
N = 9: 1, 1 N = 10: 11, 11 N = 11: 0, 0
N = 12: 12, 12 N = 13: 1, 1 N = 14: 15, 15
N = 15: 0, 0 N = 16: 16, 16 N = 17: 1, 1
N = 18: 19, 19 N = 19: 0, 0 N = 20: 20, 20
N = 21: 1, 1 N = 22: 23, 23 N = 23: 0, 0
N = 24: 24, 24 N = 25: 1, 1 N = 26: 27, 27
N = 27: 0, 0 N = 28: 28, 28 N = 29: 1, 1
N = 30: 31, 31 N = 31: 0, 0 N = 32: 32, 32
N = 33: 1, 1 N = 34: 35, 35 N = 35: 0, 0
N = 36: 36, 36 N = 37: 1, 1 N = 38: 39, 39
N = 39: 0, 0 N = 40: 40, 40 N = 41: 1, 1
N = 42: 43, 43 N = 43: 0, 0 N = 44: 44, 44
N = 45: 1, 1 N = 46: 47, 47 N = 47: 0, 0
N = 48: 48, 48 N = 49: 1, 1 N = 50: 51, 51
Erläuterung:
Low-Bit ist XOR zwischen Low-Bit und nächstem Bit.
Für jedes Bit außer Low-Bit gilt:
Für den Fall von ungeradem N ist das Ergebnis also immer 0 oder 1.
(N % 2 ? 0 : ~0)
kann ausgedrückt werden als ((N&1) - 1UL)
solange Sie verwenden unsigned
Integer also Wrap-Around aus 0
zu -1UL
ist wohldefiniert, dh alle Bits gesetzt, like ~0UL
. Dies kann Compilern bei der Optimierung helfen, insbesondere weil N&1
wird bereits in einem anderen Teil des Ausdrucks benötigt.
– Peter Cordes
23. Mai 2018 um 21:38 Uhr
Sieht aus wie (N & ((N&1)-1UL)) | ( ((N & 2)>>1) ^ (N & 1) )
kompiliert gut für x86 mit aktuellem gcc/clang. Siehe asm-Ausgabe im Compiler Explorer: godbolt.org/g/bsfMsS. (Ich habe 3 Versionen davon in Inline-Funktionen mit ein paar tmp-Variablen eingefügt, um sie besser lesbar zu machen.)
– Peter Cordes
23. Mai 2018 um 22:16 Uhr
Einschließlich Code aus einigen anderen Antworten: godbolt.org/g/PJHFwS (und AArch64-Ausgabe sowie x86-64)
– Peter Cordes
23. Mai 2018 um 22:29 Uhr
Nikita Rybak
bearbeiten
GSerg Hat eine Formel ohne Schleifen gepostet, aber aus irgendeinem Grund gelöscht (jetzt nicht gelöscht). Die Formel ist vollkommen gültig (abgesehen von einem kleinen Fehler). Hier ist die C++-ähnliche Version.
if n % 2 == 1 {
result = (n % 4 == 1) ? 1 : 0;
} else {
result = (n % 4 == 0) ? n : n + 1;
}
Man kann es durch Induktion beweisen, indem man alle Erinnerungen an die Division durch überprüft 4
. Obwohl, keine Ahnung, wie Sie darauf kommen können, ohne Ausgabe zu erzeugen und Regelmäßigkeit zu sehen.
Bitte erläutern Sie Ihre Vorgehensweise etwas genauer.
Da bei der xor-Operation jedes Bit unabhängig ist, können Sie sie separat berechnen.
Auch, wenn Sie sich das k-te Bit der Zahl ansehen 0..n
, es wird ein Muster bilden. ZB Zahlen von 0 bis 7 in binärer Form.
000
001
010
011
100
101
110
111
Sie sehen, dass es für das k-te Bit (k beginnt bei 0) gibt 2^k
Nullen, 2^k
diejenigen, dann 2^k
wieder Nullen usw.
Daher können Sie für jedes Bit berechnen, wie viele Einsen es gibt, ohne tatsächlich alle Zahlen von 1 bis n durchzugehen.
Bsp. für k = 2
es gibt wiederholte Blöcke von 2^2 == 4
Nullen und Einsen. Dann,
int ones = (n / 8) * 4; // full blocks
if (n % 8 >= 4) { // consider incomplete blocks in the end
ones += n % 8 - 3;
}
GSerg hat es gelöscht, weil es falsch war (jedes Mal um 1). Ich habe es tatsächlich mehrmals gelöscht und jedes Mal etwas repariert 🙂
– GSerg
14. Oktober 2010 um 11:30 Uhr
Ich habe die Frage vor dem Login gepostet, daher kann ich sie jetzt nicht offiziell akzeptieren, aber ich könnte glauben, dass Ihre Antwort die beste ist 🙂
– Quixotisch
14. Oktober 2010 um 11:33 Uhr
Ordentlich und unkompliziert
– Spiralmond
2. Juni 2015 um 6:28 Uhr
GSerg
Für ungerade N
das Ergebnis ist entweder 1
oder 0
(zyklisch, 0 für N=3
1 für N=5
0 für N=7
etc.)
Für sogar N
das Ergebnis ist entweder N
oder N+1
(zyklisch, N+1 für N=2
N für N=4
N+1 für N=6
N für N=8
etc).
Pseudocode:
if (N mod 2) = 0
if (N mod 4) = 0 then r = N else r = N+1
else
if (N mod 4) = 1 then r = 1 else r = 0
Ja, es stellt sich als richtig heraus. Neugierig, was der mathematische Hintergrund dahinter ist. 🙂
– Schlüsselschnecke
14. Oktober 2010 um 11:24 Uhr
Sollte nicht sein (N mod 4) = 1
in der zweiten Zeile?
– Usta
14. Oktober 2010 um 11:32 Uhr
+1 Gute Arbeit! Das wird mir beibringen, ein Beispiel der Sequenz zu generieren, bevor ich sie “löse” 🙂
– Nikita Rybak
14. Oktober 2010 um 11:36 Uhr
@usta: Nein, ich suche nach (N mod 2) = 0
wenn der erste Platz (im Gegensatz zur C++-Version von Nikita Rybak
– GSerg
14. Oktober 2010 um 11:36 Uhr
Sie hatten ursprünglich if (N mod 4) = 3 then r = 1 else r = 0
was ich kommentiert habe.
– Usta
14. Oktober 2010 um 11:48 Uhr
Saurabh Manchanda
Nehmen wir an, die Funktion, die alle Werte von 1 bis N XOR-verknüpft, ist dann XOR(N).
XOR(1) = 000 1 = 0 1 ( The 0 is the dec of bin 000) XOR(2) = 001 1 = 1 1 XOR(3) = 000 0 = 0 0 XOR(4) = 010 0 = 2 0 XOR(5) = 000 1 = 0 1 XOR(6) = 011 1 = 3 1 XOR(7) = 000 0 = 0 0 XOR(8) = 100 0 = 4 0 XOR(9) = 000 1 = 0 1 XOR(10)= 101 1 = 5 1 XOR(11)= 000 0 = 0 0 XOR(12)= 110 0 = 6 0
Ich hoffe, Sie können das Muster erkennen. Es sollte auch für andere Nummern ähnlich sein.
ruslik
Versuche dies:
Das LSB wird jedes Mal umgeschaltet, wenn das N ungerade ist, also können wir das sagen
rez & 1 == (N & 1) ^ ((N >> 1) & 1)
Das gleiche Muster kann für die restlichen Bits beobachtet werden. Jedes Mal die Bits B
und B+1
(ab LSB) ein N
wird anders sein, bisschen B
im Ergebnis gesetzt werden soll.
Das Endergebnis lautet also (einschließlich N): rez = N ^ (N >> 1)
EDIT: Entschuldigung, es war falsch. die richtige Antwort:
für ungerade N: rez = (N ^ (N >> 1)) & 1
für gerade N: rez = (N & ~1) | ((N ^ (N >> 1)) & 1)
Wofür steht rez? Wenn die endgültige Antwort dann nicht richtig ist, denke ich 🙂
– Quixotisch
14. Oktober 2010 um 11:17 Uhr
1^2^3==0
aber 3^(3>>1)==2
– Usta
14. Oktober 2010 um 11:27 Uhr
usta
Tolle Antwort von Alexey Malistov! Eine Variation seiner Formel: n & 1 ? (n & 2) >> 1 ^ 1 : n | (n & 2) >> 1
oder gleichwertig n & 1 ? !(n & 2) : n | (n & 2) >> 1
.
Wofür steht rez? Wenn die endgültige Antwort dann nicht richtig ist, denke ich 🙂
– Quixotisch
14. Oktober 2010 um 11:17 Uhr
1^2^3==0
aber 3^(3>>1)==2
– Usta
14. Oktober 2010 um 11:27 Uhr
Willem van Onsem
Diese Methode vermeidet die Verwendung von Bedingungen F(N)=(N&((N&1)-1))|((N&1)^((N&3)>>1)
F(N)= (N&(b0-1)) | (b0^b1)
Wenn Sie sich das XOR der ersten paar Zahlen ansehen, erhalten Sie:
N | F(N)
------+------
0001 | 0001
0010 | 0011
0011 | 0000
0100 | 0100
0101 | 0001
0110 | 0111
0111 | 0000
1000 | 1000
1001 | 0001
Hoffentlich erkennen Sie das Muster:
wenn
N mod 4 = 1
alsF(N)=1
wenn
N mod 4 = 3
alsF(N)=0
wenn
N mod 4 = 0
alsF(N)=N
wenn
N mod 4 = 2
alsF(N)=N
aber mit dem ersten bisschen wie1
AlsoN|1
Der knifflige Teil besteht darin, dies in einer Anweisung ohne Bedingungen zu erhalten, um die Logik zu erklären, mit der ich dies getan habe.
Nehmen Sie die ersten 2 signifikanten Bits von N, nennen Sie sie:
b0
und b1
und diese erhält man mit:
b0 = N&1
b1 = N&3>>1
Beachten Sie, dass wenn b0 == 1
wir müssen 0
alle Bits, aber wenn nicht, bleiben alle Bits mit Ausnahme des ersten Bits gleich. Wir können dieses Verhalten tun, indem wir:
N & (b0-1)
: das funktioniert wegen dem 2er-Komplement, -1
ist gleich einer Zahl, bei der alle Bits auf gesetzt sind 1
und 1-1=0
also wann b0=1
das führt zu F(N)=0
.. das ist also der erste Teil der Funktion:
F(N)= (N&(b0-1))...
jetzt wird dies für für funktionieren N mod 4 == 3
und 0
für die anderen 2 Fälle schauen wir uns nur an b1
, b0
und F(N)0
:
b0|b1|F(N)0
--+--+-----
1| 1| 0
0| 0| 0
1| 0| 1
0| 1| 1
Ok, hoffentlich kommt dir diese Wahrheitstabelle bekannt vor! es ist b0 XOR b1 (b1^b0)
. Jetzt, da wir wissen, wie wir das letzte Bit bekommen, lassen Sie das auf unsere Funktion setzen:
F(N)=(N&(b0-1))|b0^b1
und los geht’s, eine Funktion ohne Verwendung von Bedingungen. Auch dies ist nützlich, wenn Sie das XOR von positiven Zahlen a bis b berechnen möchten. du kannst tun:
F(a) XOR F(b)
.
Sie sollten erklären, warum dies funktioniert. Bei SO geht es nicht darum, eine Copy-Paste-Antwort zu geben, sondern eine Erklärung bereitzustellen, damit die Menschen in Zukunft ihre eigenen Probleme lösen können.
– Willem Van Onsem
26. März 2015 um 21:06 Uhr
danke CommuSoft Ich werde ab jetzt alles erklären, was ich hier anfasse
– Alex
1. April 2015 um 22:33 Uhr
wow, danke, dass du diese Formatierung hinzugefügt hast! wusste gar nicht, dass man das kann. Ha
– Alex
2. April 2015 um 22:42 Uhr
Ich denke, Sie müssten jedes Bit der Reihe nach berücksichtigen, damit es mindestens O (log N) wäre. Warum brauchen Sie eine O(1)-Lösung?
– Rup
14. Oktober 2010 um 10:55 Uhr
Bitte erläutern Sie Ihre Herangehensweise etwas genauer.
– Quixotisch
14. Oktober 2010 um 10:57 Uhr
@Rup: Beachten Sie, dass alle arithmetischen Operationen grundsätzlich
O(log n)
in dem Sinne, dass, wenn Sie mit Bigints und nicht mit Wörtern mit fester Größe arbeiten, sie nehmenO(log n)
Zeit. Aber selbst mit Bigints gibt diese Formel einO(1)
Lösung für die xor-Summe (vorausgesetzt, Sie können die Eingabe überschreiben, um sie als Ausgabe zu verwenden, oder optional eine Konstante 0/1 als Ausgabe zurückgeben).– R.. GitHub HÖR AUF, EIS ZU HELFEN
14. Oktober 2010 um 13:27 Uhr
Eigentlich lag ich etwas daneben. Ich dachte, es wäre
N+1
eher im Sinne von “plus bedeutet xor” als wirklichN+1
. Letzteres kann niemals seinO(1)
. Nun ja…– R.. GitHub HÖR AUF, EIS ZU HELFEN
14. Oktober 2010 um 13:32 Uhr
@R. sicher – meine Erwartung zum Zeitpunkt des Kommentars (bevor es Antworten oder Formeln gab) war, dass dies nicht in einem einzigen Ausdruck berechnet werden konnte und Sie eine separate Berechnung benötigen würden, um jedes Bit in der Antwort einzeln zu bestimmen, dh mehr explizit log_2 N. Aber mehr durch die Beobachtung von Mustern als durch Mathematik sind wir schließlich doch bei einer Formel gelandet.
– Rup
14. Oktober 2010 um 13:35 Uhr