Operatory bitowe: AND, OR, XOR, NOT i przesunięcia
Powtórzyć działanie wszystkich sześciu operatorów bitowych z użyciem tabel prawdy i przykładów w Pythonie oraz zrozumieć, jak przesunięcia w lewo i w prawo wiążą się z mnożeniem i dzieleniem przez dwa
Operatory bitowe: AND, OR, XOR, NOT i przesunięcia to bezpłatna lekcja DSA Interview Prep na CoddyKit. To lekcja 1 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej DSA Interview Prep, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs DSA Interview Prep zawiera 4 lekcji w sumie.
Dlaczego operacje bitowe mają znaczenie
Operacje bitowe pozwalają działać bezpośrednio na binarnej reprezentacji liczb całkowitych. Wiele problemów, które wydają się złożone, staje się trywialnych dzięki odpowiedniej sztuczce bitowej: można znaleźć brakującą liczbę w czasie O(n) i przy użyciu O(1) pamięci, zamienić wartości zmiennych bez zmiennej tymczasowej lub zwięźle zakodować podzbiory. Rekruterzy wykorzystują takie zadania do sprawdzania znajomości działania na niskim poziomie oraz kreatywnego myślenia.
Liczby całkowite w Pythonie mają dowolną precyzję — mogą być tak duże, jak pozwala na to pamięć — ale operacje bitowe zawsze stosują na poziomie sprzętowym standardową semantykę uzupełnienia do dwóch. Wszystkie sześć operatorów działa bit po bicie na binarnych reprezentacjach liczb całkowitych.
# 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 = 5Operator AND: maskowanie bitów
Operator AND (&) zwraca 1 tylko wtedy, gdy oba bity wejściowe mają wartość 1. Jego głównym zastosowaniem jest maskowanie: wybieranie określonych bitów liczby przy jednoczesnym wyzerowaniu wszystkich pozostałych. Aby sprawdzić, czy bit k jest ustawiony w liczbie n, należy obliczyć n & (1 << k) — jeśli wynik jest różny od zera, bit k ma wartość 1.
AND służy także do zerowania najmniej znaczącego ustawionego bitu: n & (n - 1) usuwa najbardziej prawy bit 1. Wykorzystuje się to do wydajnego zliczania ustawionych bitów oraz do sprawdzania, czy liczba jest potęgą dwójki (potęga dwójki ma dokładnie jeden ustawiony bit, więc 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}')Operator OR: ustawianie bitów
Operator OR (|) zwraca 1, jeśli co najmniej jeden bit wejściowy ma wartość 1. Jego głównym zastosowaniem jest ustawianie określonego bitu na 1 bez wpływania na pozostałe. Aby ustawić bit k w liczbie n, należy użyć n | (1 << k). Jedynka przesunięta na pozycję k włącza ten bit, a wszystkie pozostałe bity pozostają niezmienione, ponieważ operacja OR dowolnego bitu z 0 daje jego pierwotną wartość.
OR służy także do łączenia flag: jeśli flagi funkcji są reprezentowane jako pojedyncze bity, za pomocą OR można włączyć wiele flag. Na przykład READ | WRITE | EXECUTE łączy trzy bity uprawnień w jedną liczbę całkowitą.
# 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)}')Operator XOR: przełączanie i wykrywanie różnic
Operator XOR (^) zwraca 1, gdy bity wejściowe różnią się. XOR ma trzy ważne właściwości algebraiczne: a ^ a = 0 (takie same wartości wejściowe się znoszą), a ^ 0 = a (zero jest elementem neutralnym), a także jest przemienny i łączny. Dzięki tym właściwościom XOR jest podstawowym narzędziem do znajdowania unikalnych elementów.
XOR służy także do przełączania określonego bitu: n ^ (1 << k) odwraca bit k, pozostawiając pozostałe bity bez zmian. Jeśli bit k miał wartość 0, otrzyma wartość 1, a jeśli miał wartość 1, otrzyma wartość 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=7Operator NOT i uzupełnienie do dwóch
Operator NOT (~) odwraca wszystkie bity. W Pythonie ~n jest równe -(n+1) ze względu na reprezentację w uzupełnieniu do dwóch. Dla wielu osób jest to zaskakujące: ~5 = -6, a nie naiwnie oczekiwane 0b11111010. Liczby całkowite w Pythonie mają nieskończoną precyzję, więc odwrócenie wszystkich bitów liczby dodatniej daje wynik ujemny w uzupełnieniu do dwóch.
W praktyce operatora ~ rzadko używa się w Pythonie samodzielnie do operacji bitowych. Zamiast tego stosuje się go w połączeniu z AND do zerowania określonych bitów albo oblicza ~n & mask, gdzie maska ogranicza szerokość do określonej liczby bitów (np. & 0xFFFFFFFF dla 32 bitów).
# 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 onesPrzesunięcie w lewo: mnożenie przez potęgi dwójki
Operator przesunięcia w lewo (<<) przesuwa wszystkie bity o k pozycji w lewo, wypełniając zwolnione pozycje z prawej strony zerami. Jest to równoważne mnożeniu przez 2^k. Przesunięcie w lewo o 1 podwaja wartość, a przesunięcie o k mnoży ją przez 2^k.
W zadaniach rekrutacyjnych przesunięcia w lewo najczęściej służą do tworzenia masek bitowych: 1 << k tworzy liczbę, w której ustawiony jest tylko bit k. Stanowi to podstawę wszystkich operacji bitowych — ustawianie, zerowanie, przełączanie i sprawdzanie pojedynczych bitów zaczyna się od 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}') # 1024Przesunięcie w prawo: dzielenie przez potęgi dwójki
Operator przesunięcia w prawo (>>) przesuwa wszystkie bity o k pozycji w prawo, odrzucając k najbardziej prawych bitów. Jest to równoważne dzieleniu całkowitemu przez 2^k. Przesunięcie w prawo w Pythonie jest zawsze arytmetyczne: najbardziej lewe bity są wypełniane bitem znaku (0 dla liczby dodatniej, 1 dla liczby ujemnej).
Typowa sztuczka rekrutacyjna polega na wyodrębnieniu bitu k z liczby n za pomocą (n >> k) & 1. Bit k zostaje w ten sposób przesunięty na pozycję 0, a wszystkie pozostałe bity są odfiltrowywane za pomocą maski. Jest to najprostszy sposób sprawdzenia dowolnego określonego bitu bez konieczności obliczania i porównywania pełnej maski.
# 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)Ściągawka z praktycznych sztuczek bitowych
Oto zestawienie najczęściej spotykanych idiomów operacji bitowych, z którymi można się zetknąć podczas rozmów rekrutacyjnych. Należy zapamiętać te wzorce — pojawiają się wielokrotnie w dziesiątkach zadań:
n & 1— sprawdzenie, czy n jest nieparzysten & (n-1)— wyzerowanie najmniej znaczącego ustawionego bitun & -n— wyodrębnienie najmniej znaczącego ustawionego bitun | (1 << k)— ustawienie bitu kn & ~(1 << k)— wyzerowanie bitu kn ^ (1 << k)— przełączenie bitu k(n >> k) & 1— sprawdzenie bitu 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}')Zliczanie ustawionych bitów (popcount)
Zliczanie liczby bitów 1 w liczbie całkowitej nazywa się zliczaniem populacji (popcount). Naiwne podejście polega na iterowaniu po wszystkich bitach. Sztuczka Briana Kernighana jest szybsza: polega na wielokrotnym zerowaniu najmniej znaczącego ustawionego bitu za pomocą n &= n - 1 i zliczaniu iteracji aż do momentu, gdy n stanie się równe 0. Każda iteracja usuwa dokładnie jeden bit 1, więc pętla wykonuje się dokładnie tyle razy, ile jest bitów 1.
Python 3.10+ udostępnia metodę int.bit_count(), która bezpośrednio zwraca tę liczbę. W starszych wersjach standardowym ręcznym rozwiązaniem jest sztuczka Kernighana. Technika ta rozwiązuje także problem „Hamming Weight” na 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}')Operacje bitowe w Pythonie: ważne pułapki
W przeciwieństwie do C/Java liczby całkowite w Pythonie są dowolnie duże — nie występuje przepełnienie 32-bitowe ani 64-bitowe. Oznacza to, że podczas rozwiązywania problemów wymagających działania na 32 bitach trzeba ręcznie ograniczać wyniki za pomocą maski: należy użyć & 0xFFFFFFFF, aby zachować tylko 32 najmłodsze bity.
Operator NOT ~n w Pythonie zwraca -(n+1), a nie wersję z odwróconymi bitami, której można by oczekiwać na podstawie języka C. W problemach 32-bitowych należy użyć ~n & 0xFFFFFFFF albo obliczyć 0xFFFFFFFF ^ n, aby uzyskać oczekiwane 32-bitowe dopełnienie. Te różnice często sprawiają problemy kandydatom przyzwyczajonym do operacji bitowych w stylu języka 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}') # 3Operatory przesunięcia a mnożenie
Przesunięcia w lewo i w prawo zapewniają niezwykle szybki sposób mnożenia lub dzielenia przez potęgi dwójki. Na poziomie sprzętowym przesunięcia bitowe są operacjami wykonywanymi w jednej instrukcji, podczas gdy mnożenie i dzielenie wymagają wielu cykli. W Pythonie mnożenie liczb całkowitych jest już wydajne, ale zrozumienie tej zależności pomaga wyraźniej dostrzegać wzorce bitowe.
Przydatna zależność: aby sprawdzić, czy n jest wielokrotnością 2^k, należy użyć (n & (2^k - 1)) == 0. Maska 2^k - 1 ma ustawione wszystkie niższe k bitów; wykonanie na niej operacji AND daje resztę z dzielenia przez 2^k. Jest to równoważne n % (2^k), ale szybsze w językach opartych na 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)}')Szybki test
Sprawdź swoją znajomość zagadnień Data Structures & Algorithms — Coding Interview Prep z tej lekcji.
Podsumowanie lekcji
W tej lekcji poznali Państwo następujące zagadnienia: AND maskuje bity, OR ustawia bity, XOR przełącza bity i wykrywa różnice, NOT odwraca bity (w Pythonie daje wynik -(n+1)), a przesunięcia służą do mnożenia i dzielenia przez potęgi dwójki, n & (n-1) zeruje najmniej znaczący ustawiony bit i stanowi podstawę sprawdzania potęg dwójki oraz zliczania bitów, a także Python nie ma przepełnienia dla stałej szerokości, dlatego problemy 32-bitowe wymagają jawnego maskowania za pomocą & 0xFFFFFFFF. W następnej części wykorzystamy właściwość samoodwrotności XOR do rozwiązywania rodziny problemów z pojedynczą liczbą.
Często zadawane pytania
Czy lekcja „Operatory bitowe: AND, OR, XOR, NOT i przesunięcia” jest bezpłatna?
Tak — pełny tekst „Operatory bitowe: AND, OR, XOR, NOT i przesunięcia” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu DSA Interview Prep, przejdź na CoddyKit PRO. Kurs DSA Interview Prep zawiera 4 lekcji w sumie.
Co nauczysz się w „Operatory bitowe: AND, OR, XOR, NOT i przesunięcia”?
Powtórzyć działanie wszystkich sześciu operatorów bitowych z użyciem tabel prawdy i przykładów w Pythonie oraz zrozumieć, jak przesunięcia w lewo i w prawo wiążą się z mnożeniem i dzieleniem przez dwa Ćwiczysz DSA Interview Prep z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.
Czy potrzebuję doświadczenia, aby zacząć DSA Interview Prep?
Nie wymagamy żadnego doświadczenia. DSA Interview Prep w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 1 z 4.
Ile czasu zajmuje lekcja „Operatory bitowe: AND, OR, XOR, NOT i przesunięcia”?
Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.
Czy mogę pisać i uruchamiać kod w tej lekcji DSA Interview Prep?
Tak. Każda lekcja DSA Interview Prep zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.
Wszystkie lekcje w tym kursie
- Operatory bitowe: AND, OR, XOR, NOT i przesunięcia
- Single Number i właściwości XOR
- Maski bitowe: ustawianie, zerowanie, przełączanie i sprawdzanie
- Zliczanie bitów, brakująca liczba i odwracanie bitów