Domanda molto interessante e trucco intelligente.
Vediamo un semplice esempio di come manipolare un singolo byte. Utilizzo di unsigned 8 bit per semplicità. Immagina che il tuo numero sia xxaxxbxx
e desideri ab000000
.
La soluzione consisteva in due passaggi: un po 'di mascheramento, seguito da moltiplicazione. La maschera di bit è una semplice operazione AND che trasforma i bit non interessanti in zeri. Nel caso sopra, la maschera sarebbe 00100100
e il risultato 00a00b00
.
Ora la parte difficile: trasformarla in ab......
.
Una moltiplicazione è un gruppo di operazioni di spostamento e aggiunta. La chiave è consentire all'overflow di "spostare" i bit di cui non abbiamo bisogno e mettere quelli che vogliamo nel posto giusto.
La moltiplicazione per 4 ( 00000100
) sposta tutto ciò che rimane per 2 e ti porta a a00b0000
. Per far b
muovere verso l'alto dobbiamo moltiplicare per 1 (per mantenere la a nel posto giusto) + 4 (per spostare la b in alto). Questa somma è 5, e combinata con i precedenti 4 otteniamo un numero magico di 20, o 00010100
. L'originale era 00a00b00
dopo il mascheramento; la moltiplicazione dà:
000000a00b000000
00000000a00b0000 +
----------------
000000a0ab0b0000
xxxxxxxxab......
Da questo approccio è possibile estendere a numeri più grandi e più bit.
Una delle domande che hai posto è stata "può essere fatto con un numero qualsiasi di bit?" Penso che la risposta sia "no", a meno che non si consentano diverse operazioni di mascheramento o diverse moltiplicazioni. Il problema è il problema delle "collisioni", ad esempio la "randagia b" nel problema precedente. Immagina di dover fare questo per un numero simile xaxxbxxcx
. Seguendo l'approccio precedente, potresti pensare che abbiamo bisogno di {x 2, x {1 + 4 + 16}} = x 42 (oooh - la risposta a tutto!). Risultato:
00000000a00b00c00
000000a00b00c0000
0000a00b00c000000
-----------------
0000a0ababcbc0c00
xxxxxxxxabc......
Come puoi vedere, funziona ancora, ma "solo". La chiave qui è che c'è "spazio sufficiente" tra i bit che vogliamo per poter spremere tutto. Non potrei aggiungere un quarto bit d subito dopo c, perché otterrei casi in cui ottengo c + d, i bit potrebbero portare, ...
Quindi, senza una prova formale, risponderei alle parti più interessanti della tua domanda come segue: "No, questo non funzionerà per nessun numero di bit. Per estrarre N bit, hai bisogno di (N-1) spazi tra i bit che vuoi estrarre o disporre di ulteriori passaggi di moltiplicazione maschera. "
L'unica eccezione che mi viene in mente per la regola "deve avere (N-1) zeri tra bit" è questa: se vuoi estrarre due bit adiacenti l'uno nell'originale E vuoi tenerli nel stesso ordine, quindi puoi ancora farlo. E ai fini della regola (N-1) contano come due bit.
C'è un'altra intuizione - ispirata alla risposta di @Ternary di seguito (vedi il mio commento lì). Per ogni bit interessante, hai solo bisogno di tanti zeri a destra di quanti ne hai bisogno di spazio per i bit che devono andare lì. Inoltre, ha bisogno di tanti bit a sinistra quanti bit di risultato a sinistra. Quindi se un bit b finisce nella posizione m di n, allora deve avere zeri m-1 alla sua sinistra e zeri nm alla sua destra. Soprattutto quando i bit non sono nello stesso ordine nel numero originale come saranno dopo il riordino, questo è un miglioramento importante dei criteri originali. Ciò significa, ad esempio, che una parola a 16 bit
a...e.b...d..c..
Può essere spostato in
abcde...........
anche se c'è solo uno spazio tra eeb, due tra d e c, tre tra gli altri. Qualunque cosa sia successa a N-1 ?? In questo caso, a...e
diventa "un blocco" - vengono moltiplicati per 1 per finire nel posto giusto, quindi "abbiamo ottenuto e gratuitamente". Lo stesso vale per be d (b ha bisogno di tre spazi a destra, d ha bisogno degli stessi tre a sinistra). Quindi, quando calcoliamo il numero magico, troviamo che ci sono duplicati:
a: << 0 ( x 1 )
b: << 5 ( x 32 )
c: << 11 ( x 2048 )
d: << 5 ( x 32 ) !! duplicate
e: << 0 ( x 1 ) !! duplicate
Chiaramente, se volessi questi numeri in un ordine diverso, dovresti spaziarli ulteriormente. Possiamo riformulare il(N-1)
regola: "Funzionerà sempre se ci sono almeno (N-1) spazi tra i bit; oppure, se si conosce l'ordine dei bit nel risultato finale, allora se un bit b finisce nella posizione m di n, deve avere zeri m-1 alla sua sinistra e zeri nm alla sua destra. "
@Ternary ha sottolineato che questa regola non funziona del tutto, poiché può esserci un riporto da bit che aggiungono "appena a destra dell'area target" - vale a dire, quando i bit che stiamo cercando sono tutti uno. Continuando l'esempio che ho dato sopra con i cinque bit ben confezionati in una parola di 16 bit: se iniziamo con
a...e.b...d..c..
Per semplicità, nominerò le posizioni dei bit ABCDEFGHIJKLMNOP
La matematica che stavamo per fare era
ABCDEFGHIJKLMNOP
a000e0b000d00c00
0b000d00c0000000
000d00c000000000
00c0000000000000 +
----------------
abcded(b+c)0c0d00c00
Fino ad ora, abbiamo pensato che qualcosa sotto abcde
(posizioni ABCDE
) non avrebbe avuto importanza, ma in effetti, come sottolineato da @Ternary, se b=1, c=1, d=1
poi (b+c)
in posizione G
causerà un po 'di portare in posizione F
, il che significa che (d+1)
in posizione F
porterà un po' inE
- e il nostro il risultato è rovinato. Si noti che lo spazio a destra del bit di interesse meno significativo ( c
in questo esempio) non ha importanza, poiché la moltiplicazione causerà il riempimento con zeri di beyone il bit meno significativo.
Quindi dobbiamo modificare la nostra regola (m-1) / (nm). Se c'è più di un bit che ha "esattamente (nm) bit inutilizzati a destra (non contando l'ultimo bit nel modello -" c "nell'esempio sopra), allora dobbiamo rafforzare la regola - e dobbiamo fallo iterativamente!
Dobbiamo guardare non solo al numero di bit che soddisfano il criterio (nm), ma anche a quelli che si trovano in (n-m + 1), ecc. Chiamiamo il loro numero Q0 (esattamente n-m
al bit successivo), Q1 ( n-m + 1), fino a Q (N-1) (n-1). Quindi rischiamo di trasportare if
Q0 > 1
Q0 == 1 && Q1 >= 2
Q0 == 0 && Q1 >= 4
Q0 == 1 && Q1 > 1 && Q2 >=2
...
Se lo guardi, puoi vederlo se scrivi una semplice espressione matematica
W = N * Q0 + (N - 1) * Q1 + ... + Q(N-1)
e il risultato è W > 2 * N
, quindi è necessario aumentare il criterio RHS di un bit a (n-m+1)
. A questo punto, l'operazione è sicura fino a quando W < 4
; se non funziona, aumentare di nuovo il criterio, ecc.
Penso che seguire quanto sopra ti porterà molto lontano per la tua risposta ...