Часто порядок генерации кортежей бывает важен.
Разберем здесь генерацию бинарных строк таким образом, чтобы каждая следующая строка отличалась от предыдущей ровно
в однои бите.
Такой способ кодирования называется кодом Грея.
Пусть Γn — последовательность кодов Грея длины n.
Тогда Γ0=ε и
Γn+1=0Γn,1ΓnR
где aX означает новую последовательность,
полученную путём присоединения a перед всеми членами последовательности X,
и XR означает новую последовательность,
полученную путём разворачивания всех членов последовательности X как строк.
Поскольку Γn+1 начинается с 0Γn, то последовательность
Γ∞=g(0),g(1),g(2),g(3),g(4),…=0,1,11,10,110,…
является перестановкой чисел N0, задающей коды Грея по их индексам.
Последовательность кодов Грея можно определить, явно указав члены последовательности Γ∞,
каждый член g(j) — код Грея с индексом j.
Пусть j=2k+r, где 0⩽r<2k.
Тогда можно воспользоваться рекуррентным соотношением и получить, что
g(2k+r)=2k+g(2k−r−1)
Отсюда можно вывести приятную формулу вычисления g(j).
Пусть двоичное представление j=(⋯j2j1j0)2,
и двоичное представление g(j)=(⋯g2g1g0)2.
Тогда
gk=jk⊕jk+1длявсехk⩾0
Имеет место и обратная формула
jk=gk⊕gk+1⊕gk+2⊕gk+3⊕⋯=i=0⨁∞gk+iдлявсехk⩾0
С помощью этих двух формул можно получить способ
очень легко и быстро вычислять код Грея и номер кода Грея с помощью битовой арифметики
g(j)=j⊕⌊j/2⌋
j=g(j)⊕⌊g(j)/2⌋⊕⌊g(j)/4⌋⊕⋯=i=0⨁∞⌊2ig(j)⌋
В коде ещё проще
unsignedintto_gray_code(unsignedint n){return n ^(n >>1);}unsignedintfrom_gray_code(unsignedint gray){unsignedint n = gray;while(gray >>=1){
n ^= gray;}return n;}
На основе кодов Грея можно сделать генератор всех битовых строк фиксированной длины,
при этом каждую итерацию изменяться будет только один бит в выдаваемой строке.
Нужно только понять, какой бит менять на k-й итерации.
ρ(k) — позиция младшего единичного бита в k
generatorbinary_tuples(int n)->tuple[bool]:array[bool] bits =[0,0,...,0]yieldtuple(bits)forint i =1; i <2** n; i++:
change_bit =(i ^(i >>1))^((i-1)^((i-1)>>1))
pos = change_bit.bit_length()-1
bits[pos]^=1yieldtuple(bits)
Функции Уолша и Радемахера
Здесь безумно часто будет необходимо получить конкретный бит числа. Поэтому я сразу введу обозначения
g(k)=(⋯g3g2g1g0)2иx=(⋯x3x2x1x0)2
Здесь xj означает j-й бит числа x.
Функции Уолша — функции, введенные Джозефом Леонардом Уолшем для упрощения работы
с комбинаторными объектами и их генерации.
w0(x)=1иwk(x)=(−1)xmod2⋅w⌊k/2⌋(⌊x/2⌋)дляk>0
Функции Уолша классически определяют как раз через код Грея и бинарную свёртку.
wk(x)=(−1)g(k)⊛x
Здесь g(k)⊛x — бинарная свёртка чисел g(k) и x:
g(x)⊛x=j=0∑∞gj⋅xj
Функции Радемахера — функции, предложенные Гансом Радемахером
rk(x)=(−1)xk
Связь с кодами Грея
Из этих свойств можно получить важное соотношение между функциями Уолша и функциями Радемахера
wk(x)=j=0∏∞rj(x)gj
Отсюда заключаем, что кратность rj(x) в wk(x) представляет собой j-й бит числа g(k).
Тогда
wk(x)=rρ(k)+1(x)⋅wk−1(x)дляk>0
Ортогональность
И функции Уолша, и функции Радемахера ортогональны
Из мультипликативности функций Радемахера следует групповой закон для функций Уолша
wa(x)⋅wb(x)=wa⊕b(x)
Вообще получается, что функции Уолша образуют абелеву группу относительно поточечного умножения.
Из этого факта следует более слабое, но часто встречающееся свойство wn(x)=w2k(x)⋅wr(x), где n=2k+r и 0⩽r<2k.
А ещё, группа функций Уошла изоморфна натуральным числам с операцией побитового исключающего или
({wk:k∈N0},⋅)≅(N0,⊕)≅Z2∞
Циклы Грея
Код Грея g(⋅) является только одним из множества способов обхода всех бинарных строк
с изменением только в одном бите за шаг.
Цикл Грея — последовательность бинарных строк v0,v1,v2,…,v2n−1 такая,
что vk отличается от v(k+1)mod2n только в одном бите.
Получается, что цикл Грея является гамильтоновым циклом в бинарном n-мерном кубе.
Поскольку цикл Грея является гамильтоновым циклом, можно выбрать индексы так, чтобы
v0=000⋯00
Будем рассматривать битовые строки v0,v1,…,v2n−1 как числа,
записанные в двоичной системе счисления.
Дельта-последовательность — последовательность чисел δk, для которых
v(k+1)mod2n=vk⊕2δk
Дельта-последовательность показывает, какие биты надо менять на каком шаге.
Например, для обычного кода Грея g(⋅) дельта-последовательность
определяется как δk=ρ(k+1),
но последнее значение δ2n−1=n−1, а не n.
Расширение цикла Грея
Пусть α1j1α2j2⋯αljl —
дельта-последовательность для n-битного цикла Грея, причем
каждое jk является одной координатой,
каждое αk является последовательностью координат, возможно пустой,
и l нечётное. Тогда