Это сайт — моя персональная записная книжка. Интересна мне, по большей части, история, своя жизнь и немного программирование.

Нечётные числа

Что же я там считаю такое, что мне понадобились 128-битные числа? Сейчас расскажу.

Коллега в офисе, который когда-то работал математиком в местном университете, наткнулся в тот свой период на интересную задачку в каком-то в иностранном журнале.

Формулировка у неё была очень простая.

Дано множество X. Единица принадлежит X. Если 3×x принадлежит X, то и x принадлежит X. Если x принадлежит X, то и 2×x + 1 принадлежит X. При этом x — натуральное, разумеется.

Требуется доказать, что таким образом можно получить множество всех натуральных нечётных чисел. Авторы статьи, кстати, были убеждены, что найти доказательство у читателей не получится.

Упомянутый коллега у себя в университете при помощи студента, Фортрана и Пролога пытался проверить эту теорему по следующему алгоритму.

Берём число, например, «9». Оно могло быть получено, согласно перечисленным утверждениям, двумя путями: либо какое-то другое нечётное число умножили на два и прибавили один, либо умножили на три. Поэтому надо проверить по формулам откуда мы могли попасть в это число, отсекая циклы и останавливаясь, когда мы попадаем в чётные числа.

В этой задаче очень быстро растёт разрядность, поэтому в те времена рассчитать даже первую сотню чисел было достаточно проблематично.

В общем, я решил с этой задачкой побаловаться.

Для начала написал программу, которая вычисляет эти числа перебором. Начинаем, понятное дело, с единицы, следующее число получаем по формуле 2×x + 1, потом, пока число делится на три, делим и выводим эти числа, считая для каждого ещё и  2×x + 1 и так далее. Кроме того, пришлось ещё запоминать все найденные числа, чтобы избегать циклов.

Первую версию я написал с использованием рекурсии, но программа падала примерно через полторы минуты работы — стек просто кончался. Пришлось переписать. Новая версия работает уже несколько часов на моём ноутбуке и вычислила за это время уже почти 17 миллионов чисел. Оставлю её на ночь, интересно, закончит ли она к утру.

12 комментариев
Владимир Новицкий 1 мес

...получить множество всех натуральных нечётных чисел.
Новая версия работает уже несколько часов на моём ноутбуке и вычислила за это время уже почти 17 миллионов чисел.

Я не силен в математике, а сколько их должно быть?

Евгений Степанищев 1 мес

У самого типа счёт на ундециллионы, но сколько чисел оно нагенерит — непонятно.

Evgeny Sureev 1 мес

Ну так то всех нечетных чисел бесконечное количество.

Евгений Степанищев 1 мес

Так понятно, что надо искать аналитическое доказательство. Эта работа была, видимо, чтобы какие-то закономерности уловить, но это моё предположение.

Роман 1 мес

Давно я не брал в руки С, поправьте, если я не прав. Но меня смущают постоянный memmove(). Как бы оно половину производительности не съело.

Евгений Степанищев 1 мес

Это для вставки, чтобы потом делать бинарный поиск, а не линейный. Чтений ожидается больше вставок.

Роман 1 мес

Да, но если числа хранить сразу в виде дерева, то поиск останется O(logN), а вот вставка будет быстрее. Но это конечно сложнее реализовать, возможно данный эксперимент того не стоит

Евгений Степанищев 1 мес

Так дерево при вставке перебалансировать надо будет.

Мистер Икс 1 мес

Сдается мне, за ночь оно не посчитается. И за неделю, и за год.

Евгений Степанищев 1 мес

Откуда такое мнение?

Роман 1 мес

Все-таки вставка будет O(logN) https://en.wikipedia.org/wiki/AVL_tree
Но с точки зрения программирования это конечно головная боль

Евгений Степанищев 1 мес

Ну, там балансировка, тут — копирование элементов. Если я всё правильно понимаю, порядок операций тот же самый. Сколько элементов надо перебалансировать в случае дерева, столько надо скопировать и тут, разве нет?

Leonid 1 мес

Я всё таки не понимаю , что проверяет программа? Что в какой то момент будет найдено нечётное число которое нельзя получить этим алгоритмом?

Евгений Степанищев 1 мес

Программа проверяет до какого числа и за какое время можно дойти современным железом. По сути, больше ничего. Ну и с такими большими числами хотелось в Си поработать, как узнаешь их ограничения, если не попробуешь?

Роман 1 мес

Зачем бы тогда вообще придумывали деревья, если бы они не были быстрее? Бинарный поиск можно и в массиве делать. А вот сложность вставки в массив (в худшем случае) — это O(N). Это куда менее быстро, чем гарантированные O(logN) в AVL-дереве.

Евгений Степанищев 1 мес

Да, точно, был неправ )

Думаю, я всё-таки остановлю расчёт цифр, слишком уж долго. Не знаю где там конец, но меня печалит столько времени ноут на зарядке держать.

Можно, кстати, lock-free дерево сделать вообще и на потоки перейти. Где-то я видел библиотеку с готовой реализацией.

Роман 1 мес

Да, многопоточность тут может сильно помочь по идее. Ядер у нынешних процессоров много.

Евгений Степанищев 1 мес

У моего ноута — 14, но скорее всего он нагреется ещё больше и будет троттлить. У него уже сейчас температура 60°.

У нас на работе есть компьютер с водяным охлаждением. Можно бы его часов на 40 зарядить для начала — посмотреть будет ли быстрее на одном ядре. А потом, если будет, попробовать сделать всё в десять потоков.

Мистер Икс 1 мес

Откуда такое мнение?

Ну, там же наверняка число какое-нибудь уровня задачки про зерна и шахматы. Если это 128-битное число без знака, то оно может быть в диапазоне от 0 до 340 282 366 920 938 463 463 374 607 431 768 211 455. Очевидно, что перебор такого диапазона на ноутбуке, с учетом того, что каждый разряд умножает длительность в десять, может занимать непредсказуемое время — дни или недели как минимум.

Евгений Степанищев 1 мес

Я надеялся, что при восхождении (умножении) ветвление будет быстро упираться в потолок и обрубать эти ветки.

Sam Spiridonov 1 мес

Очень похоже на https://en.wikipedia.org/wiki/Collatz_conjecture но в попытках решить «в другую сторону»

Евгений Степанищев 1 мес

Да, я эту задачу тоже видел. Но за ссылку спасибо в любом случае!

Oleg Oriscthenko 27 дн

Дополнительные материалы:
https://habr.com/ru/articles/870220/
Гипотеза Коллатца как фейл мировой математики / Хабр

Евгений Степанищев 27 дн

Спасибо!

Я пока сделал паузу — у меня перебираются в один поток 64-битные числа, а многопоточную версию я пока не дописал. Закоммитил промежуточное состояние, но там есть ошибки.