Що стосується того, щоб rand() % n
бути менш ідеальним
Дія rand() % n
має нерівномірний розподіл. Ви отримаєте непропорційну кількість певних значень, оскільки кількість значень не кратна 20
Далі, rand()
це типовий генератор лінійного конгруенції (є багато інших , саме це, швидше за все, реалізований - і з меншими за ідеальні параметри (існує безліч способів вибору параметрів)). Найбільша проблема з цим полягає в тому, що часто низькі біти в ньому (ті, які ви отримуєте з % 20
виразом типу), не такі випадкові. Я згадую один rand()
з років тому , коли молодший біт чергувалися з 1
до 0
кожним викликом rand()
- це було не дуже випадково.
На сторінці чоловіка rand (3):
Версії rand () та srand () у бібліотеці Linux C використовують одне і те ж
генератор випадкових чисел як випадкових (), так і 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 (). Через це RNG у всій грі працює погано. Це особливо помітно перед тим, як дії користувача вводять подальшу випадковість, тобто у генеруванні персонажів та створенні першого рівня.
Хоча вищезгадане було з 2003 року, все ж слід пам’ятати, оскільки це може бути не так, що всі системи, в яких запущена ваша гра, будуть оновленою системою Linux з хорошою функцією rand ().
Якщо ви це робите для себе, ви можете перевірити, наскільки хороший ваш генератор випадкових чисел, написавши якийсь код і протестувавши вихід з ent .
Про властивості випадкових чисел
Є й інші трактування «випадкових», які не зовсім випадкові. У випадковому потоці даних цілком можливо отримати одне і те ж число двічі. Якщо перевернути монету (випадково), цілком можливо отримати дві голови підряд. Або киньте кістки двічі і отримайте однакове число двічі поспіль. Або крутиться колесо рулетки і отримуєш ту саму кількість двічі.
Розподіл чисел
Під час відтворення списку пісень люди очікують, що "випадковий" означає, що та сама пісня чи виконавець не будуть відтворюватися другий раз поспіль. Проведення відтворення у "Бітлз" двох разів поспіль вважається "не випадковим" (хоча воно є випадковим). Сприйняття, що за список відтворень із чотирьох пісень виконували загалом вісім разів:
1 3 2 4 1 2 4 3
є більш "випадковим", ніж:
1 3 3 2 1 4 4 2
Детальніше про це для "перетасування" пісень: Як перетасувати пісні?
На повторних значеннях
Якщо ви не хочете повторювати значення, слід розглянути інший підхід. Створіть усі можливі значення та перемістіть їх.
Якщо ви телефонуєте rand()
(або будь-який інший генератор випадкових чисел), ви викликаєте його із заміною. Ви завжди можете отримати одне і те ж число двічі. Одним із варіантів є викидання значень знову і знову, поки ви не вибрали той, який відповідає вашим вимогам. Я зазначу, що це має недетермінований час виконання, і цілком можливо, що ви могли б опинитися в ситуації, коли існує нескінченний цикл, якщо ви не почнете робити більш складний пошук назад.
Список та вибір
Інший варіант - сформувати список усіх можливих дійсних станів, а потім вибрати випадковий елемент із цього списку. Знайдіть у кімнаті всі порожні плями (які відповідають деяким правилам), а потім виберіть випадковий із цього списку. А потім робіть це знову і знову, поки не закінчите.
Перемішати
Інший підхід - перетасувати, ніби це колоди карт. Почніть з усіх порожніх плям у кімнаті, а потім почніть призначати їх, роздаючи порожні плями, по одному, кожному правилу / процесу, запитуючи про порожнє місце. Ви закінчили, коли у вас закінчилися картки або речі перестали їх просити.