Question très intéressante et astuce astucieuse.
Regardons un exemple simple de manipulation d'un seul octet. Utilisation de 8 bits non signés pour plus de simplicité. Imaginez que votre numéro est xxaxxbxx
et que vous voulez ab000000
.
La solution consistait en deux étapes: un peu de masquage, suivi d'une multiplication. Le masque de bits est une opération ET simple qui transforme les bits non intéressants en zéros. Dans le cas ci-dessus, votre masque serait 00100100
et le résultat 00a00b00
.
Maintenant, la partie difficile: transformer cela en ab......
.
Une multiplication est un ensemble d'opérations de décalage et d'ajout. La clé est de permettre au débordement de «déplacer» les bits dont nous n'avons pas besoin et de mettre ceux que nous voulons au bon endroit.
La multiplication par 4 ( 00000100
) déplacerait tout ce qui reste par 2 et vous amènerait à a00b0000
. Pour faire b
monter le nous devons multiplier par 1 (pour garder le a au bon endroit) + 4 (pour déplacer le b vers le haut). Cette somme est 5, et combinée avec les 4 précédentes, nous obtenons un nombre magique de 20, ou 00010100
. L'original était 00a00b00
après le masquage; la multiplication donne:
000000a00b000000
00000000a00b0000 +
----------------
000000a0ab0b0000
xxxxxxxxab......
De cette approche, vous pouvez étendre à de plus grands nombres et plus de bits.
L'une des questions que vous avez posées était "est-ce que cela peut être fait avec n'importe quel nombre de bits?" Je pense que la réponse est "non", sauf si vous autorisez plusieurs opérations de masquage ou plusieurs multiplications. Le problème est la question des "collisions" - par exemple, le "b errant" dans le problème ci-dessus. Imaginez que nous devons faire cela à un nombre comme xaxxbxxcx
. En suivant l'approche précédente, on pourrait penser que nous avons besoin de {x 2, x {1 + 4 + 16}} = x 42 (oooh - la réponse à tout!). Résultat:
00000000a00b00c00
000000a00b00c0000
0000a00b00c000000
-----------------
0000a0ababcbc0c00
xxxxxxxxabc......
Comme vous pouvez le voir, cela fonctionne toujours, mais "seulement". La clé ici est qu'il y a "suffisamment d'espace" entre les bits que nous voulons pour pouvoir tout serrer. Je ne pouvais pas ajouter un quatrième bit d juste après c, car j'obtiendrais des instances où j'obtiens c + d, des bits pourraient porter, ...
Donc, sans preuve formelle, je répondrais aux parties les plus intéressantes de votre question comme suit: "Non, cela ne fonctionnera pas pour n'importe quel nombre de bits. Pour extraire N bits, vous avez besoin (N-1) d'espaces entre les bits que vous voulez extraire ou avoir des étapes supplémentaires de multiplication de masque. "
La seule exception à laquelle je peux penser pour la règle "doit avoir (N-1) des zéros entre les bits" est la suivante: si vous voulez extraire deux bits qui sont adjacents l'un à l'autre dans l'original, ET vous voulez les garder dans le même ordre, alors vous pouvez toujours le faire. Et aux fins de la règle (N-1), ils comptent comme deux bits.
Il y a un autre aperçu - inspiré par la réponse de @Ternary ci-dessous (voir mon commentaire là-bas). Pour chaque bit intéressant, vous avez seulement besoin d'autant de zéros à sa droite que vous avez besoin d'espace pour les bits qui doivent y aller. Mais aussi, il a besoin d'autant de bits à gauche qu'il y a de bits de résultat à gauche. Donc, si un bit b se retrouve en position m de n, il doit avoir m-1 zéros à sa gauche et nm zéros à sa droite. Surtout lorsque les bits ne sont pas dans le même ordre dans le numéro d'origine qu'ils le seront après la réorganisation, il s'agit d'une amélioration importante par rapport aux critères d'origine. Cela signifie, par exemple, qu'un mot de 16 bits
a...e.b...d..c..
Peut être déplacé en
abcde...........
même s'il n'y a qu'un seul espace entre e et b, deux entre d et c, trois entre les autres. Qu'est-il arrivé à N-1 ?? Dans ce cas, a...e
devient "un bloc" - ils sont multipliés par 1 pour se retrouver au bon endroit, et donc "nous avons eu e gratuitement". Il en va de même pour b et d (b a besoin de trois espaces à droite, d a besoin des mêmes trois à gauche). Ainsi, lorsque nous calculons le nombre magique, nous constatons qu'il y a des doublons:
a: << 0 ( x 1 )
b: << 5 ( x 32 )
c: << 11 ( x 2048 )
d: << 5 ( x 32 ) !! duplicate
e: << 0 ( x 1 ) !! duplicate
De toute évidence, si vous vouliez ces chiffres dans un ordre différent, vous devriez les espacer davantage. Nous pouvons reformuler la (N-1)
règle: "Cela fonctionnera toujours s'il y a au moins (N-1) espaces entre les bits; ou, si l'ordre des bits dans le résultat final est connu, alors si un bit b se retrouve en position m de n, il doit avoir m-1 zéros à sa gauche et nm zéros à sa droite. "
@Ternary a souligné que cette règle ne fonctionne pas tout à fait, car il peut y avoir un report de bits ajoutant "juste à droite de la zone cible" - à savoir, lorsque les bits que nous recherchons sont tous un. Poursuivant l'exemple que j'ai donné ci-dessus avec les cinq bits serrés dans un mot de 16 bits: si nous commençons par
a...e.b...d..c..
Pour simplifier, je nommerai les positions des bits ABCDEFGHIJKLMNOP
Le calcul que nous allions faire était
ABCDEFGHIJKLMNOP
a000e0b000d00c00
0b000d00c0000000
000d00c000000000
00c0000000000000 +
----------------
abcded(b+c)0c0d00c00
Jusqu'à présent, nous pensions que tout ce qui était en dessous abcde
(positions ABCDE
) n'aurait pas d'importance, mais en fait, comme l'a souligné @Ternary, si b=1, c=1, d=1
alors (b+c)
en position G
, un peu se poursuivra en position F
, ce qui signifie qu'en (d+1)
position, F
cela portera un peu dans E
- et notre le résultat est gâté. Notez que l'espace à droite du bit d'intérêt le moins significatif (c
dans cet exemple) n'a pas d'importance, car la multiplication provoquera un remplissage avec des zéros de au-delà du bit le moins significatif.
Nous devons donc modifier notre règle (m-1) / (nm). S'il y a plus d'un bit qui a "exactement (nm) bits inutilisés vers la droite (sans compter le dernier bit du motif -" c "dans l'exemple ci-dessus), alors nous devons renforcer la règle - et nous devons faites-le de manière itérative!
Nous devons regarder non seulement le nombre de bits qui répondent au critère (nm), mais aussi ceux qui sont à (n-m + 1), etc. Appelons leur nombre Q0 (exactement n-m
au bit suivant), Q1 ( n-m + 1), jusqu'à Q (N-1) (n-1). Ensuite, nous risquons de porter si
Q0 > 1
Q0 == 1 && Q1 >= 2
Q0 == 0 && Q1 >= 4
Q0 == 1 && Q1 > 1 && Q2 >=2
...
Si vous regardez cela, vous pouvez voir que si vous écrivez une expression mathématique simple
W = N * Q0 + (N - 1) * Q1 + ... + Q(N-1)
et le résultat est W > 2 * N
, alors vous devez augmenter le critère RHS d'un bit à (n-m+1)
. À ce stade, l'opération est sûre tant que W < 4
; si cela ne fonctionne pas, augmentez encore le critère, etc.
Je pense qu'en suivant ce qui précède, vous obtiendrez un long chemin vers votre réponse ...