Це далі з раніше опублікованого питання:
Як генерувати випадкове число в С?
Я хочу мати можливість генерувати випадкове число з певного діапазону, наприклад, від 1 до 6, щоб імітувати сторони плашки.
Як би я пішов робити це?
Це далі з раніше опублікованого питання:
Як генерувати випадкове число в С?
Я хочу мати можливість генерувати випадкове число з певного діапазону, наприклад, від 1 до 6, щоб імітувати сторони плашки.
Як би я пішов робити це?
Відповіді:
Усі відповіді поки що математично неправильні. Повернення rand() % Nне рівномірно дає число в діапазоні, [0, N)якщо не Nділить довжину інтервалу, на який rand()повертається (тобто є потужністю 2). Крім того, не має уявлення про те, чи є модулі rand()незалежними: можливо, вони йдуть 0, 1, 2, ..., що є рівномірним, але не дуже випадковим. Єдине припущення, яке здається розумним зробити, полягає в тому, що він rand()видає розподіл Пуассона: будь-які два неперекриваються підінтервали однакового розміру є однаково вірогідними та незалежними. Для кінцевого набору значень це означає рівномірний розподіл, а також забезпечує rand()гарне розсіяння значень .
Це означає, що єдиний правильний спосіб зміни асортименту rand()- розділити його на коробки; наприклад, якщо RAND_MAX == 11і ви хочете діапазон 1..6, вам слід призначити {0,1}1, {2,3}2 і так далі. Це непересічні інтервали, однакові за розміром, і таким чином розподіляються рівномірно та незалежно.
Пропозиція використовувати поділ з плаваючою комою є математично правдоподібною, але в принципі страждає від питань округлення. Можливо, doubleдостатньо висока точність, щоб змусити його працювати; можливо, ні. Я не знаю, і мені не хочеться цього розгадувати; у будь-якому випадку відповідь залежить від системи.
Правильний спосіб - використовувати цілу арифметику. Тобто ви хочете щось подібне:
#include <stdlib.h> // For random(), RAND_MAX
// Assumes 0 <= max <= RAND_MAX
// Returns in the closed interval [0, max]
long random_at_most(long max) {
unsigned long
// max <= RAND_MAX < ULONG_MAX, so this is okay.
num_bins = (unsigned long) max + 1,
num_rand = (unsigned long) RAND_MAX + 1,
bin_size = num_rand / num_bins,
defect = num_rand % num_bins;
long x;
do {
x = random();
}
// This is carefully written not to overflow
while (num_rand - defect <= (unsigned long)x);
// Truncated division is intentional
return x/bin_size;
}
Петля необхідна для отримання ідеально рівномірного розподілу. Наприклад, якщо вам задаються випадкові числа від 0 до 2, і ви хочете лише одиниці від 0 до 1, ви просто продовжуєте тягнути, поки не отримаєте 2; не важко перевірити, що це дає 0 або 1 з однаковою ймовірністю. Цей метод також описаний у посиланні, яке нос дав у своїй відповіді, хоча кодується інакше. Я використовую, random()а не rand()тому, що він має кращий розподіл (як це відмічено на сторінці man rand()).
Якщо ви хочете отримати випадкові значення за межами діапазону за замовчуванням [0, RAND_MAX], то вам доведеться зробити щось складне. Мабуть, найбільш доцільним є визначення функції, random_extended()яка витягує nбіти (використовуючи random_at_most()) та повертається [0, 2**n), а потім застосувати random_at_most()разом random_extended()із random()(і 2**n - 1замість RAND_MAX), щоб витягнути випадкове значення менше 2**n, якщо припустити, що у вас є числовий тип, який може містити таку значення. Нарешті, звичайно, ви можете отримати значення при [min, max]використанні min + random_at_most(max - min), включаючи негативні значення.
max - min > RAND_MAX, що є більш серйозним, ніж проблема, про яку я говорив вище (наприклад, VC ++ має RAND_MAXлише 32767).
do {} while().
Виходячи з відповіді @Ryan Reich, я подумав, що запропоную свою очищену версію. Перевірка першого межі не потрібна, враховуючи перевірку другої межі, і я зробив її більш ітеративною, а не рекурсивною. Він повертає значення в діапазоні [хв, макс], де max >= minі 1+max-min < RAND_MAX.
unsigned int rand_interval(unsigned int min, unsigned int max)
{
int r;
const unsigned int range = 1 + max - min;
const unsigned int buckets = RAND_MAX / range;
const unsigned int limit = buckets * range;
/* Create equal size buckets all in a row, then fire randomly towards
* the buckets until you land in one of them. All buckets are equally
* likely. If you land off the end of the line of buckets, try again. */
do
{
r = rand();
} while (r >= limit);
return min + (r / buckets);
}
limitвведення int (і, можливо, bucketтеж), оскільки RAND_MAX / range< INT_MAXі buckets * range<= RAND_MAX. EDIT: Я надсилаю та редагую пропозицію.
Ось формула, якщо ви знаєте максимальні та мінімальні значення діапазону, і ви хочете генерувати числа включно між діапазоном:
r = (rand() % (max + 1 - min)) + min
intперелив max+1-min.
unsigned int
randr(unsigned int min, unsigned int max)
{
double scaled = (double)rand()/RAND_MAX;
return (max - min +1)*scaled + min;
}
Дивіться тут інші варіанти.
(((max-min+1)*rand())/RAND_MAX)+minі отримати, мабуть, такий самий розподіл (якщо припустити, що RAND_MAX є досить малим відносно int, щоб не переповнювати).
max + 1, якщо будь-яка rand() == RAND_MAX, або rand()дуже близька до RAND_MAXпомилок з плаваючою комою, підштовхує остаточний результат минулого max + 1. Щоб бути безпечним, слід перевірити, чи є результат у межах, перш ніж повернути його.
RAND_MAX + 1.0. Я все ще не впевнений, що це досить добре, щоб запобігти max + 1поверненню, зокрема: зокрема, + minкінець включає в себе раунд, який може в кінцевому підсумку max + 1отримати великі значення rand (). Безпечніше взагалі відмовитися від цього підходу та використовувати цілу арифметику.
RAND_MAXзамінюється RAND_MAX+1.0як припускає Christoph, то я вважаю , що це безпечно при умови , що + minце робиться з використанням цілочисельний арифметики: return (unsigned int)((max - min + 1) * scaled) + min. Причина (не очевидна) полягає в тому, що припускаючи арифметику IEEE 754 і рівну половину до парного, (а також max - min + 1це точно представлено як подвійне, але це буде правдою на типовій машині), це завжди правда, що x * scaled < xдля будь-який позитивний подвійний xі будь-який подвійний scaledзадовольняючий 0.0 <= scaled && scaled < 1.0.
randr(0, UINT_MAX): завжди породжує 0.
Чи не просто ви зробите:
srand(time(NULL));
int r = ( rand() % 6 ) + 1;
%є оператором модуля. По суті, він просто розділиться на 6, а решту поверне ... від 0 до 5
rand()включаються біти низького порядку стану генератора (якщо він використовує LCG). Я ще не бачив цього - усі вони (так, включаючи MSVC з RAND_MAX лише 32767) видаляють біти низького порядку. Використання модуля не рекомендується з інших причин, а саме, щоб воно перекривало розподіл на користь менших чисел.
Для тих, хто розуміє проблему зміщення, але не витримує непередбачуваний час виконання методів, що базуються на відхиленні, ця серія створює прогресивно менш упереджене випадкове ціле число в [0, n-1]інтервалі:
r = n / 2;
r = (rand() * n + r) / (RAND_MAX + 1);
r = (rand() * n + r) / (RAND_MAX + 1);
r = (rand() * n + r) / (RAND_MAX + 1);
...
Це робиться шляхом синтезу високоточної випадкової кількості i * log_2(RAND_MAX + 1)бітів з фіксованою точкою (де iкількість ітерацій) та проведення довгого множення на n.
Коли кількість бітів досить велика порівняно з n, зміщення стає незмірно малим.
Не має значення, якщо RAND_MAX + 1вона менша n(як у цьому питанні ), чи вона не є потужністю двох, але слід бути обережним, щоб уникнути переливу цілих чисел, якщо RAND_MAX * nвін великий.
RAND_MAXчасто INT_MAX, так RAND_MAX + 1-> UB (як INT_MIN)
RAND_MAX * nвеликий". Вам потрібно домовитись про використання відповідних типів для ваших вимог.
RAND_MAXчасто INT_MAX" Так, але лише в 16-бітових системах! Будь-яка досить сучасна архітектура поставить INT_MAXу 2 ^ 32/2 та RAND_MAXв 2 ^ 16 / 2. Це неправильне припущення?
intкомпілятори, я знайшов RAND_MAX == 32767на одному і RAND_MAX == 2147483647на іншому. Мій загальний досвід (десятиліття) - це RAND_MAX == INT_MAXчастіше. Так НЕ згоден , що досить сучасний 32-бітна архітектура, безумовно , є RAND_MAXв 2^16 / 2. Оскільки специфікація C дозволяє 32767 <= RAND_MAX <= INT_MAX, я кодую це все одно, а не тенденцію.
Щоб уникнути зміщення модуля (запропонованого в інших відповідях), ви завжди можете використовувати:
arc4random_uniform(MAX-MIN)+MIN
Де "MAX" - це верхня межа, а "MIN" - нижня межа. Наприклад, для чисел від 10 до 20:
arc4random_uniform(20-10)+10
arc4random_uniform(10)+10
Просте рішення та краще, ніж використання "rand ()% N".
#include <bsd/stdlib.h>спочатку потрібно . Крім того, будь-яка ідея, як отримати це в Windows без MinGW або CygWin?
Ось трохи простіший алгоритм, ніж рішення Райана Рейха:
/// Begin and end are *inclusive*; => [begin, end]
uint32_t getRandInterval(uint32_t begin, uint32_t end) {
uint32_t range = (end - begin) + 1;
uint32_t limit = ((uint64_t)RAND_MAX + 1) - (((uint64_t)RAND_MAX + 1) % range);
/* Imagine range-sized buckets all in a row, then fire randomly towards
* the buckets until you land in one of them. All buckets are equally
* likely. If you land off the end of the line of buckets, try again. */
uint32_t randVal = rand();
while (randVal >= limit) randVal = rand();
/// Return the position you hit in the bucket + begin as random number
return (randVal % range) + begin;
}
Example (RAND_MAX := 16, begin := 2, end := 7)
=> range := 6 (1 + end - begin)
=> limit := 12 (RAND_MAX + 1) - ((RAND_MAX + 1) % range)
The limit is always a multiple of the range,
so we can split it into range-sized buckets:
Possible-rand-output: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
Buckets: [0, 1, 2, 3, 4, 5][0, 1, 2, 3, 4, 5][X, X, X, X, X]
Buckets + begin: [2, 3, 4, 5, 6, 7][2, 3, 4, 5, 6, 7][X, X, X, X, X]
1st call to rand() => 13
→ 13 is not in the bucket-range anymore (>= limit), while-condition is true
→ retry...
2nd call to rand() => 7
→ 7 is in the bucket-range (< limit), while-condition is false
→ Get the corresponding bucket-value 1 (randVal % range) and add begin
=> 3
RAND_MAX + 1може легко переповнювати intдодавання. У цьому випадку (RAND_MAX + 1) % rangeдасть сумнівні результати. Поміркуйте(RAND_MAX + (uint32_t)1)
Хоча Райан правильний, рішення може бути набагато простішим на основі того, що відомо про джерело випадковості. Щоб повторно заявити про проблему:
[0, MAX)з рівномірним розподілом.[rmin, rmax]де 0 <= rmin < rmax < MAX.На мій досвід, якщо кількість бункерів (або "коробок") значно менша, ніж діапазон вихідних номерів, а оригінальне джерело криптографічно сильне - не потрібно проходити всю цю ригамаролу, і простий поділ за модулем би достатньо (як output = rnd.next() % (rmax+1), якщо rmin == 0) і виробляти випадкові числа, які розподіляються рівномірно "достатньо" і без втрати швидкості. Ключовим фактором є джерело випадковості (тобто діти, не намагайтеся цього робити вдома rand()).
Ось приклад / доказ того, як це працює на практиці. Я хотів генерувати випадкові числа від 1 до 22, маючи криптографічно сильне джерело, яке виробляло випадкові байти (на основі Intel RDRAND). Результати:
Rnd distribution test (22 boxes, numbers of entries in each box): 1: 409443 4.55% 2: 408736 4.54% 3: 408557 4.54% 4: 409125 4.55% 5: 408812 4.54% 6: 409418 4.55% 7: 408365 4.54% 8: 407992 4.53% 9: 409262 4.55% 10: 408112 4.53% 11: 409995 4.56% 12: 409810 4.55% 13: 409638 4.55% 14: 408905 4.54% 15: 408484 4.54% 16: 408211 4.54% 17: 409773 4.55% 18: 409597 4.55% 19: 409727 4.55% 20: 409062 4.55% 21: 409634 4.55% 22: 409342 4.55% total: 100.00%
Це настільки близько до уніформи, як мені потрібно для моєї мети (справедливе кидання кісток, генерування криптографічно сильних кодових книг для шифрових машин Другої світової війни, таких як http://users.telenet.be/d.rijmenants/en/kl-7sim.htm тощо) ). Результат не демонструє помітного зміщення.
Ось джерело криптографічно сильного (справжнього) генератора випадкових чисел: генератор цифрових випадкових чисел Intel та зразок коду, який виробляє 64-бітні (непідписані) випадкові числа.
int rdrand64_step(unsigned long long int *therand)
{
unsigned long long int foo;
int cf_error_status;
asm("rdrand %%rax; \
mov $1,%%edx; \
cmovae %%rax,%%rdx; \
mov %%edx,%1; \
mov %%rax, %0;":"=r"(foo),"=r"(cf_error_status)::"%rax","%rdx");
*therand = foo;
return cf_error_status;
}
Я компілював його на Mac OS X з clang-6.0.1 (прямо) та з gcc-4.8.3, використовуючи прапор "-Wa, q" (оскільки GAS не підтримує ці нові інструкції).
gcc randu.c -o randu -Wa,q(GCC 5.3.1 на Ubuntu 16) або clang randu.c -o randu(Clang 3.8.0) працює, але скидає ядро під час виконання Illegal instruction (core dumped). Якісь ідеї?
rand(). Я спробував кілька тестів і розмістив це запитання, але остаточної відповіді не можу знайти.
Як було сказано раніше, модуль недостатній, оскільки він перекриває розподіл. Ось мій код, який маскує біти і використовує їх, щоб гарантувати, що розподіл не перекручений.
static uint32_t randomInRange(uint32_t a,uint32_t b) {
uint32_t v;
uint32_t range;
uint32_t upper;
uint32_t lower;
uint32_t mask;
if(a == b) {
return a;
}
if(a > b) {
upper = a;
lower = b;
} else {
upper = b;
lower = a;
}
range = upper - lower;
mask = 0;
//XXX calculate range with log and mask? nah, too lazy :).
while(1) {
if(mask >= range) {
break;
}
mask = (mask << 1) | 1;
}
while(1) {
v = rand() & mask;
if(v <= range) {
return lower + v;
}
}
}
Наступний простий код дозволяє переглянути дистрибутив:
int main() {
unsigned long long int i;
unsigned int n = 10;
unsigned int numbers[n];
for (i = 0; i < n; i++) {
numbers[i] = 0;
}
for (i = 0 ; i < 10000000 ; i++){
uint32_t rand = random_in_range(0,n - 1);
if(rand >= n){
printf("bug: rand out of range %u\n",(unsigned int)rand);
return 1;
}
numbers[rand] += 1;
}
for(i = 0; i < n; i++) {
printf("%u: %u\n",i,numbers[i]);
}
}
v = rand(); if (v > RAND_MAX - (RAND_MAX % range) -> reject and try again; else return v % range;Я розумію, що модуль - це набагато повільніше, ніж маскування, але я все одно думаю ..... це слід перевірити.
rand()повертає intв діапазоні [0..RAND_MAX]. Цей діапазон легко може бути піддіапазоном, uint32_tа потім randomInRange(0, ,b)ніколи не генерує значення в діапазоні (INT_MAX...b].