Переменные типа int хранятся в двоичной системе счисления в виде последовательности бит. Биты нумеруются от 0, биты будем записывать справа налево (то есть бит с номером 0 будет записан самым правым, а самый старший бит — самым левым).
a = 0 # 0b0 a = 1 # 0b1 a = 2 # 0b10 a = 10 # 0b1010 a = 255 # 0b11111111
Например, если a = 10
, то в битовой записи
a
биты с номерами 1 и 3 равны 1,
а остальные биты равны 0.
В программах на языке Питон числа в двоичной системе счисления можно записывать
в виде последовательностей из 0 и 1, предваряя их префиксом 0b
.
Например, допустимо присваивание a = 0b101
.
Для двух переменных одинакового скалярного типа
определены битовые операции:
&
битовое И (AND)
|
битовое ИЛИ (OR)
^
битовое ИСКЛЮЧАЮЩЕЕ ИЛИ (XOR)
~
битовое ОТРИЦАНИЕ (NOT) — унарная операция.
Битовые операторы работают следующим образом. Берутся два операнда, и к каждой паре соответствующих бит для левого и правого операнда применяется данная операция, результатом будет переменная того же типа, каждый бит которой есть результат применения соответствующей логической операции к соответствующим битам двух операндов. Рассмотрим пример:
a = 5 # 0b101 b = 6 # 0b110 c = a & b # 0b100 == 4 d = a | b # 0b111 == 7 e = a ^ b # 0b11 == 3 f = ~ a # 0b1...11111010 == -6
Битовое отрицание числа (величина f
в последнем примере) — это число, полученное
из исходного заменой всех нулей на единицы и наоборот.
Применение побитового отрицания к неотрицательному числу даст отрицательное число, что связано с особенностями представления отрицательных чисел в виде дополнительного кода.
Есть еще две операции, работающие с битами: это битовые сдвиги.
Их два: сдвиг влево и вправо.
Оператор a >> n
возвращает число,
которое получается из a
сдвигом всех бит на
n
позиций вправо, при этом самые правые
n
бит отбрасываются.
Например:
a = 43 # 0b101011 b = a >> 1 # 0b10101 == 21 c = a >> 2 # 0b1010 == 10 d = a >> 3 # 0b101 == 5 e = a >> 5 # 0b1 == 1
Понятно, что для положительных чисел битовый сдвиг числа
вправо на n
равносилен целочисленному делению
на 2n. Для отрицательных чисел в языке Питон операции
битового сдвига неприменимы.
Аналогично, битовый сдвиг влево на n
бит равносилен (для положительных чисел) умножению на
2n и осуществляется при помощи оператора <<
:
a = 5 # 0b101 b = a << 1 # 0b1010 == 10 c = a << 2 # 0b10100 == 20 d = a << 3 # 0b101000 == 40
n &= k n |= k n ^= k n <<= k n >>= k
Упражнения
Во всех упражнениях (если не оговорено иное) нельзя использовать
арифметические операторы сложения, умножения, вычитания,
деления, взятия остатка.
Вместо них используем побитовые операторы &
, |
,
~
, ^
, <<
, >>
.
01: 2k
Дано число k, 0≤k≤31. Запишите число 2k, то есть число, у которого k-й бит равен 1, а остальные — нули.
Ввод | Вывод |
---|---|
8 |
256 |
02: 2k+2n
Даны два неравных целых неотрицательных числа: k и n. Вычислите 2k+2n.
Ввод | Вывод |
---|---|
0 1 |
3 |
03: Обнулить последние биты
Дано целое число A и целое неотрицательное число число k. Обнулите у числа A его последние k бит и выведите результат.
Вычисления оформите в виде функции clear_lower_bits(A, k)
.
Ввод | Вывод |
---|---|
3 1 |
2 |
04: Установить бит
Дано целое число A и целое неотрицательное число k. Выведите число, которое получается из числа A установкой значения k-го бита равному 1. Вычисления оформите в виде функцииset_bit(A, k)
.
Ввод | Вывод |
---|---|
12 1 |
14 |
05: Инвертировать бит
Дано целое число A и целое неотрицательное число k. Выведите число, которое получается из числа A инвертированием k-го бита. Вычисления оформите в виде функцииtoggle_bit(A, k)
.
Ввод | Вывод |
---|---|
15 2 |
11 |
06: Значение бита
Дано целое число A и целое неотрицательное число k. Выведите значение k-го бита числа A, то есть 0 или 1. Вычисления оформите в виде функцииtest_bit(A, k)
.
Ввод | Вывод |
---|---|
179 0 |
1 |
07: Обнулить бит
Дано целое число A и целое неотрицательное число k. Выведите число, которое получается из числа A установкой значения k-го бита равному 0. Вычисления оформите в виде функцииclear_bit(A, k)
.
Ввод | Вывод |
---|---|
14 1 |
12 |
08: Обрезать старшие биты
Дано целое число A и натуральное число k. Выведите число, которое состоит только из k последних бит числа A (то есть обнулите все биты числа A, кроме последних k). Вычисления оформите в виде функцииclear_high_bits(A, k)
.
Ввод | Вывод |
---|---|
126 3 |
6 |
09: Битовое представление
Дано натуральное число. Выведите его битовое представление.
В этой задаче можно использовать циклы, но нельзя использовать операции
деления и умножения, а также любые операции со строками, кроме метода .join
.
Вычисления оформите в виде функции int2bin(n)
.
Ввод | Вывод |
---|---|
179 |
10110011 |
10: Битовая длина
Дано натуральное число. Выведите длину его битового представления.
В этой задаче можно использовать циклы, но нельзя использовать операции
деления и умножения, а также любые операции со строками.
Вычисления оформите в виде функции bit_length(n)
.
Ввод | Вывод |
---|---|
179 |
8 |
183765996899 |
38 |
11: Число единиц в битовой записи
Дано натуральное число. Выведите число единиц в его битовом представлении.
В этой задаче можно использовать циклы, но нельзя использовать операции
деления и умножения, а также любые операции со строками.
Вычисления оформите в виде функции bit_count(n)
.
Ввод | Вывод |
---|---|
179 |
5 |
183765996899 |
19 |
12: Число единиц в битовой записи — 2
Решите предыдущую задачу так, чтобы число повторений в цикле не превосходило число единиц в битовой записи числа.
13: Битовый обмен
В переменных x
и y
хранятся два натуральных числа.
Необходимо обменять значения переменных местами, используя только битовые операции.
Операции вида x, y = y, x
, а также дополнительные переменные использовать запрещается.
Вычисления оформите в виде функции swap_numbers(x, y)
.
Ввод | Вывод |
---|---|
9 17 |
17 9 |
14: Быстрое вычисление
Даны числа a и b. Используя только битовые операции и операции сложения и вычитания вычислите число x=(18a+[b16])mod. Выведите результат на экран.
Ввод | Вывод |
---|---|
1 2 |
18 |
2 16 |
5 |
15: Быстрое умножение
Даны числа a и b. Не используя операции *
, /
, //
, %
вычислите их произведение.
Ввод | Вывод |
---|---|
2 3 |
6 |
16: Заменить 1 на 0
Дано число, замените первую справа единицу его двоичной записи на ноль.
Разрешается использовать битовые и арифметические операции.
Запрещается использовать ветвления и циклы.
Вычисления оформите в виде функции lowest_clear(n)
.
Ввод | Вывод |
---|---|
1 |
0 |
6 |
4 |
17: Замените 0 на 1
Дано число, замените первый справа ноль его двоичной записи на единицу.
Разрешается использовать битовые и арифметические операции.
Запрещается использовать ветвления и циклы.
Вычисления оформите в виде функции lowest_set(n)
.
Ввод | Вывод |
---|---|
0 |
1 |
5 |
7 |
18: Шифрование перемешиванием
Дано число, переставьте его соседние биты (то есть поменяйте местами биты с номерами 0 и 1, 2 и 3, 4 и 5 и т.д.). Разрешается использовать битовые операции. Запрещается использовать арифметические операции, ветвления, циклы.
Общее число бит в числе не превосходит 32.
Ввод | Вывод |
---|---|
78 |
141 |
19: Из дополнительного кода
Поскольку в языке Питон встроенная целочисленная арифметика является длинной, то создается иллюзия того, что целые
числа имеют бесконечное число разрядов. При этом у положительных чисел лидирующие разряды в двоичной системе счисления
заполнены битом 0, а у отрицательных чисел — битом 1.
Этот факт мы будем записывать следующим образом: символы “0~
” будут обозначать бесконечное
число нулевых бит, а символы “1~
” бесконечное число единичных бит.
То есть число 5 в дополнительном коде мы будем записывать, как 0~101
.
Для записи отрицательных чисел используется дополнение до двойки: пусть n — натуральное число, а число k минимальное из тех, для которых 2^k \ge n. Тогда для записи числа -n в дополнительном коде используется битовая запись числа 2^k - n (с ведущими нулями), перед которой записано бесконечное число битов 1. То есть число -5 будет записываться как 1~011
.
В такой записи бит, следующий после знака ~
должен отличаться от бита, идущего до него, то
есть запись 0~0101
или 1~11011
считается неправильной. Исключениями
являются числа 0 (записывается как 0~0
) и -1 (записыватеся как 1~1
).
Дана запись ненулевого числа в дополнительном коде, в соответствии с указанным выше форматом. Определите значение записанного числа.
Вычисления оформите в виде функции twos_complement2int(code)
.
Ввод | Вывод |
---|---|
0~101 |
5 |
1~011 |
-5 |
20: В дополнительный код
Решите задачу, обратную предыдущей.
Вычисления оформите в виде функции int2twos_complement(n)
.
Ввод | Вывод |
---|---|
5 |
0~101 |
-5 |
1~011 |
21: Кого потеряли
Дана последовательность из n натуральных чисел, в которой все числа, за исключением некоторого одного, повторяются два раза. Найдите это число. Время работы алгоритма должно быть O(n). Число n может быть порядка 10^6.
Ввод | Вывод |
---|---|
1 2 3 2 1 |
3 |
1 17 9 20 17 179 9 1 20 |
179 |
22: Кто лишний
Дана последовательность из n+1 натурального числа от 1 до n, ровно одно число повторяется дважды. Найдите это число. Время работы алгоритма должно быть O(n). Число n может быть порядка 10^6.
Ввод | Вывод |
---|---|
1 2 3 2 |
2 |
5 1 4 2 3 4 |
4 |
23: Контрольная сумма BSD
В операционной системе BSD используется следующий алгоритм вычисления контрольных сумм.
Контрольная сумма хранится в двубайтовой целочисленной переменной h (то есть хранятся только два байта числа, все вычисления выполняются в кольце вычетов по модулю 2^{16}. С самого начала эта переменная равна 0.
Далее с каждым считанным байтом c выполняются следующие операции.
Значение h циклически сдвигается вправо (то есть последний бит становится первым, не забываем, что число h является 16-битным. К значению h прибавляется значение считанного байта (то есть ASCII-кода его), от результата берется последние 16 бит.
Вот пример вычисления контрольной суммы для строки “AND“
h = 0b0000000000000000 - начальная инициализация h = 0b0000000000000000 - циклический сдвиг вправо h = 0b0000000001000001 - добавили 65 - ASCII-код A h = 0b1000000000100000 - циклический сдвиг вправо h = 0b1000000001101110 - добавили 78 - ASCII-код N h = 0b0100000000110111 - циклический сдвиг вправо h = 0b0100000001111011 - добавили 68 - ASCII-код D
Результат равен 16507. Обратите внимание, что после сложения результат может оказаться более чем 16-битным, и требуется оставить только последние 16 бит.
Вычисления оформите в виде функции BSD(data)
.
Ввод | Вывод |
---|---|
AND |
16507 |
Hello, world! |
37288 |
24: Adler-32
В алгоритме Adler-32 вычисляются две 16-битные суммы: A — сумма всех байт входных данных и B — сумма всех промежуточных значений суммы A. При этом начальное значение A инициализируется числом 1, а начальное значение B — числом 0. Все вычисления проводятся по модулю 65521 (максимальное простое, меньшее 2^{16}).
Таким образом, если файл состоит из байт d_1, d_2, ..., d_n, то A = 1 + d_1 + d_2 + ... + d_n \bmod 65521, B = (1 + d_1) + (1 + d_1 + d_2) + ... + (1 + d_1 + ... + d_n) \bmod 65521.
Итоговым значением контрольной суммы является одно 32-битное число, в котором в старших 16 битах записано значение B, в младших 16 битах - значение A.
Вычислите контрольную сумму Adler-32 для данной строки.
Вычисления оформите в виде функции adler(data)
.
Ввод | Вывод |
---|---|
AND |
27656404 |
Hello, world! |
543032458 |
25: FNV-1 хеш-функция
Алгоритм хеширования FNV-1 устроен следующим образом. Используется 64-битная арифметика. Переменная h хранит текущее значение хеш-функции. Начальное значение h равно 14695981039346656037. На каждом шаге значение h домножается на 1099511628211, затем делается побитовое исключающее ИЛИ с очередным байтом входных данных. Все вычисления проводятся с 64-битными целыми числами, поэтому после выполнения всех операций нужно брать младшие 64 бита результата.
Вычислите значение хеш-функции FNV-1 для данной строки.
Вычисления оформите в виде функции FNV(data)
.
Ввод | Вывод |
---|---|
AND |
15595937027161525016 |
Hello, world! |
7285062107457560934 |
26: PJW-32 хеш-функция
Алгоритм хеширования PJW-32 устроен следующим образом. Используется 32-битная арифметика. Переменная h хранит текущее значение хеш-функции. Далее для каждого считанного байта с сообщения выполняются следующие операции:
1. Значение h сдвигается на 4 бита влево, к нему прибавляется (арифметическим суммированием) значение c.
2. Если хотя бы один из 4 старших битов h равен 1, то старшие 4 бита сдвигаются на 24 бита вправо, и выполняется операция побитового исключающего ИЛИ со значением h. После чего обнуляются старшие 4 бита значения h.
Все операции проводятся с 32-битными числами, то есть берутся 32 младших бита результата.
Вычисления оформите в виде функции PJW(data)
.
Ввод | Вывод |
---|---|
AND |
17956 |
27: Ханойские башни без рекурсии
Головоломка “Ханойские башни” состоит из трех стержней, пронумерованных числами 1, 2, 3. На стержень 1 надета пирамидка из n дисков различного диаметра в порядке возрастания диаметра. Диски можно перекладывать с одного стержня на другой по одному, при этом диск нельзя класть на диск меньшего диаметра. Необходимо переложить всю пирамидку со стержня 1 на стержень 3 за минимальное число перекладываний.
Напишите программу, которая решает головоломку без использования рекурсии;
для данного числа дисков n печатает последовательность перекладываний в формате
a b c
, где a
— номер перекладываемого диска,
b
— номер стержня с которого снимается данный диск,
c
— номер стержня на который надевается данный диск.
Например, строка 1 2 3
означает перемещение диска номер 1 со стержня
2 на стержень 3. В одной строке печатается одна команда.
Диски пронумерованы числами от 1 до n в порядке возрастания диаметров.
Программа должна вывести минимальный (по количеству произведенных операций) способ перекладывания пирамидки из данного числа дисков.
Указание: попробуйте связать операцию, которую необходимо выполнить на шаге i с записью числа i в двоичной системе счисления.
Вычисления оформите в виде функции hanoi(n)
.
Ввод | Вывод |
---|---|
2 |
1 1 2 |
28: SHA-1
SHA-1 — алгоритм, вычисляющий криптографические контрольные суммы от произвольной битовой последовательности. Результатом вычисления функции SHA-1 является 160-битный хэш, который как правило записывается в виде 40 16-ричных цифр. Хэш-функция является односторонней, то есть по значению хэш-функции тяжело подобрать какую-либо исходную последовательность, имеющую такое значение хэш-функции.
Изучите описание алгоритма вычисления контрольной суммы SHA-1 по материалам википедии и реализуйте данный алгоритм.
Программа получает на вход одну строку и должна вывести значение SHA-1 суммы
для этой строки. Исходная последовательность байт для вычисления SHA-1 суммы
состоит только из символов этой строки, так в примере ниже входная строка
имеет длину 3 байта = 24 бита. Добавлять к строке символ \n
и иные спецсимволы не нужно.
Программа должна вывести 40 16-ричных цифр, цифры a
-f
записываются в строчном регистре.
Ввод | Вывод |
---|---|
da39a3ee5e6b4b0d3255bfef95601890afd80709 |
|
sha |
d8f4590320e1343a915b6394170650a8f35d6926 |
Hello, world! |
943a702d06f34599aee1f8da8ef9f7296031d699 |
................................................................. |
e63aa2689b774a507aba9433e4a0c661586990a4 |
Обратите внимание, вам достаточно реализовать чуть более простой вариант SHA-1, который работает только в случае, когда исходное сообщение состоит из целого числа байт, в то время как спецификация SHA-1 описывает алгоритм, который получает на вход последовательность бит произвольной длины, не обязательно кратной 8.
Вычисления оформите в виде функции SHA(data)
.