Относительно того, чтобы rand() % n
быть менее чем идеальным
Ведение rand() % n
имеет неравномерное распределение. Вы получите непропорциональное количество определенных значений, потому что число значений не кратно 20
Затем, rand()
как правило, это линейный конгруэнтный генератор (есть много других , только наиболее вероятно, что он реализован - и с параметрами, меньшими, чем идеальные (есть много способов выбора параметров)). Самая большая проблема в этом состоит в том, что часто младшие биты в нем (те, которые вы получаете с % 20
выражением типа) не настолько случайны. Я вспоминаю один rand()
из прошлых лет, когда младший бит чередовался с 1
на 0
каждый вызов rand()
- это было не очень случайно.
Из справочной страницы rand (3):
Версии rand () и srand () в библиотеке Linux C используют одинаково
генератор случайных чисел, как random () и srandom (), поэтому нижний порядок
биты должны быть такими же случайными, как биты высшего порядка. Однако на старших
реализации rand () и текущих реализаций на разных
В системах младшего разряда биты гораздо менее случайны, чем старшие
биты заказа. Не используйте эту функцию в приложениях, предназначенных для
портативный, когда нужна хорошая случайность.
Теперь это можно отнести к истории, но вполне возможно, что у вас все еще есть плохая реализация rand (), скрывающаяся где-то в стеке. В этом случае его все еще вполне применимо.
Нужно просто использовать хорошую библиотеку случайных чисел (которая дает хорошие случайные числа), а затем запрашивать случайные числа в нужном диапазоне.
Пример хорошего кода случайного числа (с 13:00 в связанном видео)
#include <iostream>
#include <random>
int main() {
std::mt19937 mt(1729); // yes, this is a fixed seed
std::uniform_int_distribution<int> dist(0, 99);
for (int i = 0; i < 10000; i++) {
std::cout << dist(mt) << " ";
}
std::cout << std::endl;
}
Сравните это с:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main() {
srand(time(NULL));
for (int i = 0; i < 10000; i++) {
printf("%d ", rand() % 100);
}
printf("\n");
}
Запустите обе эти программы и сравните, как часто определенные числа появляются (или не появляются) в этом выводе.
Видео по теме: rand () считается вредным
Некоторые исторические аспекты rand (), вызывающие ошибки в Nethack, которые следует наблюдать и учитывать в собственных реализациях:
Nethack RNG Проблема
Rand () - очень фундаментальная функция для генерации случайных чисел в Nethack. То, как Nethack использует его, ошибочно, или можно утверждать, что lrand48 () генерирует дрянные псевдослучайные числа. (Однако lrand48 () - это библиотечная функция, использующая определенный метод PRNG, и любая программа, которая ее использует, должна учитывать недостатки этого метода.)
Ошибка в том, что Nethack полагается (иногда исключительно, как в случае с rn (2)) на младшие биты результатов lrand48 (). Из-за этого ГСЧ во всей игре работает плохо. Это особенно заметно прежде, чем действия пользователя привнесут дополнительную случайность, то есть в генерацию персонажа и создание первого уровня.
Хотя вышесказанное относится к 2003 году, об этом следует помнить, поскольку, возможно, дело не в том, что все системы, на которых установлена ваша игра, будут современной системой Linux с хорошей функцией rand ().
Если вы просто делаете это для себя, вы можете проверить, насколько хорош ваш генератор случайных чисел, написав некоторый код и протестировав вывод с помощью ent .
О свойствах случайных чисел
Существуют и другие интерпретации «случайного», которые не совсем случайны. В случайном потоке данных вполне возможно получить одно и то же число дважды. Если вы подбросите монету (случайно), вполне возможно получить две головы подряд. Или бросьте кубик дважды и получите одно и то же число дважды подряд. Или вращая колесо рулетки и получая одно и то же число дважды там.
Распределение чисел
При воспроизведении списка песен люди ожидают, что «случайный» означает, что одна и та же песня или исполнитель не будут воспроизводиться второй раз подряд. Имея список воспроизведения играть The Beatles дважды подряд считается «не случайным» (хотя это является случайным). Восприятие, что для списка воспроизведения из четырех песен играли в общей сложности восемь раз:
1 3 2 4 1 2 4 3
является более «случайным», чем:
1 3 3 2 1 4 4 2
Подробнее об этом для «перемешивания» песен: как перемешать песни?
На повторных значениях
Если вы не хотите повторять значения, следует рассмотреть другой подход. Сгенерируйте все возможные значения и перемешайте их.
Если вы звоните rand()
(или любой другой генератор случайных чисел), вы звоните с заменой. Вы всегда можете получить один и тот же номер дважды. Один из вариантов - отбрасывать значения снова и снова, пока вы не выберете то, что соответствует вашим требованиям. Я укажу, что это имеет недетерминированное время выполнения, и возможно, что вы окажетесь в ситуации, когда есть бесконечный цикл, если вы не начнете выполнять более сложную обратную трассировку.
Список и выбор
Другой вариант - создать список всех возможных допустимых состояний и затем выбрать случайный элемент из этого списка. Найдите все пустые места (которые соответствуют некоторым правилам) в комнате, а затем выберите случайное место из этого списка. И затем делайте это снова и снова, пока не закончите.
шарканье
Другой подход - перетасовать, как если бы это была колода карт. Начните со всех пустых пятен в комнате, а затем начните назначать их, раздавая пустые пятна, по одному, каждому правилу / процессу, запрашивая пустое место. Вы закончили, когда у вас закончились карты или вещи перестали просить их.