# теория · шаг 1 из 7
Код Грея: соседние числа отличаются одним битом
Коротко
Код Грея - порядок, в котором идут все 2ⁿ наборов из n битов так, что каждый следующий отличается от
предыдущего ровно в одном разряде. Номер x переводится в код одной строкой: x ^ (x >> 1). Обратно -
сворачиванием: каждый бит номера равен ^ всех битов кода не младше него. Какой именно бит меняется на
шаге k, говорит число хвостовых нулей у k. Отсюда два применения: датчики положения, которым нельзя
менять два бита разом, и перебор подмножеств, где каждое следующее получается из предыдущего одним
добавлением или удалением: 24 618 шагов вместо 135 213.
Если ты выпишешь трёхбитный код Грея, переведёшь номер в код и обратно и объяснишь, почему при переборе подмножеств в этом порядке сумму не надо считать заново, - листай до «Теперь сам».
Как думаешь, что прочитает датчик между 3 и 4
На валу стоит диск с тремя дорожками, каждая - чёрно-белые секторы; три фотоэлемента читают три бита и
получают номер сектора от 0 до 7. Вал поворачивается из сектора 3 в сектор 4: 011 сменяется на 100.
Прежде чем читать дальше, ответь себе: что прочтёт датчик, если опросить его ровно на границе? Три бита
меняются одновременно только на бумаге. На деле один фотоэлемент переключится чуть раньше других, и на
мгновение датчик покажет 111, 000, 110 - что угодно, включая сектор на противоположной стороне
диска.
На что это похоже
Кодовый замок с тремя переключателями, и ты хочешь перебрать все восемь положений, щёлкая как можно меньше. Если идти по обычному счёту - 000, 001, 010, - то между 001 и 010 придётся щёлкнуть двумя переключателями, а между 011 и 100 - всеми тремя. Всего одиннадцать щелчков на семь переходов.
А можно найти порядок, в котором на каждый переход - ровно один щелчок, семь на всё. Такой порядок и есть код Грея. Для датчика это значит, что на границе секторов меняется один бит, и что бы он ни прочитал - старое значение или новое, - ошибка не больше одного сектора.
Механизм: отражение, формула и обратный перевод
Строится код отражением. Для одного бита порядок - 0, 1. Чтобы получить порядок для двух битов, список
записывают дважды - прямо и в обратном порядке - и к первой половине приписывают слева ноль, ко второй -
единицу:
1 бит: 0 1
2 бита: 00 01 | 11 10 вторая половина - первая, прочитанная назад, с единицей впереди
3 бита: 000 001 011 010 | 110 111 101 100
Внутри каждой половины соседи отличаются одним битом - они взяты из меньшего кода. На стыке половин стоят два одинаковых слова с разным старшим битом. И последний элемент отличается от первого тоже одним битом: код замкнут в кольцо.
Код Грея числа x - слово, стоящее на месте x в этом порядке. Формула: g = x ^ (x >> 1). Каждый
бит кода - это ^ двух соседних битов номера: бит i кода равен единице, когда биты i и i + 1
номера различны.
x = 1 0 1 1 0 22
x >> 1 = 0 1 0 1 1
g = 1 1 1 0 1 старший бит переходит как есть
Почему соседи отличаются одним битом. Прибавление единицы к x переворачивает младший ноль и хвост
единиц под ним - участок вида 0111 становится 1000. Бит кода - это ^ двух соседних битов номера.
Внутри участка перевернулись оба соседа, и их ^ не изменился; выше участка не перевернулся ни один.
Изменилась одна пара: верхний бит участка и нетронутый бит над ним. В коде Грея это один бит - тот,
что соответствует верхнему биту участка. Его номер - число хвостовых нулей
у нового x.
Обратно. Из формулы: старший бит номера равен старшему биту кода, а каждый следующий - ^
предыдущего бита номера с очередным битом кода. Раскрыв цепочку, получаем, что бит i номера - ^ всех
битов кода от старшего до i-го.
Правило
Код Грея числа x: g = x ^ (x >> 1). Обратно: x = g ^ (g >> 1) ^ (g >> 2) ^ …, пока сдвиг не
обнулит g. Коды чисел k − 1 и k различаются в одном бите, и его номер равен числу хвостовых нулей
k. Последний код отличается от первого тоже одним битом.
Разбор: таблица, обратный перевод и какой бит меняется
def v_grej(x):
return x ^ x >> 1
def iz_greya(g):
x = g
g = g >> 1
while g > 0:
x = x ^ g
g = g >> 1
return x
for x in range(8):
g = v_grej(x)
print(x, bin(x)[2:].zfill(3), bin(g)[2:].zfill(3), iz_greya(g))
0 000 000 0
1 001 001 1
2 010 011 2
3 011 010 3
4 100 110 4
5 101 111 5
6 110 101 6
7 111 100 7
Третий столбец совпал с порядком, построенным отражением. Между секторами 3 и 4 из начала урока теперь
010 и 110 - один бит.
smena = []
for k in range(1, 16):
raznica = v_grej(k) ^ v_grej(k - 1)
smena.append(raznica.bit_length() - 1)
print(smena)
[0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0]
Номер меняющегося бита на шаге k - число хвостовых нулей k: у нечётных это 0, у 2 и 6 - единица, у 4
и 12 - двойка, у 8 - тройка. Младший бит меняется на каждом втором шаге, следующий - на каждом четвёртом.
Эту же последовательность выдаёт решение ханойских башен из первого раздела: на шаге k двигается диск с
номером, равным числу хвостовых нулей k.
iz_greya делает на один круг меньше, чем битов в коде. Для 32-битного слова есть вариант за пять
строк - сдвиги на 1, 2, 4, 8, 16, по образцу размазывания старшей единицы: x = x ^ x >> 1, затем
x = x ^ x >> 2 и так далее. Обратный перевод дают все пять строк вместе; одной первой мало.
Разбор: перебор подмножеств с одной правкой на шаг, и граница
Сколько подмножеств набора из 12 чисел дают сумму ровно 40? В программах ves - список чисел, cel -
нужная сумма; значения - в таблице ниже. Прямой способ - перебрать маски по порядку и для каждой
сложить её элементы:
n = len(ves)
podhodit = 0
for m in range(1 << n):
s = 0
for i in range(n):
if m >> i & 1:
s = s + ves[i]
if s == cel:
podhodit = podhodit + 1
print(podhodit)
В порядке кода Грея соседние подмножества отличаются одним элементом, и сумму не пересчитывают, а поправляют:
n = len(ves)
podhodit = 0
s = 0
g = 0
if s == cel:
podhodit = podhodit + 1
for k in range(1, 1 << n):
i = (k & -k).bit_length() - 1
g = g ^ 1 << i
if g >> i & 1:
s = s + ves[i]
else:
s = s - ves[i]
if s == cel:
podhodit = podhodit + 1
print(podhodit)
На шаге k переключается элемент с номером «число хвостовых нулей k»: если он появился в множестве -
его вес прибавляется, если исчез - вычитается.
веса 3, 34, 4, 12, 5, 2, 7, 8, 11, 9, 21, 13; цель 40
способ ответ шагов
сумма заново для каждой маски 42 135 213
порядок Грея, одна правка 42 24 618
Выигрыш - множитель n: вместо n · 2ⁿ действий стало 2ⁿ. В шагах тренажёра разница - в пять с
половиной раз, а не в двенадцать: шаг в порядке Грея - шесть строк, круг внутреннего цикла - меньше трёх. Он тем важнее, чем дороже пересчёт: если
вместо суммы проверяется связность графа после удаления рёбер или стоимость расписания, правка одного
элемента против пересчёта с нуля - это разница в порядок.
Граница. Код Грея уменьшает работу на шаг, но шагов по-прежнему 2ⁿ. Он не помогает там, где нужны не все
подмножества, а только нужного размера или только подмаски данной маски: соседние по Грею множества
имеют разный размер. И правка должна быть обратимой: сумму можно уменьшить вычитанием, а вот минимум
элементов множества при удалении элемента так просто не восстановить.
Что ломается без этого
Первое: обратный перевод той же формулой. g ^ (g >> 1) - не обратное преобразование: применённое к
коду, оно даёт третье число - из кода 110 числа 4 получается 101. Для чисел до 3 результат случайно
совпадает с номером, поэтому на маленьких примерах ошибка не видна. Обратный перевод сворачивает все сдвиги, а не один.
Второе: арифметика в коде Грея. Коды нельзя складывать и сравнивать как числа: 110 в коде Грея -
это 4, а 100 - это 7. Сначала перевод в обычный номер, потом действия.
Третье: счётчик между двумя схемами. Обычный двоичный счётчик, прочитанный схемой с другой тактовой частотой в момент смены нескольких битов, даёт значение, которого счётчик никогда не принимал. Указатели очередей между такими схемами передают в коде Грея ровно по причине из начала урока.
Четвёртое: код не замкнут при числе значений не степень двойки. Десять секторов вместо восьми - и отражённый код из четырёх битов, обрезанный до десяти значений, на стыке последнего с первым меняет больше одного бита. Для таких случаев берут середину шестнадцатизначного кода: коды чисел от 3 до 12.
Зачем это в работе
Датчики угла и линейного положения - от станков до колёсика мыши - выдают код Грея. Тот же довод работает в цифровых схемах всюду, где сигнал из нескольких проводов читают в непредсказуемый момент.
Перебор с одной правкой на шаг: полный перебор подмножеств в задачах на разбиение, перебор состояний при тестировании («включи и выключи каждую комбинацию опций, переключая по одной»), обход всех углов многомерного куба по рёбрам.
Карта Карно, по которой упрощают логические формулы, нумерует строки и столбцы кодом Грея, чтобы соседние клетки отличались одной переменной. А в генетических алгоритмах числа кодируют кодом Грея, чтобы маленькое изменение числа было маленьким изменением записи: в обычном коде переход от 7 к 8 переворачивает четыре бита.
Теперь сам
- Найди код Грея числа 13.
- Какому числу соответствует код
1011? - Какой бит изменится при переходе от кода числа 11 к коду числа 12?
- Выпиши двухбитный код и построй из него трёхбитный отражением.
- Перебор подмножеств в порядке Грея пересчитывает не сумму, а произведение элементов. Что пойдёт не так, если среди элементов есть ноль?
Ответ. 1. 13 = 1101, 13 >> 1 = 0110, 1101 ^ 0110 = 1011.
-
Сворачивание:
1011 ^ 0101 ^ 0010 ^ 0001 = 1101, это 13. Или по битам сверху: 1;1 ^ 0 = 1;1 ^ 1 = 0;0 ^ 1 = 1. -
У 12 два хвостовых нуля - меняется бит 2. Проверка: код 11 -
1110, код 12 -1010. -
00, 01, 11, 10. Трёхбитный:000, 001, 011, 010, затем те же в обратном порядке с единицей:110, 111, 101, 100. -
Добавление нуля обнуляет произведение, а «убрать» его делением нельзя: деление на ноль. Правка необратима, и для такого элемента произведение придётся пересчитать заново.
Главное
Код Грея отвечает на вопрос, который в обычном счёте не возникает: сколько разрядов меняется при шаге на единицу. В двоичном счёте - от одного до всех, в коде Грея - всегда один. Поэтому его берут там, где промежуточное состояние между двумя соседними числами опасно, и там, где следующий вариант выгодно получать из предыдущего малой правкой, а не строить с нуля.
Комментарии
Пока нет комментариев. Будь первым!
Оставить комментарий