И еще немного о двоичной системе

May 03, 2018 14:20

Немного с алиасингом поиграемся. Без предисловия. Под кат.




В предыдущей записи разобрались с делением. А как у нас обстоят дела с умножением?

Двоичные числа по порядку:
0001
0010
0011
0100
0101
0110
0111
1000
1001
1010
1011
1100
1101
1110
1111
и т.д.

Если их сложить вместе (добавляя нужное количество ведущих нулей, чтобы выровнять по правому краю (по младшему разряду)) и раскрасить единицы в белый цвет, а нули - в черный. Получим вот такой паттерн:



Пока ничего необычного. А что, если попробовать отобрать числа кратные некоторому Z. Для разных Z:



Тут хорошо видно, что для чисел кратных k*(2^n) получаем числа кратные k со сдвигом на n бит влево.

Для Z=(0, 416):



Попробовал уменьшить количество разрядов и увеличить количество чисел в каждом столбике:



Тут видно некий паттерн, который проходит через все столбики! Чтобы сделать этот паттерн более явным - запихнем наши числа в трехмерную матрицу, где Y - число кратное Z, а X - разряд в двоичной системе. Или, проще говоря, взяли эти столбики выше и сложили их один на другой. Теперь можно сделать срез по координате X.

Первые 8 разрядов (младший разряд будем считать нулевым):



Размер повторяющейся части паттерна равен 2^(n+1), где n-разряд. Картинка быстро выходит за область прорисовки, поэтому написал скрипт, позволяющий двигать паттерн.



Возьмем наш 7-й разряд и подвинем на 64 пикселя влево (z+64) и на 64 пикселя вверх (y+64):



8-й разряд (128+64 влево и вверх):



9-й разряд (256+128 влево и вверх):



11-й разряд, центр симметрии:



15-й разряд. Размер повторяющейся части = 65536. Центр симметрии:



Каждый паттерн в младшем разряде - уменьшенный паттерн (в два раза) из старшего разряда. Паттерн уменьшается и теряется детализация. Это очевидно. Если мы наш делитель Z умножим на два - получим сдвиг всей нашей матрицы по оси X. То есть, вся эта ахинея сдвинется на один разряд вверх. А что если мы наш делитель на 3 умножим? Поехали.

Z*3 (первые 8 разрядов):


Z*4 - матрица на 2 разряда сдвинется.

Z*5:


Z*6 - получим матрица умноженная на 3 со сдвигом на 1 разряд.

Z*7:


Z*8 - степень двойки. Сдвиг на три разряда.

Z*9:


Z*10 - Z*5 со сдвигом на 1 разряд.

Z*11:


Z*12 - сдвиг Z*3 на два разряда.

Z*13:


Z*15:


Вот тут, кстати, у нас в 7 разряде хорошо видно, что паттерны обладают самоподобием (привет, фракталы).

Z*17:


Z*19:


Z*21:


Первые 4 разряда далее можно не рассматривать - они повторяются.
Z*23 (4, 5, 6 и 7 разряды):


Далее от 25 до 55 все нечетные.
Для 4 разряда:


Для 5 разряда:


Для 6 разряда:


Для 7 разряда:


От 57 до 87, 7-й разряд:


Откуда берутся эти паттерны?

Вернемся к нашим столбикам.



Если не изменять Z, то есть оставить его постоянным (=1) для всей матрицы, эти столбики будут выглядеть вот так:



Дальше, если мы сделаем срез по X для матрицы, чтобы посмотреть, что находится в разных разрядах двоичного числа - увидим горизонтальные линии:



То есть, в нулевом разряде находится:



В первом разряде:



И т.д.

Теперь, если мы изменяем Z - мы берем в каждом столбике цифру, индекс которой кратен этому самому Z. В первом столбике - все цифры, во втором - 2, 4, 6. В третьем - 3, 6, 9. Вот так для нулевого разряда:


Остается выровнять по верхнему краю, убрав все не выбранные цифры. Получаем паттерны.

Проверяем.

Шаблон у нас одномерный - хватит одномерного массива.


Берем для каждого элемента массива остаток от деления индекса на 256. Если остаток меньше 128 - помещаем в массив 0. Если больше или равно - 1.

Проверяем шаблон:




Отлично. Применяем к шаблону наш алгоритм:



Паттерн совпал:



А вот тут самое интересно начинается. Наш начальный шаблон (массив) можно заполнить произвольной двоичной последовательность (повторяющейся)!

Не обязательно физически делать массив очень длинным и заполнять его повторяющейся последовательностью. Достаточно взять остаток от деления (x*y) на длину повторяющейся части и этот остаток использовать в качестве индекса (номера элемента в последовательности). Пример:

Есть у нас массив:
[0, 0, 0, 0, 1];
Длина массива = 5. Допустим, нам надо брать каждый 3-й элемент (нумерация элементов начинается с 0).

Берем
0*3 = 0. Остаток от деления 0 на 5 = 0.
[0, 0, 0, 0, 1];
1*3=3. Остаток от деления 3 на 5 = 3.
[0, 0, 0, 0, 1];
2*3 = 6. Остаток от деления 6 на 5 = 1.
[0, 0, 0, 0, 1];
3*3 = 9. Остаток от деления 9 на 5 = 4.
[0, 0, 0, 0, 1];
4*3 = 12. Остаток от деления 12 на 5 = 2.
[0, 0, 0, 0, 1];

Фактически:
[0, 0, 0, 0, 1]; - взяли первый элемент
[0, 0, 0, 0, 1]; - взяли элемент через два
[0, 0, 0, 0, 1]; - уперлись в конец последовательности, продолжили считать от начала
[0, 0, 0, 0, 1];
[0, 0, 0, 0, 1];

В качестве примера возьмем вот такой начальный шаблон (повторяющуюся часть последовательности):
[0, 1]; - для первой итерации
[0, 0, 1]; - для второй
[0, 0, 0, 1]; - третьей
[0, 0, 0, 0, 1]; - четвертой
и т.д.

Скрипт выглядит довольно просто:



Результат:



Шаблон [0, 1, 1]. На каждой следующей итерации добавляем к шаблону слева 0:



[0, 1, 1, 1]:



[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1]; - начальная последовательность. На каждой итерации добавляем слева 0. В качестве индекса берем: [(y*x*z)%l]



Двоичных последовательностей длиной n существует 2^n штук. Просмотреть их все невозможно. К примеру, если длина последовательности = 30, таких последовательностей существует больше миллиарда.
Думается мне, для перебора всех возможных последовательностей, сюда отлично впишется генетический алгоритм. Но это как-то в другой раз.

Previous post Next post
Up