Kann mir jemand erklären, wie das XOR-Austauschen von zwei Variablen ohne temporäre Variable funktioniert?
void xorSwap (int *x, int *y)
{
if (x != y) {
*x ^= *y;
*y ^= *x;
*x ^= *y;
}
}
Ich verstehe, WAS es tut, aber kann mich jemand durch die Logik führen, wie es funktioniert?
language-agnostic
bit-manipulation
xor
mmcdole
quelle
quelle
Antworten:
Sie können sehen, wie es funktioniert, indem Sie die Ersetzung durchführen:
Ersetzen,
Weil xor vollständig assoziativ und kommutativ ist:
Da
x xor x == 0
für jedes x,Und da
x xor 0 == x
für jedes x,Und der Tausch ist erledigt.
quelle
y2
, kaum widersteheny1
. Es löst mich aus, dass Sie habenx0
undx1
danny0
und verwendeny2
. : -]Andere Leute haben es erklärt, jetzt möchte ich erklären, warum es eine gute Idee war, aber jetzt nicht.
Früher, als wir einfache Einzelzyklus- oder Mehrzyklus-CPUs hatten, war es billiger, diesen Trick zu verwenden, um kostspielige Speicherstörungen oder das Verschütten von Registern auf den Stapel zu vermeiden. Wir haben jetzt jedoch stattdessen CPUs mit massiven Pipelines. Die Pipeline des P4 reichte von 20 bis 31 (oder so) Stufen in ihren Pipelines, wobei jede Abhängigkeit zwischen Lesen und Schreiben in ein Register dazu führen könnte, dass das Ganze ins Stocken gerät. Der xor-Swap weist einige sehr starke Abhängigkeiten zwischen A und B auf, die eigentlich gar nicht wichtig sind, aber die Pipeline in der Praxis blockieren. Eine blockierte Pipeline verursacht einen langsamen Codepfad. Wenn sich dieser Swap in Ihrer inneren Schleife befindet, bewegen Sie sich sehr langsam.
In der Regel kann Ihr Compiler herausfinden, was Sie wirklich tun möchten, wenn Sie einen Swap mit einer temporären Variablen durchführen, und ihn zu einer einzelnen XCHG-Anweisung kompilieren. Die Verwendung des xor-Swaps erschwert es dem Compiler erheblich, Ihre Absicht zu erraten, und ist daher weniger wahrscheinlich, dass er sie richtig optimiert. Ganz zu schweigen von der Code-Wartung usw.
quelle
Ich denke lieber grafisch als numerisch darüber nach.
Angenommen, Sie beginnen mit x = 11 und y = 5. In der Binärdatei (und ich werde eine hypothetische 4-Bit-Maschine verwenden) sind hier x und y
Für mich ist XOR eine Invertierungsoperation und zweimal ist dies ein Spiegel:
quelle
Hier ist eine, die etwas einfacher zu verstehen sein sollte:
Jetzt kann man den XOR-Trick etwas leichter verstehen, wenn man versteht, dass ^ als + oder - betrachtet werden kann . Genauso wie:
, damit:
quelle
Die meisten Leute würden zwei Variablen x und y mit einer temporären Variablen wie folgt tauschen:
Hier ist ein netter Programmiertrick, um zwei Werte zu tauschen, ohne eine Temperatur zu benötigen:
Weitere Details finden Sie unter Tauschen Sie zwei Variablen mit XOR aus
quelle
Der Grund, warum es funktioniert, ist, dass XOR keine Informationen verliert. Sie könnten dasselbe mit gewöhnlicher Addition und Subtraktion tun, wenn Sie den Überlauf ignorieren könnten. Wenn das Variablenpaar A, B ursprünglich die Werte 1,2 enthält, können Sie sie folgendermaßen austauschen:
Übrigens gibt es einen alten Trick zum Codieren einer in beide Richtungen verknüpften Liste in einem einzelnen "Zeiger". Angenommen, Sie haben eine Liste von Speicherblöcken an den Adressen A, B und C. Das erste Wort in jedem Block lautet:
Wenn Sie Zugriff auf Block A haben, erhalten Sie die Adresse B. Um zu C zu gelangen, nehmen Sie den "Zeiger" in B und subtrahieren A und so weiter. Es funktioniert genauso gut rückwärts. Um entlang der Liste zu laufen, müssen Sie Zeiger auf zwei aufeinanderfolgende Blöcke behalten. Natürlich würden Sie XOR anstelle von Addition / Subtration verwenden, sodass Sie sich keine Gedanken über einen Überlauf machen müssen.
Sie können dies auf ein "verknüpftes Web" erweitern, wenn Sie Spaß haben möchten.
quelle
int
Überlauf UB ist)@VonC hat es richtig, es ist ein ordentlicher mathematischer Trick. Stellen Sie sich 4-Bit-Wörter vor und prüfen Sie, ob dies hilft.
quelle
Grundsätzlich besteht der XOR-Ansatz aus drei Schritten:
a '= a XOR b (1)
b' = a 'XOR b (2)
a' = a 'XOR b' (3)
Um zu verstehen, warum dies funktioniert, beachten Sie zunächst Folgendes:
Nach Schritt (1) hat die binäre Darstellung von a nur an den Bitpositionen, an denen a und b entgegengesetzte Bits haben, 1 Bits. Das ist entweder (ak = 1, bk = 0) oder (ak = 0, bk = 1). Wenn wir nun die Substitution in Schritt (2) durchführen, erhalten wir:
b '= (a XOR b) XOR b
= a XOR (b XOR b), weil XOR assoziativ ist
= a XOR 0 wegen [4] oben
= a aufgrund der Definition von XOR (siehe 1 oben)
Jetzt können wir in Schritt (3) ersetzen:
a ”= (a XOR b) XOR a
= (b XOR a) XOR a, weil XOR kommutativ ist
= b XOR (a XOR a), weil XOR assoziativ ist
= b XOR 0, weil [4] oben
= b aufgrund der Definition von XOR (siehe 1 oben)
Nähere Informationen hier: Notwendig und ausreichend
quelle
Als Randnotiz habe ich dieses Rad vor einigen Jahren unabhängig voneinander neu erfunden, indem ich ganze Zahlen ausgetauscht habe, indem ich Folgendes getan habe:
(Dies ist oben auf schwer lesbare Weise erwähnt),
Die gleiche Argumentation gilt für xor-Swaps: a ^ b ^ b = a und a ^ b ^ a = a. Da xor kommutativ ist, x ^ x = 0 und x ^ 0 = x, ist dies seitdem ziemlich leicht zu erkennen
und
Hoffe das hilft. Diese Erklärung wurde bereits gegeben ... aber nicht sehr klar imo.
quelle
Ich möchte nur eine mathematische Erklärung hinzufügen, um die Antwort vollständiger zu machen. In der Gruppentheorie ist XOR eine abelsche Gruppe , auch kommutative Gruppe genannt. Dies bedeutet, dass es fünf Anforderungen erfüllt: Abschluss, Assoziativität, Identitätselement, Inverses Element, Kommutativität.
XOR-Swap-Formel:
Erweitern Sie die Formel, ersetzen Sie a, b durch die vorherige Formel:
Kommutativität bedeutet "a XOR b" gleich "b XOR a":
Assoziativität bedeutet "(a XOR b) XOR c" gleich "a XOR (b XOR c)":
Das inverse Element in XOR ist selbst, es bedeutet, dass jeder Wert XOR mit sich selbst Null ergibt:
Das Identitätselement in XOR ist Null. Dies bedeutet, dass jeder Wert XOR mit Null unverändert bleibt:
Weitere Informationen erhalten Sie in der Gruppentheorie .
quelle