Operadores bit a bit: AND, OR, XOR, NOT y desplazamientos
Repase los seis operadores bit a bit con tablas de verdad y ejemplos en Python, y comprenda cómo los desplazamientos a izquierda y derecha se relacionan con multiplicar y dividir entre dos.
Operadores bit a bit: AND, OR, XOR, NOT y desplazamientos es una lección gratuita de Coding Interview Prep en CoddyKit. Esta es la lección 1 de 4. Puedes leer la lección completa abajo gratuitamente — luego la practicas en el navegador con un editor de código integrado y un tutor de IA 24/7. Forma parte de la ruta de aprendizaje de Coding Interview Prep, y tu progreso se sincroniza en la web y la app de CoddyKit. El curso de Coding Interview Prep incluye 4 lecciones en total.
Por qué es importante la manipulación de bits
La manipulación de bits permite operar directamente sobre la representación binaria de los enteros. Muchos problemas que parecen complejos se vuelven triviales con el truco bit a bit adecuado: encontrar un número ausente en O(n) de tiempo y O(1) de espacio, intercambiar variables sin una variable temporal o codificar subconjuntos de forma compacta. Los entrevistadores utilizan estos problemas para evaluar la comprensión de bajo nivel y la capacidad de pensar de forma creativa.
Los enteros de Python tienen precisión arbitraria —pueden ser tan grandes como permita la memoria—, pero las operaciones con bits siempre siguen la semántica estándar del complemento a dos a nivel de hardware. Los seis operadores funcionan bit a bit sobre las representaciones binarias de los enteros.
# All six bitwise operators in Python
a, b = 0b1010, 0b1100 # 10 and 12 in decimal
print(f'a = {bin(a)} = {a}')
print(f'b = {bin(b)} = {b}')
print(f'a & b (AND) = {bin(a & b)} = {a & b}') # 1000 = 8
print(f'a | b (OR) = {bin(a | b)} = {a | b}') # 1110 = 14
print(f'a ^ b (XOR) = {bin(a ^ b)} = {a ^ b}') # 0110 = 6
print(f'~a (NOT) = {~a}') # -11 (two's complement)
print(f'a << 1 (LSH) = {bin(a << 1)} = {a << 1}') # 10100 = 20
print(f'a >> 1 (RSH) = {bin(a >> 1)} = {a >> 1}') # 101 = 5Operador AND: enmascaramiento de bits
El operador AND (&) produce 1 únicamente cuando ambos bits de entrada son 1. Su uso principal es el enmascaramiento: seleccionar bits concretos de un número mientras se ponen a cero todos los demás. Para comprobar si el bit k está activado en el número n, evalúe n & (1 << k); si el resultado no es cero, el bit k vale 1.
AND también se utiliza para limpiar el bit activado de menor peso: n & (n - 1) elimina el bit 1 situado más a la derecha. Esto se usa para contar bits activados de forma eficiente y para comprobar si un número es una potencia de dos (una potencia de dos tiene exactamente un bit activado, por lo que n & (n-1) == 0).
n = 0b10110100 # 180
# Check if bit 5 is set (0-indexed from right)
bit_5 = (n >> 5) & 1
print(f'Bit 5 of {n}: {bit_5}') # 1
# Clear lowest set bit
print(f'n = {bin(n)}')
print(f'n & (n-1) = {bin(n & (n-1))}') # 10110000, removed the '100'
# Check power of two
for x in [16, 15, 8, 6, 1, 0]:
is_pow2 = x > 0 and (x & (x - 1)) == 0
print(f'{x}: power of 2 = {is_pow2}')Operador OR: activar bits
El operador OR (|) produce 1 si al menos uno de los bits de entrada es 1. Su uso principal es activar un bit concreto sin afectar a los demás. Para activar el bit k del número n, use n | (1 << k). El 1 desplazado a la posición k activa ese bit; todos los demás bits permanecen sin cambios porque cualquier valor combinado mediante OR con 0 conserva su valor.
OR también se utiliza para combinar flags: si representa los flags de funcionalidades como bits individuales, puede activar varios flags mediante OR. Por ejemplo, READ | WRITE | EXECUTE combina tres bits de permisos en un único entero.
# Set bit k in n
def set_bit(n, k):
return n | (1 << k)
n = 0b1000 # 8
print(f'Original: {bin(n)}')
print(f'Set bit 1: {bin(set_bit(n, 1))}') # 1010
print(f'Set bit 0: {bin(set_bit(n, 0))}') # 1001
# Flag combination example
READ = 0b001 # 1
WRITE = 0b010 # 2
EXECUTE = 0b100 # 4
perms = READ | EXECUTE
print(f'READ|EXECUTE permissions: {bin(perms)} = {perms}')
print(f'Has READ: {bool(perms & READ)}')
print(f'Has WRITE: {bool(perms & WRITE)}')
print(f'Has EXECUTE: {bool(perms & EXECUTE)}')Operador XOR: alternancia y diferencia
El operador XOR (^) produce 1 cuando los bits de entrada son diferentes. XOR tiene tres propiedades algebraicas muy útiles: a ^ a = 0 (las entradas iguales se cancelan), a ^ 0 = a (cero es el elemento neutro), y XOR es conmutativo y asociativo. Estas propiedades hacen de XOR la herramienta preferida para encontrar elementos únicos.
XOR también se utiliza para alternar un bit concreto: n ^ (1 << k) invierte el bit k y deja los demás sin cambios. Si el bit k era 0, pasa a ser 1; si era 1, pasa a ser 0.
# XOR properties
print(5 ^ 5) # 0 — same values cancel
print(5 ^ 0) # 5 — zero is identity
print(5 ^ 3 ^ 3) # 5 — 3 cancels itself
# Toggle bit k
def toggle_bit(n, k):
return n ^ (1 << k)
n = 0b1010
print(f'Toggle bit 3: {bin(toggle_bit(n, 3))}') # 0010 (was 1)
print(f'Toggle bit 0: {bin(toggle_bit(n, 0))}') # 1011 (was 0)
# XOR swap without temp variable
a, b = 7, 13
a = a ^ b
b = a ^ b # b now gets original a
a = a ^ b # a now gets original b
print(f'After XOR swap: a={a}, b={b}') # a=13, b=7Operador NOT y complemento a dos
El operador NOT (~) invierte todos los bits. En Python, ~n equivale a -(n+1) debido a la representación en complemento a dos. Esto sorprende a muchas personas: ~5 = -6, no el 0b11111010 que se esperaría ingenuamente. Los enteros de Python tienen precisión infinita, por lo que invertir todos los bits de un número positivo produce un resultado negativo en complemento a dos.
En la práctica, rara vez se utiliza ~ por sí solo en Python para la manipulación de bits. En su lugar, se combina con AND para limpiar bits concretos, o se calcula ~n & mask, donde mask limita el ancho a un número específico de bits (por ejemplo, & 0xFFFFFFFF para 32 bits).
# NOT in Python: ~n = -(n+1)
for n in [0, 1, 5, 127]:
print(f'~{n} = {~n}') # all give -(n+1)
# Clear bit k using NOT
def clear_bit(n, k):
return n & ~(1 << k)
n = 0b1111
print(f'Clear bit 2: {bin(clear_bit(n, 2))}') # 1011
print(f'Clear bit 0: {bin(clear_bit(n, 0))}') # 1110
# Limiting to 32-bit with mask
def bitwise_not_32(n):
return ~n & 0xFFFFFFFF
print(f'32-bit NOT of 5: {bin(bitwise_not_32(5))}') # 32 zeros then onesDesplazamiento a la izquierda: multiplicar por potencias de dos
El operador de desplazamiento a la izquierda (<<) desplaza todos los bits k posiciones hacia la izquierda y rellena con ceros las posiciones vacías de la derecha. Esto equivale a multiplicar por 2^k. Desplazar 1 posición a la izquierda duplica el valor; desplazar k posiciones lo multiplica por 2^k.
En los problemas de entrevistas, los desplazamientos a la izquierda se utilizan principalmente para crear máscaras de bits: 1 << k crea un número en el que solo está activado el bit k. Esta es la base de todas las operaciones de manipulación de bits: activar, limpiar, alternar y comprobar bits individuales comienza con 1 << k.
# Left shift = multiply by 2^k
n = 1
for k in range(8):
print(f'1 << {k} = {1 << k}') # 1,2,4,8,16,32,64,128
# Practical use: creating bitmasks
def bit_mask(k):
return 1 << k
print(f'\nBitmask for bit 0: {bin(bit_mask(0))}') # 1
print(f'Bitmask for bit 3: {bin(bit_mask(3))}') # 1000
print(f'Bitmask for bit 7: {bin(bit_mask(7))}') # 10000000
# Fast exponentiation: 2^10 = 1024
print(f'2^10 = {1 << 10}') # 1024Desplazamiento a la derecha: dividir por potencias de dos
El operador de desplazamiento a la derecha (>>) desplaza todos los bits k posiciones hacia la derecha y descarta los k bits situados más a la derecha. Esto equivale a una división entera entre 2^k. El desplazamiento a la derecha de Python siempre es aritmético: los bits de la izquierda se rellenan con el bit de signo (0 para los valores positivos y 1 para los negativos).
Un truco habitual en entrevistas: para extraer el bit k del número n, use (n >> k) & 1. Esto desplaza el bit k hasta la posición 0 y enmascara todos los demás bits. Es la forma más sencilla de comprobar cualquier bit concreto sin tener que calcular y comparar una máscara completa.
# Right shift = integer division by 2^k
n = 64
for k in range(7):
print(f'{n} >> {k} = {n >> k}') # 64,32,16,8,4,2,1
# Extract bit k from n
def get_bit(n, k):
return (n >> k) & 1
n = 0b10110101 # 181
print(f'\nBits of {n} ({bin(n)}):')
for k in range(8):
print(f' Bit {k}: {get_bit(n, k)}')
# Negative number right shift (arithmetic)
print(f'-8 >> 1 = {-8 >> 1}') # -4 (fills with sign bit 1)Guía rápida de trucos prácticos de bits
A continuación se recopilan las expresiones idiomáticas más comunes de manipulación de bits que encontrará en las entrevistas. Memorice estos patrones: aparecen repetidamente en decenas de problemas:
n & 1— comprobar si n es imparn & (n-1)— limpiar el bit activado de menor peson & -n— aislar el bit activado de menor peson | (1 << k)— activar el bit kn & ~(1 << k)— limpiar el bit kn ^ (1 << k)— alternar el bit k(n >> k) & 1— comprobar el bit k
# Bit trick cheatsheet — all at once
n = 0b10110100 # 180
print(f'n = {bin(n)} = {n}')
print(f'n & 1 (odd check) = {n & 1}') # 0: even
print(f'n & (n-1) (clear lowest bit) = {bin(n & (n-1))}')
print(f'n & -n (isolate lowest bit) = {bin(n & -n)}')
print(f'n | (1<<1) (set bit 1) = {bin(n | (1<<1))}')
print(f'n & ~(1<<2) (clear bit 2) = {bin(n & ~(1<<2))}')
print(f'n ^ (1<<5) (toggle bit 5) = {bin(n ^ (1<<5))}')
print(f'(n>>4) & 1 (check bit 4) = {(n>>4) & 1}')Contar bits activados (popcount)
Contar el número de bits 1 de un entero se denomina recuento de bits activados (popcount). El enfoque ingenuo recorre todos los bits. El truco de Brian Kernighan es más rápido: limpia repetidamente el bit activado de menor peso mediante n &= n - 1 y cuenta las iteraciones hasta que n se convierte en 0. Cada iteración elimina exactamente un bit 1, por lo que el bucle se ejecuta exactamente tantas veces como bits 1 haya.
Python 3.10+ proporciona int.bit_count(), que devuelve el recuento directamente. En versiones anteriores, el truco de Kernighan es el enfoque manual estándar. Esta técnica también resuelve el problema «Hamming Weight» de LeetCode.
# Method 1: naive O(log n)
def count_bits_naive(n):
count = 0
while n:
count += n & 1
n >>= 1
return count
# Method 2: Brian Kernighan O(k) where k = number of set bits
def count_bits_fast(n):
count = 0
while n:
n &= n - 1 # clear lowest set bit
count += 1
return count
# Method 3: Python built-in (3.10+)
# n.bit_count()
for x in [0, 1, 7, 255, 180, 1024]:
naive = count_bits_naive(x)
fast = count_bits_fast(x)
print(f'{x:4d} ({bin(x):10s}): naive={naive}, fast={fast}')Manipulación de bits en Python: aspectos importantes
A diferencia de C/Java, los enteros de Python tienen un tamaño arbitrariamente grande: no existe desbordamiento de 32 ni de 64 bits. Esto significa que debe enmascarar manualmente los resultados a un ancho fijo al resolver problemas que esperan un comportamiento de 32 bits: use & 0xFFFFFFFF para conservar únicamente los 32 bits inferiores.
El operador NOT ~n en Python devuelve -(n+1), no la versión con los bits invertidos que podría esperar de C. Para problemas de 32 bits, use ~n & 0xFFFFFFFF o calcule 0xFFFFFFFF ^ n para obtener el complemento de 32 bits esperado. Estas diferencias confunden a muchos candidatos acostumbrados a la manipulación de bits al estilo de C.
# Python vs C gotchas
# In C: unsigned 32-bit NOT of 5 = 4294967290
# In Python: ~5 = -6
print(f'Python ~5 = {~5}') # -6
print(f'32-bit ~5 = {~5 & 0xFFFFFFFF}') # 4294967290
# No integer overflow in Python
big = 1 << 100 # 2^100: huge number, no overflow
print(f'2^100 = {big}') # works fine
# Right shift on negatives: arithmetic (sign-extending)
print(f'-1 >> 3 = {-1 >> 3}') # -1 (all ones shifted in)
# Safe 32-bit mask for problems expecting C/Java semantics
MASK32 = 0xFFFFFFFF
result = (5 + 0xFFFFFFFE) & MASK32 # simulates 32-bit overflow
print(f'5 + (-2) in 32-bit = {result}') # 3Operadores de desplazamiento y multiplicación
Los desplazamientos a la izquierda y a la derecha proporcionan una forma extremadamente rápida de multiplicar o dividir por potencias de dos. En el hardware, los desplazamientos de bits son operaciones de una sola instrucción, mientras que la multiplicación y la división requieren varios ciclos. En Python, la multiplicación de enteros ya es eficiente, pero comprender esta relación le ayuda a visualizar mejor los patrones de bits.
Una identidad útil: para comprobar si n es múltiplo de 2^k, use (n & (2^k - 1)) == 0. La máscara 2^k - 1 tiene todos los k bits inferiores activados; al aplicar AND con ella se obtiene el resto de dividir entre 2^k. Esto equivale a n % (2^k), pero es más rápido en lenguajes basados en C.
# Shift vs arithmetic equivalence
for k in range(1, 5):
n = 48
print(f'{n} * 2^{k} = {n * (2**k)} = {n << k} (left shift)')
print(f'{n} // 2^{k} = {n // (2**k)} = {n >> k} (right shift)')
print()
# Check divisibility by power of 2
def divisible_by_power_of_2(n, k):
mask = (1 << k) - 1 # 2^k - 1: lower k bits all 1
return (n & mask) == 0
for n in [16, 24, 32, 15, 100]:
print(f'{n} divisible by 4? {divisible_by_power_of_2(n, 2)}')Comprobación rápida
Ponga a prueba su comprensión de los conceptos de Data Structures & Algorithms — Coding Interview Prep de esta lección.
Resumen de la lección
En esta lección ha aprendido que: AND enmascara bits, OR los activa, XOR los alterna y detecta diferencias, NOT los invierte (produce -(n+1) en Python), y los desplazamientos multiplican o dividen por potencias de dos, n & (n-1) limpia el bit activado de menor peso y sirve de base para comprobar potencias de dos y contar bits, y Python no tiene desbordamiento de ancho fijo, por lo que los problemas de 32 bits requieren un enmascaramiento explícito con & 0xFFFFFFFF. A continuación exploraremos la propiedad de autoinversión de XOR para resolver la familia de problemas del número único.
Preguntas frecuentes
¿La lección «Operadores bit a bit: AND, OR, XOR, NOT y desplazamientos» es gratis?
Sí — el texto completo de «Operadores bit a bit: AND, OR, XOR, NOT y desplazamientos» es gratis para leer aquí en la web. Para practicarla de forma interactiva (editor de código integrado y tutor de IA 24/7) y desbloquear el resto del curso de Coding Interview Prep, actualiza a CoddyKit PRO. El curso de Coding Interview Prep incluye 4 lecciones en total.
¿Qué aprenderé en «Operadores bit a bit: AND, OR, XOR, NOT y desplazamientos»?
Repase los seis operadores bit a bit con tablas de verdad y ejemplos en Python, y comprenda cómo los desplazamientos a izquierda y derecha se relacionan con multiplicar y dividir entre dos. Practicas Coding Interview Prep con código real que ejecutas directamente en el navegador, y un tutor de IA 24/7 responde tus preguntas mientras trabajas en la lección.
¿Necesito experiencia previa para empezar Coding Interview Prep?
No se requiere experiencia previa. Coding Interview Prep en CoddyKit está estructurado para principiantes hasta estudiantes avanzados, así que puedes empezar aquí o desde el inicio y avanzar a tu ritmo. Esta es la lección 1 de 4.
¿Cuánto tiempo toma la lección «Operadores bit a bit: AND, OR, XOR, NOT y desplazamientos»?
La mayoría de las lecciones de CoddyKit toman alrededor de 5–10 minutos. Cada una es compacta e interactiva, así que avanzas constantemente y retomas exactamente por donde dejaste en la web y la app.
¿Puedo escribir y ejecutar código en esta lección de Coding Interview Prep?
Sí. Cada lección de Coding Interview Prep incluye un editor de código integrado, así que escribes y ejecutas código real directamente en tu navegador y obtienes retroalimentación instantánea de IA — sin configuración local necesaria.
Todas las lecciones de este curso
- Operadores bit a bit: AND, OR, XOR, NOT y desplazamientos
- Single Number y propiedades de XOR
- Máscaras de bits: establecer, borrar, alternar y comprobar
- Contar bits, número ausente e invertir bits