Operatory Bitowe (&, |, ^, <<, >>) na Maturze z Informatyki
Operatory bitowe to specjalne operatory, które nie działają na liczbie jako całości (np. 123), ale bezpośrednio na jej bitach (czyli jej reprezentacji binarnej, np. 1111011). Zamiast wykonywać dodawanie czy mnożenie, wykonują operacje logiczne (AND, OR, XOR) na każdej parze bitów jednocześnie. Na przykład, porównując 5 (101) i 3 (011), porównują pierwszy bit z pierwszym, drugi z drugim itd. Są to operacje 'niskopoziomowe', co oznacza, że są ekstremalnie szybkie – często wykonywane w jednym cyklu procesora. Na maturze pozwalają na sprytne i wydajne rozwiązywanie niektórych zadań, zwłaszcza tych 'na papierze'.
Dlaczego to ważne? Musisz znać operatory bitowe, aby rozwiązać specyficzne zadania maturalne, które wprost o nie pytają (jak zadanie z XOR w 2023 roku). Są też fundamentem do zrozumienia, jak komputer przechowuje dane, jak działają maski bitowe (np. do sprawdzania, czy dany bit jest 'zapalony') oraz do wykonywania superszybkich operacji mnożenia i dzielenia przez potęgi dwójki (za pomocą przesunięć bitowych).
Teoria
Główna idea polega na traktowaniu liczb jako ciągów bitów. Operacja bitowa jest stosowana 'kolumnowo' do każdej pary bitów. Np. dla 5 & 3:
101 (5)
& 011 (3)
----
001 (1)
Dzieje się tak, bo: 1&0=0, 0&1=0, 1&1=1.
Jak to działa?
&(Bitowe AND): Zwraca 1 tylko jeśli oba bity na tej samej pozycji to 1.|(Bitowe OR): Zwraca 1 jeśli przynajmniej jeden z bitów na tej pozycji to 1.^(Bitowe XOR - 'albo'): Zwraca 1 jeśli bity na tej pozycji są różne.<<(Przesunięcie w lewo): Przesuwa wszystkie bity w lewo, dopisując zera z prawej.x << nto szybkie mnożenie x * 2^n.>>(Przesunięcie w prawo): Przesuwa wszystkie bity w prawo, 'wyrzucając' te z prawej.x >> nto szybkie dzielenie całkowite x // 2^n.~(Bitowe NOT): Odwraca wszystkie bity (0 na 1, 1 na 0). Rzadziej używane na maturze ze względu na komplikacje z liczbami ujemnymi (kod U2).
Złożoność: Wszystkie operatory bitowe są operacjami o czasie stałym, O(1) (lub O(k) gdzie k to liczba bitów w liczbie, np. 32/64, co jest stałą). Są to jedne z najszybszych operacji, jakie może wykonać procesor.
Implementacja
Operatory AND, OR, XOR
Pythona = 5 # binarnie: 101
b = 3 # binarnie: 011 (uzupełniamy zerem z przodu)
print(f"a = {a:03b}, b = {b:03b}") # :03b - binarnie, z zerami do 3 cyfr
# AND (&): 101 & 011 = 001
print(f"a & b = {a & b} ({a & b:03b})") # Wynik: 1 (001)
# OR (|): 101 | 011 = 111
print(f"a | b = {a | b} ({a | b:03b})") # Wynik: 7 (111)
# XOR (^): 101 ^ 011 = 110
print(f"a ^ b = {a ^ b} ({a ^ b:03b})") # Wynik: 6 (110)To podstawowe użycie. Python (i C++) wykonuje operacje na bitach. `101 & 011` daje `001`, ponieważ tylko ostatnia kolumna miała `1 & 1`. `101 | 011` daje `111`, bo w każdej kolumnie występował przynajmniej jeden bit `1`. Wyniki są pokazane zarówno dziesiętnie, jak i binarnie.
Przesunięcia bitowe (Shift) - Mnożenie i Dzielenie
Pythonn = 10 # binarnie: 1010
# Przesunięcie w lewo o 1 (mnożenie przez 2^1 = 2)
# 1010 -> 10100 (z 10 robi się 20)
print(f"{n} << 1 = {n << 1}") # Wynik: 20
# Przesunięcie w lewo o 3 (mnożenie przez 2^3 = 8)
# 1010 -> 1010000 (z 10 robi się 80)
print(f"{n} << 3 = {n << 3}") # Wynik: 80
# Przesunięcie w prawo o 1 (dzielenie całkowite przez 2^1 = 2)
# 1010 -> 101 (z 10 robi się 5)
print(f"{n} >> 1 = {n >> 1}") # Wynik: 5
# Przesunięcie w prawo o 2 (dzielenie całkowite przez 2^2 = 4)
# 1010 -> 10 (z 10 robi się 2)
print(f"{n} >> 2 = {n >> 2}") # Wynik: 2Przesunięcia bitowe to superszybki sposób na mnożenie i dzielenie przez potęgi dwójki. `n << k` to to samo co `n * (2**k)`. `n >> k` to to samo co `n // (2**k)`.
Wzorzec: Maska bitowa (sprawdzanie bitu)
Python# Jak sprawdzić, czy 3. bit (licząc od 0) w liczbie n jest '1'?
# 3. bit odpowiada wartości 2^3 = 8 (binarnie: 1000)
# To jest nasza 'maska'
n = 13 # binarnie: 1101
maska = 1 << 3 # To jest 8, czyli binarnie 1000
# Używamy AND. Jeśli wynik jest > 0, to bit był zapalony.
# 1101 (13)
# & 1000 (maska 8)
# ------
# 1000 (8, co jest > 0)
if (n & maska) > 0:
print("3. bit jest ustawiony (1)")
else:
print("3. bit jest 0")
# Sprawdzenie bitu 0 (wartość 2^0 = 1)
if (n & 1) == 0:
print("Liczba jest parzysta")
else:
print("Liczba jest nieparzysta")To najważniejsze zastosowanie operatora `&`. Tworzymy 'maskę' z jedną jedynką na pozycji, którą chcemy sprawdzić (używając przesunięcia `1 << k`). Operacja `n & maska` 'wygasza' wszystkie bity oprócz tego, który nas interesuje. Jeśli wynik jest niezerowy, to bit był ustawiony.
Przykładowe Zadania Maturalne
Napisz program, który wczyta z pliku 'liczby.txt' 200 liczb całkowitych (każda w nowej linii). Podaj, ile z tych liczb ma dokładnie jeden ustawiony bit '1' w swojej reprezentacji binarnej (są to potęgi liczby 2, np. 1, 2, 4, 8, 16...).
Wskazówka: Liczba jest potęgą dwójki (ma jeden bit '1'), jeśli spełnia sprytny warunek bitowy: n > 0 AND (n & (n - 1)) == 0. Na przykład dla n=8: n = 1000, n-1 = 0111. 1000 & 0111 = 0000. Dla n=6: n = 110, n-1 = 101. 110 & 101 = 100 (nie jest zerem).
Pokaż szkic rozwiązania
1. Otwórz plik 'liczby.txt'.
2. Zainicjuj `licznik_poteg_dwojki = 0`.
3. Dla każdej linii w pliku:
a. Wczytaj `n = int(linia.strip())`.
b. Sprawdź warunek: `if n > 0 and (n & (n - 1)) == 0:`
c. ` licznik_poteg_dwojki += 1`
4. Wypisz `licznik_poteg_dwojki`.
5. (Alternatywnie: `if bin(n).count('1') == 1:`)Oblicz (123_10 XOR 101101_2) XOR 2D_16. Wynik podaj w systemie dziesiętnym.
Wskazówka: To zadanie 'na papierze', które wprost testuje znajomość operatora XOR. Aby je wykonać, musisz sprowadzić wszystkie trzy liczby do jednego, wspólnego systemu (np. dziesiętnego lub binarnego) i wykonać operacje po kolei.
Pokaż szkic rozwiązania
1. Zamień wszystkie liczby na system dziesiętny: a. `123_10` = 123. b. `101101_2` = $1*32 + 0*16 + 1*8 + 1*4 + 0*2 + 1*1 = 32 + 8 + 4 + 1 = 45$. c. `2D_16` = $2*16 + 13*1 = 32 + 13 = 45$ (ponieważ D=13). 2. Wykonaj operacje XOR (w Pythonie/C++ to operator `^`): a. Oblicz pierwszy nawias: `123 ^ 45`. b. `123` to `1111011_2`. c. `45` to `0101101_2`. d. `1111011 ^ 0101101 = 1010110_2`. e. `1010110_2` to $64 + 16 + 4 + 2 = 86$. f. Oblicz drugą operację: `86 ^ 45`. g. `86` to `1010110_2`. h. `45` to `0101101_2`. i. `1010110 ^ 0101101 = 1111011_2`. 3. Zamień wynik z powrotem na dziesiętny (jeśli liczysz binarnie): a. `1111011_2` to $64 + 32 + 16 + 8 + 2 + 1 = 123$. 4. Odpowiedź: 123.
Częste Błędy
❌ Mylenie operatorów bitowych (&, |) z logicznymi (and, or)
To krytyczny błąd! if x > 10 and y > 10 (logiczne) sprawdza dwa warunki. if x & y (bitowe) wykonuje operację na bitach. 4 and 2 da True (bo 2 jest 'prawdziwe'), ale 4 & 2 (100 & 010) da 0 (False).
Poprawka: Do łączenia warunków if używaj and, or. Do manipulacji bitami używaj &, |, ^.
❌ Python: Używanie ^ do potęgowania
W wielu językach (i matematyce) ^ oznacza potęgę. W Pythonie i C++ ^ to XOR. Do potęgowania w Pythonie służy operator **.
Poprawka: Zapamiętaj: 2 ^ 3 to 1 (bo 10 ^ 11 = 01), a 2 ** 3 to 8.
❌ Dziwne wyniki negacji (~)
Początkujący wpisuje ~5 (~101) i spodziewa się 010 (2). Zamiast tego dostaje -6. Dzieje się tak z powodu używania przez komputery kodu uzupełnień do dwóch (U2) do reprezentacji liczb ujemnych.
Poprawka: Unikaj operatora ~ na maturze, jeśli nie jesteś absolutnie pewien, jak działa kod U2. Do wyzerowania bitu lepiej użyć maski z & (np. n & 0b111011).
❌ Zła kolejność działań (precedens)
Operatory bitowe mają niższą kolejność niż arytmetyczne i czasem niższą niż porównania. Kod if n & 1 == 0: może być zinterpretowany jako if n & (1 == 0): czyli if n & 0:, co jest zawsze fałszem.
Poprawka: Zawsze, zawsze używaj nawiasów przy operacjach bitowych: if (n & 1) == 0:.
Kluczowe Wnioski
- Operatory bitowe
&,|,^działają na pojedynczych bitach liczby. &(AND) - 'zapala' bit, gdy oba są zapalone. Świetne do sprawdzania bitów (maskowanie).|(OR) - 'zapala' bit, gdy choć jeden jest zapalony. Świetne do ustawiania bitów.^(XOR) - 'zapala' bit, gdy są różne. Świetne do przełączania bitów.<< kto szybkie mnożenie przez 2^k.>> kto szybkie dzielenie całkowite przez 2^k.- Zawsze używaj nawiasów dla pewności:
(n & maska) == 0. - W Pythonie
^to XOR, a**to potęga.
Najczęściej Zadawane Pytania
❓ Jaka jest różnica między `&` a `and`?
`&` to operator *bitowy*: `5 & 3` (`101 & 011`) daje `1`. `and` to operator *logiczny*: `5 and 3` daje `True` (w Pythonie zwraca `3`, ale w kontekście `if` liczy się jako `True`). `&` działa na bitach, `and` na wartościach logicznych.
❓ Po co mi XOR? Jakie ma właściwości?
XOR ma dwie magiczne właściwości: 1) `x ^ x = 0` (XOR liczby z samą sobą daje zero). 2) `x ^ 0 = x` (XOR z zerem nie zmienia liczby). Jest też odwracalny: `(x ^ y) ^ y = x`. Używa się go w kryptografii, sumach kontrolnych i do 'przełączania' bitów.
❓ Jak sprawdzić, czy liczba jest parzysta za pomocą bitów?
Liczba jest parzysta, jeśli jej ostatni bit (bit $2^0$) jest równy 0. Można to sprawdzić warunkiem `if (n & 1) == 0:`. Jeśli wynik to 0, jest parzysta. Jeśli 1, jest nieparzysta. To jest (marginalnie) szybsze niż `n % 2 == 0`.
❓ Jak ustawić `k`-ty bit liczby `n` na 1?
Użyj operatora OR i maski. `n = n | (1 << k)`. To 'zapali' k-ty bit, nie ruszając pozostałych.
❓ Jak wyzerować `k`-ty bit liczby `n`?
Użyj operatora AND i zanegowanej maski. `n = n & ~(1 << k)`. Maska `~(1 << k)` to liczba, która ma same jedynki, *oprócz* zera na `k`-tej pozycji. Operacja AND zachowa wszystkie bity `n` i wyzeruje ten jeden.
❓
Chcesz opanować wszystkie tematy maturalne?
Dołącz do kursu i zyskaj dostęp do interaktywnych lekcji, edytora kodu i setek zadań.