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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Evgeny Sureev 11 мес

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

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

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

Роман 11 мес

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

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

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

Роман 11 мес

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

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

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

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

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

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

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

Роман 11 мес

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

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

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

Leonid 11 мес

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

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

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

Роман 11 мес

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

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

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

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

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

Роман 11 мес

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

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

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

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

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

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

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

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

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

Sam Spiridonov 11 мес

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

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

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

Oleg Oriscthenko 11 мес

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

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

Спасибо!

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