Нечётные числа
Что же я там считаю такое, что мне понадобились 128-битные числа? Сейчас расскажу.
Коллега в офисе, который когда-то работал математиком в местном университете, наткнулся в тот свой период на интересную задачку в каком-то в иностранном журнале.
Формулировка у неё была очень простая.
Дано множество X. Единица принадлежит X. Если 3×x принадлежит X, то и x принадлежит X. Если x принадлежит X, то и 2×x + 1 принадлежит X. При этом x — натуральное, разумеется.
Требуется доказать, что таким образом можно получить множество всех натуральных нечётных чисел. Авторы статьи, кстати, были убеждены, что найти доказательство у читателей не получится.
Упомянутый коллега у себя в университете при помощи студента, Фортрана и Пролога пытался проверить эту теорему по следующему алгоритму.
Берём число, например, «9». Оно могло быть получено, согласно перечисленным утверждениям, двумя путями: либо какое-то другое нечётное число умножили на два и прибавили один, либо умножили на три. Поэтому надо проверить по формулам откуда мы могли попасть в это число, отсекая циклы и останавливаясь, когда мы попадаем в чётные числа.
В этой задаче очень быстро растёт разрядность, поэтому в те времена рассчитать даже первую сотню чисел было достаточно проблематично.
В общем, я решил с этой задачкой побаловаться.
Для начала написал программу, которая вычисляет эти числа перебором. Начинаем, понятное дело, с единицы, следующее число получаем по формуле 2×x + 1, потом, пока число делится на три, делим и выводим эти числа, считая для каждого ещё и 2×x + 1 и так далее. Кроме того, пришлось ещё запоминать все найденные числа, чтобы избегать циклов.
Первую версию я написал с использованием рекурсии, но программа падала примерно через полторы минуты работы — стек просто кончался. Пришлось переписать. Новая версия работает уже несколько часов на моём ноутбуке и вычислила за это время уже почти 17 миллионов чисел. Оставлю её на ночь, интересно, закончит ли она к утру.
Я не силен в математике, а сколько их должно быть?
У самого типа счёт на ундециллионы, но сколько чисел оно нагенерит — непонятно.
Ну так то всех нечетных чисел бесконечное количество.
Так понятно, что надо искать аналитическое доказательство. Эта работа была, видимо, чтобы какие-то закономерности уловить, но это моё предположение.
Давно я не брал в руки С, поправьте, если я не прав. Но меня смущают постоянный memmove(). Как бы оно половину производительности не съело.
Это для вставки, чтобы потом делать бинарный поиск, а не линейный. Чтений ожидается больше вставок.
Да, но если числа хранить сразу в виде дерева, то поиск останется O(logN), а вот вставка будет быстрее. Но это конечно сложнее реализовать, возможно данный эксперимент того не стоит
Так дерево при вставке перебалансировать надо будет.
Сдается мне, за ночь оно не посчитается. И за неделю, и за год.
Откуда такое мнение?
Все-таки вставка будет O(logN) https://en.wikipedia.org/wiki/AVL_tree
Но с точки зрения программирования это конечно головная боль
Ну, там балансировка, тут — копирование элементов. Если я всё правильно понимаю, порядок операций тот же самый. Сколько элементов надо перебалансировать в случае дерева, столько надо скопировать и тут, разве нет?
Я всё таки не понимаю , что проверяет программа? Что в какой то момент будет найдено нечётное число которое нельзя получить этим алгоритмом?
Программа проверяет до какого числа и за какое время можно дойти современным железом. По сути, больше ничего. Ну и с такими большими числами хотелось в Си поработать, как узнаешь их ограничения, если не попробуешь?
Зачем бы тогда вообще придумывали деревья, если бы они не были быстрее? Бинарный поиск можно и в массиве делать. А вот сложность вставки в массив (в худшем случае) — это O(N). Это куда менее быстро, чем гарантированные O(logN) в AVL-дереве.
Да, точно, был неправ )
Думаю, я всё-таки остановлю расчёт цифр, слишком уж долго. Не знаю где там конец, но меня печалит столько времени ноут на зарядке держать.
Можно, кстати, lock-free дерево сделать вообще и на потоки перейти. Где-то я видел библиотеку с готовой реализацией.
Да, многопоточность тут может сильно помочь по идее. Ядер у нынешних процессоров много.
У моего ноута — 14, но скорее всего он нагреется ещё больше и будет троттлить. У него уже сейчас температура 60°.
У нас на работе есть компьютер с водяным охлаждением. Можно бы его часов на 40 зарядить для начала — посмотреть будет ли быстрее на одном ядре. А потом, если будет, попробовать сделать всё в десять потоков.
Ну, там же наверняка число какое-нибудь уровня задачки про зерна и шахматы. Если это 128-битное число без знака, то оно может быть в диапазоне от 0 до 340 282 366 920 938 463 463 374 607 431 768 211 455. Очевидно, что перебор такого диапазона на ноутбуке, с учетом того, что каждый разряд умножает длительность в десять, может занимать непредсказуемое время — дни или недели как минимум.
Я надеялся, что при восхождении (умножении) ветвление будет быстро упираться в потолок и обрубать эти ветки.
Очень похоже на https://en.wikipedia.org/wiki/Collatz_conjecture но в попытках решить «в другую сторону»
Да, я эту задачу тоже видел. Но за ссылку спасибо в любом случае!
Дополнительные материалы:
https://habr.com/ru/articles/870220/
Гипотеза Коллатца как фейл мировой математики / Хабр
Спасибо!
Я пока сделал паузу — у меня перебираются в один поток 64-битные числа, а многопоточную версию я пока не дописал. Закоммитил промежуточное состояние, но там есть ошибки.