Pergunta muito interessante e truque inteligente.
Vejamos um exemplo simples de como manipular um único byte. Usando 8 bits não assinados para simplificar. Imagine seu número xxaxxbxx
e você deseja ab000000
.
A solução consistiu em duas etapas: um pouco de máscara, seguida de multiplicação. A máscara de bits é uma operação AND simples que transforma bits desinteressantes em zeros. No caso acima, sua máscara seria 00100100
e o resultado 00a00b00
.
Agora a parte mais difícil: transformar isso em ab......
.
Uma multiplicação é um monte de operações de troca e adição. A chave é permitir que o excesso "desvie" os bits de que não precisamos e coloque os que queremos no lugar certo.
A multiplicação por 4 ( 00000100
) mudaria tudo o que restava por 2 e o levaria a00b0000
. Para b
subir, precisamos multiplicar por 1 (para manter a no lugar certo) + 4 (para subir b). Essa soma é 5 e, combinada com as 4 anteriores, obtemos um número mágico de 20 ou 00010100
. O original foi 00a00b00
depois de mascarar; a multiplicação dá:
000000a00b000000
00000000a00b0000 +
----------------
000000a0ab0b0000
xxxxxxxxab......
A partir dessa abordagem, você pode estender para números maiores e mais bits.
Uma das perguntas que você fez foi "isso pode ser feito com qualquer número de bits?" Eu acho que a resposta é "não", a menos que você permita várias operações de mascaramento ou várias multiplicações. O problema é a questão das "colisões" - por exemplo, o "b perdido" no problema acima. Imagine que precisamos fazer isso com um número parecido xaxxbxxcx
. Seguindo a abordagem anterior, você pensaria que precisamos de {x 2, x {1 + 4 + 16}} = x 42 (oooh - a resposta para tudo!). Resultado:
00000000a00b00c00
000000a00b00c0000
0000a00b00c000000
-----------------
0000a0ababcbc0c00
xxxxxxxxabc......
Como você pode ver, ainda funciona, mas "apenas". A chave aqui é que existe "espaço suficiente" entre os bits que queremos que possamos espremer tudo. Eu não poderia adicionar um quarto bit d logo após c, porque eu receberia instâncias em que eu recebia c + d, os bits poderiam carregar, ...
Portanto, sem prova formal, eu responderia as partes mais interessantes da sua pergunta da seguinte maneira: "Não, isso não funcionará para nenhum número de bits. Para extrair N bits, você precisa de espaços (N-1) entre os bits que deseja extrair ou ter etapas adicionais de multiplicação de máscara ".
A única exceção que posso pensar para a regra "deve ter (N-1) zeros entre bits" é a seguinte: se você deseja extrair dois bits adjacentes um ao outro no original, E você deseja mantê-los no diretório mesma ordem, você ainda pode fazê-lo. E para os fins da regra (N-1), eles contam como dois bits.
Há outro insight - inspirado na resposta do @Ternary abaixo (veja meu comentário lá). Para cada bit interessante, você precisa apenas de zeros à direita e de espaço para os bits que precisam ir para lá. Mas também precisa de tantos bits para a esquerda quanto de bits para a esquerda. Portanto, se um bit b termina na posição m de n, ele precisa ter m-1 zeros à esquerda e nm zeros à direita. Especialmente quando os bits não estão na mesma ordem no número original, como estarão após o novo pedido, isso é uma melhoria importante nos critérios originais. Isso significa, por exemplo, que uma palavra de 16 bits
a...e.b...d..c..
Pode ser deslocado para
abcde...........
mesmo que exista apenas um espaço entre e e b, dois entre d e c, três entre os outros. O que aconteceu com o N-1 ?? Nesse caso, a...e
torna-se "um bloco" - eles são multiplicados por 1 para terminar no lugar certo e, portanto, "recebemos e de graça". O mesmo vale para b e d (b precisa de três espaços à direita, d precisa dos mesmos três à sua esquerda). Então, quando calculamos o número mágico, descobrimos que existem duplicatas:
a: << 0 ( x 1 )
b: << 5 ( x 32 )
c: << 11 ( x 2048 )
d: << 5 ( x 32 ) !! duplicate
e: << 0 ( x 1 ) !! duplicate
Claramente, se você quisesse esses números em uma ordem diferente, teria que espaçá-los ainda mais. Podemos reformular a (N-1)
regra: "Sempre funcionará se houver pelo menos (N-1) espaços entre os bits; ou, se a ordem dos bits no resultado final for conhecida, se um bit b terminar na posição m de n, ele precisa ter m-1 zeros à esquerda e nm zeros à direita. "
O @Ternary apontou que essa regra não funciona muito bem, pois pode haver uma carga de bits adicionando "exatamente à direita da área de destino" - ou seja, quando os bits que estamos procurando são todos iguais. Continuando o exemplo que dei acima com os cinco bits compactados em uma palavra de 16 bits: se começarmos com
a...e.b...d..c..
Para simplificar, vou nomear as posições dos bits ABCDEFGHIJKLMNOP
A matemática que íamos fazer era
ABCDEFGHIJKLMNOP
a000e0b000d00c00
0b000d00c0000000
000d00c000000000
00c0000000000000 +
----------------
abcded(b+c)0c0d00c00
Até agora, pensávamos que qualquer coisa abaixo abcde
(posições ABCDE
) não importaria, mas, na verdade, como @Ternary apontou, se b=1, c=1, d=1
então a (b+c)
posição G
fará com que um pouco carregue para a posição F
, o que significa que (d+1)
na posição F
levará um pouco para dentro E
- e nossa resultado é estragado. Observe que o espaço à direita do bit de interesse menos significativo ( c
neste exemplo) não importa, pois a multiplicação causará preenchimento com zeros de além do bit menos significativo.
Portanto, precisamos modificar nossa regra (m-1) / (nm). Se houver mais de um bit com "exatamente (nm) bits não utilizados à direita (sem contar o último bit no padrão -" c "no exemplo acima), precisamos fortalecer a regra - e precisamos faça-o iterativamente!
Temos que observar não apenas o número de bits que atendem ao critério (nm), mas também os que estão em (n-m + 1), etc. Vamos chamar o número Q0 (exatamente n-m
para o próximo bit), Q1 ( n-m + 1), até Q (N-1) (n-1). Então corremos o risco de carregar se
Q0 > 1
Q0 == 1 && Q1 >= 2
Q0 == 0 && Q1 >= 4
Q0 == 1 && Q1 > 1 && Q2 >=2
...
Se você olhar para isso, poderá ver que, se escrever uma expressão matemática simples
W = N * Q0 + (N - 1) * Q1 + ... + Q(N-1)
e o resultado é W > 2 * N
, então você precisa aumentar o critério RHS em um bit para (n-m+1)
. Neste ponto, a operação é segura enquanto W < 4
; se isso não funcionar, aumente mais o critério, etc.
Penso que, seguindo o exposto acima, você obterá um longo caminho para sua resposta ...