Sehr interessante Frage und kluger Trick.
Schauen wir uns ein einfaches Beispiel für die Manipulation eines einzelnen Bytes an. Der Einfachheit halber wird vorzeichenloses 8-Bit verwendet. Stellen Sie sich vor, Ihre Nummer ist xxaxxbxx
und Sie wollen ab000000
.
Die Lösung bestand aus zwei Schritten: einer Bitmaskierung, gefolgt von einer Multiplikation. Die Bitmaske ist eine einfache UND-Operation, die uninteressante Bits in Nullen umwandelt. Im obigen Fall wäre Ihre Maske 00100100
und das Ergebnis 00a00b00
.
Nun der schwierige Teil: das zu machen ab......
.
Eine Multiplikation ist eine Reihe von Shift-and-Add-Operationen. Der Schlüssel ist, dass der Überlauf die nicht benötigten Bits "wegschiebt" und die gewünschten an die richtige Stelle bringt.
Die Multiplikation mit 4 ( 00000100
) würde alles, was übrig bleibt, um 2 verschieben und Sie dazu bringen a00b0000
. Um das b
nach oben zu bringen, müssen wir mit 1 multiplizieren (um das a an der richtigen Stelle zu halten) + 4 (um das b nach oben zu bewegen). Diese Summe ist 5 und zusammen mit den früheren 4 erhalten wir eine magische Zahl von 20 oder 00010100
. Das Original war 00a00b00
nach dem Maskieren; Die Multiplikation ergibt:
000000a00b000000
00000000a00b0000 +
----------------
000000a0ab0b0000
xxxxxxxxab......
Von diesem Ansatz aus können Sie auf größere Zahlen und mehr Bits erweitern.
Eine der Fragen, die Sie gestellt haben, war: "Kann dies mit einer beliebigen Anzahl von Bits durchgeführt werden?" Ich denke, die Antwort ist "nein", es sei denn, Sie erlauben mehrere Maskierungsoperationen oder mehrere Multiplikationen. Das Problem ist das Problem der "Kollisionen" - zum Beispiel das "Streuner b" im obigen Problem. Stellen Sie sich vor, wir müssen dies mit einer Zahl wie tun xaxxbxxcx
. Nach dem früheren Ansatz würden Sie denken, wir brauchen {x 2, x {1 + 4 + 16}} = x 42 (oooh - die Antwort auf alles!). Ergebnis:
00000000a00b00c00
000000a00b00c0000
0000a00b00c000000
-----------------
0000a0ababcbc0c00
xxxxxxxxabc......
Wie Sie sehen können, funktioniert es immer noch, aber "nur gerade". Der Schlüssel hier ist, dass zwischen den Bits, die wir wollen, "genug Platz" ist, damit wir alles zusammenpressen können. Ich konnte kein viertes Bit d direkt nach c hinzufügen, da ich Fälle bekommen würde, in denen ich c + d bekomme, Bits könnten tragen, ...
Ohne formalen Beweis würde ich die interessanteren Teile Ihrer Frage wie folgt beantworten: "Nein, dies funktioniert nicht für eine beliebige Anzahl von Bits. Um N Bits zu extrahieren, benötigen Sie (N-1) Leerzeichen zwischen den gewünschten Bits extrahieren oder zusätzliche Maskenmultiplikationsschritte ausführen. "
Die einzige Ausnahme, die ich mir für die Regel "Muss (N-1) Nullen zwischen Bits haben" vorstellen kann, ist folgende: Wenn Sie zwei Bits extrahieren möchten, die im Original nebeneinander liegen, UND Sie sie in der behalten möchten gleiche Reihenfolge, dann können Sie es noch tun. Und für die Zwecke der (N-1) -Regel zählen sie als zwei Bits.
Es gibt noch eine weitere Erkenntnis - inspiriert von der Antwort von @Ternary unten (siehe meinen Kommentar dort). Für jedes interessante Bit benötigen Sie nur so viele Nullen rechts davon, wie Sie Platz für Bits benötigen, die dorthin gehen müssen. Aber es braucht auch so viele Bits links wie es Ergebnisbits links hat. Wenn also ein Bit b an der Position m von n landet, muss es links m-1 Nullen und rechts nm Nullen haben. Insbesondere wenn die Bits in der ursprünglichen Nummer nicht in der gleichen Reihenfolge sind wie nach der Neuordnung, ist dies eine wichtige Verbesserung der ursprünglichen Kriterien. Dies bedeutet zum Beispiel, dass ein 16-Bit-Wort
a...e.b...d..c..
Kann verschoben werden in
abcde...........
obwohl es nur einen Raum zwischen e und b gibt, zwei zwischen d und c, drei zwischen den anderen. Was ist mit N-1 passiert? In diesem Fall a...e
wird "ein Block" - sie werden mit 1 multipliziert, um an der richtigen Stelle zu landen, und so "wir haben e kostenlos". Gleiches gilt für b und d (b benötigt drei Leerzeichen rechts, d benötigt die gleichen drei links). Wenn wir also die magische Zahl berechnen, stellen wir fest, dass es Duplikate gibt:
a: << 0 ( x 1 )
b: << 5 ( x 32 )
c: << 11 ( x 2048 )
d: << 5 ( x 32 ) !! duplicate
e: << 0 ( x 1 ) !! duplicate
Wenn Sie diese Nummern in einer anderen Reihenfolge haben möchten, müssen Sie sie natürlich weiter platzieren. Wir können die (N-1)
Regel neu formulieren : "Es wird immer funktionieren, wenn mindestens (N-1) Leerzeichen zwischen den Bits vorhanden sind; oder wenn die Reihenfolge der Bits im Endergebnis bekannt ist, wenn ein Bit b in Position m von endet n, es muss links m-1 Nullen und rechts nm Nullen haben. "
@Ternary wies darauf hin, dass diese Regel nicht ganz funktioniert, da es einen Übertrag von Bits geben kann, die "rechts neben dem Zielbereich" hinzufügen - nämlich wenn die Bits, nach denen wir suchen, alle diejenigen sind. Fortsetzung des obigen Beispiels mit den fünf dicht gepackten Bits in einem 16-Bit-Wort: Wenn wir mit beginnen
a...e.b...d..c..
Der Einfachheit halber werde ich die Bitpositionen benennen ABCDEFGHIJKLMNOP
Die Mathematik, die wir machen wollten, war
ABCDEFGHIJKLMNOP
a000e0b000d00c00
0b000d00c0000000
000d00c000000000
00c0000000000000 +
----------------
abcded(b+c)0c0d00c00
Bis jetzt dachten wir, dass alles unten abcde
(Positionen ABCDE
) keine Rolle spielen würde, aber tatsächlich, wie @Ternary hervorhob, wenn b=1, c=1, d=1
dann (b+c)
in Position G
ein bisschen in Position gebracht wird F
, was bedeutet, dass (d+1)
in Position F
ein bisschen in E
- und unsere Ergebnis ist verwöhnt. Beachten Sie, dass der Platz rechts neben dem niedrigstwertigen interessierenden Bit ( c
in diesem Beispiel) keine Rolle spielt, da die Multiplikation ein Auffüllen mit Nullen von jenseits des niedrigstwertigen Bits verursacht.
Wir müssen also unsere (m-1) / (nm) -Regel ändern. Wenn es mehr als ein Bit gibt, das "genau (nm) unbenutzte Bits rechts hat (ohne das letzte Bit im Muster zu zählen -" c "im obigen Beispiel), müssen wir die Regel verstärken - und wir müssen mach das iterativ!
Wir müssen nicht nur die Anzahl der Bits betrachten, die das (nm) -Kriterium erfüllen, sondern auch diejenigen, die bei (n-m + 1) usw. liegen. Nennen wir ihre Anzahl Q0 (genau n-m
zum nächsten Bit), Q1 ( n-m + 1) bis zu Q (N-1) (n-1). Dann riskieren wir zu tragen, wenn
Q0 > 1
Q0 == 1 && Q1 >= 2
Q0 == 0 && Q1 >= 4
Q0 == 1 && Q1 > 1 && Q2 >=2
...
Wenn Sie sich das ansehen, können Sie das sehen, wenn Sie einen einfachen mathematischen Ausdruck schreiben
W = N * Q0 + (N - 1) * Q1 + ... + Q(N-1)
und das Ergebnis ist W > 2 * N
, dann müssen Sie das RHS-Kriterium um ein Bit auf erhöhen (n-m+1)
. Zu diesem Zeitpunkt ist der Betrieb sicher, solange W < 4
; Wenn dies nicht funktioniert, erhöhen Sie das Kriterium noch einmal usw.
Ich denke, dass das Befolgen der obigen Anweisungen einen langen Weg zu Ihrer Antwort bringt ...