2010-05-28 14 views
8

¿Cómo doblar una cantidad de dígitos binarios en un número entero? Por ejemplo, si bin (x) = "1001", bin (y) debe ser "11000011". ¿Hay algún algoritmo inteligente y rápido?Duplicar dígitos binarios

ACTUALIZACIÓN: Aquí es una solución elegante:

''.join([''.join(i) for i in zip(X,X)]) 

donde X es bin (int_x) [2:]

Sin embargo, me interesa de un modo más rápido y por los enteros de cualquier tamaño. Tal vez una transformación aritmética debería ayudar.

+0

@Daniel Trebbien: gracias por la aclaración. Mi error. – dnagirl

+0

¿Cuál debería ser la salida para entrada no simétrica, p. si la entrada de 8 (b1000) da 3 (b00000011) o 192 (b11000000)? –

+1

Si la entrada es b0 + 2 * b1 + 4 * b2 + 8 * b3 + ... entonces creo que @psihodelia quiere b0 + 2 * b0 + 4 * b1 + 8 * b1 + 16 * b2 + 32 * b2 + ... –

Respuesta

20

He aquí una manera que debería ser razonablemente rápido: convertir su número a una serie binaria, reinterpretar el resultado como en la base 4. Ahora, para asegurarse de que todos los años 1 se duplican correctamente, multiplicar el resultado por 3.

>>> x = 9 
>>> bin(x) 
'0b1001' 
>>> y = int(bin(x)[2:], 4)*3 
>>> bin(y) 
'0b11000011' 
+0

¡Método muy genial! – psihodelia

+1

¡Bonito! Me llevó algo de tiempo conseguirlo. –

+2

+1 para un método muy genial. – Max

4

any_number - int

str (n) - produce cadena de int.

str :: replace (pattern, replaced_value) - reemplaza todos los patrones en string por valor_remplazado.

int (str) - hace int de la cadena.

n=any_number 
result_number = int(str(n).replace("0","00").replace("1","11")) 
+1

se llame rápido? – Andrey

+2

Al menos es una solución. –

+0

No estoy seguro de que sea rápido, pero es elegante. no lo noté en cuestión, pero comúnmente uso Python cuando no me importa la velocidad. De todos modos, ¿por qué esto no debería ser rápido? El método str :: replace reemplaza todas las entradas en un recorrido. Así que aquí hay dos recorridos, 2 es solo constante, la complejidad sigue siendo O (N) – Max

11

La solución sencilla simplemente utilizando la aritmética de enteros sería:

def doubledigits(n): 
    result = 0 
    power = 1 
    while n > 0: 
     if n%2==1: 
      result += 3*power 
     power *= 4 
     n //= 2 
    return result 
+0

Es mucho menos elegante que usar una cuerda (más código), pero también probablemente sea mucho más rápido (especialmente porque es fácilmente transportable a c). –

+0

@Matthieu: Es más rápido sólo si está realmente escrito en C. – kennytm

+0

Probablemente (no he referenciado ella) ya mano-bucles son notoriamente lentos en Python en comparación con las operaciones integradas. –

0
y = 0; 
for(i = 15; i >= 0; i--) { 
    if((1 << i) & x) { 
     y |= 3; 
    } 
    y <<= 2; 
} 
0
def doubledigits(x): 
    from math import log 
    print (bin(x)) 
    numdigits = x.bit_length() 
    result = 1 << (numdigits*2) 
    for i in range(numdigits, -1, -1): 
     mask = 1 << i 
     if (x & mask > 0): 
      rmask = 0b11 << (2*i) 
      result = result | rmask 
    return result 

debe hacerlo.

+0

Compare esta solución con la solución anterior de Peter Milley. ¿Cuál es más agradable? – unbeli

+0

No funciona para x = 0. Simplemente use 'x.bit_length()' en lugar de registrar. – kennytm

+0

@unbeli compare su comentario con los demás en esta pregunta, ¿cuál es más agradable? : P ey, en lo que a mí respecta, ¡al menos dio una solución de trabajo! – Jeriko

1
$ python2.6 
Python 2.6.5 (r265:79063, Mar 25 2010, 14:13:28) 
>>> def dd(n): return eval("0b" + "".join(d * 2 for d in str(bin(n))[2:])) 
... 
>>> dd(9) 
195 
+1

Sé que eval es malo, si no necesitas convertir el resultado a decimal, no lo necesitas. – grokus

+0

no necesita usar 'eval' en absoluto. 'eval (" 0b "+ ...)' es lo mismo que 'int (..., 2)' – user102008

16

(Referencia http://graphics.stanford.edu/~seander/bithacks.html#Interleave64bitOps):

Si su número es inferior a 256, puede utilizar

@magic 
def double_digits_holger8(x): 
    m = (x * 0x0101010101010101 & 0x8040201008040201) * 0x0102040810204081 
    return ((m >> 49) & 0x5555) | ((m >> 48) & 0xAAAA) 

y si está por debajo 65536,

@more_magic 
def double_digits_binmag16(x): 
    x = (x | x << 8) & 0x00FF00FF 
    x = (x | x << 4) & 0x0F0F0F0F 
    x = (x | x << 2) & 0x33333333 
    x = (x | x << 1) & 0x55555555 
    return x | x << 1 

Comparación con otras soluciones (la función debe tomar un número entero y devolver un número entero para la comparación razonable):

Method  Time per 256 calls 
-------------------------------- 
Do nothing  46.2 usec 
Holger8   256 usec 
BinMag16   360 usec 
Mark    367 useC# http://stackoverflow.com/questions/2928886/doubling-binary-digits/2929198#2929198 
Max    720 useC# http://stackoverflow.com/questions/2928886/doubling-binary-digits/2928938#2928938 
Peter   1.08 mseC# http://stackoverflow.com/questions/2928886/doubling-binary-digits/2928973#2928973 
Phiµµ w/o Log 1.11 mseC# http://stackoverflow.com/questions/2928886/doubling-binary-digits/2929106#2929106 
Jim16   1.26 mseC# http://stackoverflow.com/questions/2928886/doubling-binary-digits/2929038#2929038 
Elegant  1.66 mseC# int(''.join([''.join(i) for i in zip(X,X)]),2) 
More Elegant 2.05 mseC# int(''.join(chain(*zip(X, X))), 2) 

código fuente Benchmark se puede encontrar en http://gist.github.com/417172.

+0

+1 para más magia. Puedes deshacerte de algunos de los puntos y comas. '; p' –

+0

Me interesa más un método universal para los enteros largos de cualquier tamaño. – psihodelia

+0

¿Hay algún motivo específico para utilizar los decoradores "magic" y "more_magic"? Al mirar tu código, parece que solo pasan la función (valor) sin hacerle nada. ¿Me equivoco al pensar que también podrías tener comentarios "mágicos" y "más_mágicos"? – JAB