| Быстрые генераторы unsigned int (05.11.2026). |
|
| 2026 - Ноябрь | |||
| 05.11.2026 00:00 | |||
Самый идеальный: 1.08ГБ/с при частоте потока 1.25ГГц, в 18.4 раза быстрее штатного rand() Borland C++ Builder v.6.0, unsigned int, выход 0...4294967295, без входных параметров, нормальное распределение чисел, коэффициенты энтропии Шеннона на блоки 200МБ 3.3 и 512Б в его составе средний 3.3 (естественно: ни о каком шифровании речи не идёт - только генерация, все ресурсы - на неё). Распределение оценивалось, вызывая генератор 4294967296 раз (не попадитесь в ловушку последнего значения: только __int64 в цикле) и группируя вызовы по сотням чисел (не хватает RAM - массив результатов размерностью 429496723). Все числа в массиве, сохранённом в файл (строка - получаемое число минус 1, число - сколько раз сгенерировано), были равны 100 (только в первой сотне 50). Куда делось ещё 46 измерений первой сотни - непонятно, но результат распределения - практически идеальный, никакого графика не нужно: исходным кодом всё проверил. Серьёзный минус: псевдохаос вместо хаоса - всегда выдаёт определённую последовательность чисел при старте программы. Решается просто: при старте ПО - прокрутить генератор произвольное количество раз, используя медленный random(255) как входной параметр - и прибавляя его потом ко всем числам генератора. unsigned int uiFast_Random() { static unsigned int uiResult = 123456789U; uiResult = uiResult * 1664525U + 1013904223U; //Пусть переполняется: проблемы нет. return uiResult; } На основе этого генератора, рождается генератор unsigned int с входным параметром максимума. ИИ очень сильно поднасрал, предложив для результата предыдущего генератора использовать побитовое И вместо %. Выигрыш в скорости вышел значительным, но при анализе входного параметра миллион и вызове (4294967296? Забыл) раз - получилось просто безумное распределение чисел: он пытался равномерно распределиться в диапазоне - но порождал только 128 чисел кучками и создавал их десятками миллионов раз. Рассвирепел (правда, в текущем состоянии это было похоже на писк мышонка в мышеловке). Отключил мозги и написал это. unsigned int uiFast_Random_Again(const unsigned int uiMax) { static unsigned int uiResult_Again = 123456789U; again: uiResult_Again = uiResult_Again * 1664525U + 1013904223U; //Пусть переполняется: проблемы нет. if (uiResult_Again < uiMax) return uiResult_Again; else goto again; //Не включая сам предел. } Работает - безумно медленно; но у него обнаружился такой бонус, который перекрыл огромное время и сделал этот генератор жизнеспособным. Запустив его 14млн раз при входном числе 1млн - распределение чисел вышло нормальным: ровно по 14 раз каждое число вызвалось (до этого - ещё на 10млн проверил). Здесь - уже возникает сомнение в работе самописного алгоритма оценки распределения. Но изучение штатного random(1000000) ставит всё на свои места: у него не нормальное, а стандартное распределение (тут уже справа само число подписано, рядом с количеством его получения). Это доказывает работоспособность и алгоритма проверки, и теоретическую нормальность именно почисленного распределения чисел изначального генератора без параметров (после успешного теста порциями по 100шт). Теперь - этот генератор с входным параметром надо ускорить. Сразу ясно несколько вещей: после этого он не будет с нормальным распределением (это очень сложная в поддержке величина), коэффициенты Шеннона не имеют смысла (зависят напрямую от входного параметра). Гугл предложил использовать % uiMax для uiResult - был послан: это уже делал, скорость падает раза в 3. Поэтому - опять окунание в говно под названием ассемблер, в попытке сделать ассемблерную вставку. Она - получилась: такое же стандартное распределение, как у %, - но скорость работы упала всего в 1.44 раза относительно генератора без параметра. Далее обнаружился прикол: генератор стал с другого числа старт давать: с % было 370032, а со вставкой - 214290. Ранее для попадания во все числа при входном параметре миллион было 13.1млн вызовов - стало 14.1млн. Define - быстрее ассемблерной вставки. Удалось их скомбинировать - и алгоритм стал медленнее изначального в 1.34 раза. Но он перестал генерировать разные числа - и генерировал только одно... Поэтому публикуется прошлый вариант. unsigned int uiFast_Random_ASM(const unsigned int uiMax) { static unsigned int uiResult_ASM = 123456789U; uiResult_ASM = uiResult_ASM * 1664525U + 1013904223U; register unsigned int uiResult; asm { mov eax, uiResult_ASM //Умножение. eax - младшие биты, edx - старшие. mul uiMax mov uiResult, edx //Ну, старшие 32 бита и берём. } return uiResult; } График стандартного распределения третьего генератора (онлайн миллион точек не построить - только в Windows от 7 x64 и Office 2010 x64 - и всё виснет, и полный писец - еле скрин сделал после окончательного зависания). Какие-то числа редко выпадают, какие-то часто, - не нормальность - стандартность. И видно, что одно число - генератор-таки профукал. Ну, простим ему это. Теперь, на основании этих генераторов, - можно и заполнять массивы, и делать другие типы данных: каждый из них - годится для своих задач. Замедление работы генератора - теперь на совести разработчика, плюс удобство использования: свои циклы, свои массивы, свои части заполнения этих массивов. А то массив внутрь добавил и число его элементов - сразу в 3 раза скорость и потерял. Мои проекты по созданию генераторов изрядно замусорились. Нужно создать новый, объединить в нём всё лучшее, протестировать алгоритмы уже в чистовых условиях. Но вряд ли это изменит числа или строки, написанные здесь, - это будет генератор в генераторе с высоким коэффициентом Шеннона (о котором будет уже отдельный материал с бешеной скоростью >6.72ГБ/с: оптимизация и ускорение продолжаются - этот лимит уже пройден). Можно ещё добавить четвёртый генератор, с уточнённой скоростью: long double с большим константным хвостом. Именно такой мне нужен для создания генератора в генераторе. Но никто не мешает вам для увеличения хаоса: пожертвовать скоростью - и добавить к целому числу ровно такую же дробную часть, а не константу. А то и двойную дробную часть по длине. //0.89ГБ/с при частоте потока 1.25ГГц. long double ldFast_Random() { static unsigned int uiResult = 123456789U; uiResult = uiResult * 1664525U + 1013904223U; //Пусть переполняется: проблемы нет. return uiResult + 0.3696425965745; //Порождение дробной части сложением - быстрее деления. } Завтра - будут уточнения по материалу. Сырой он вышел - нужно дополнить и поправить изначальный текст незначительно. |
|||
| Обновлено ( 05.11.2026 19:49 ) |