Случайна C ++ функция

В разгара на създаването на STL и насилствената война на стандартния C ++ език, редица програмисти са разработили собствена кръстосана платформа библиотека от класове, които предоставят на разработчиците инструменти за решаване на ежедневни задачи като обработка на данни, алгоритми, манипулиране на файлове и т.н. , Проектът е толкова успешен, че възможностите на Boost са заети и се вписват в стандартния език, започвайки с C ++ 11. Едно такова приложение е подобрена работа със случайни числа.


Функциите rand () и srand () се отнасят до училищното ниво и са подходящи за писане на прости програми. Минусът на тези функции е генерирането на недостатъчно добра последователност от псевдослучайни числа (виж по-горе). Също така, характеристиките на простите функции не са достатъчни за разработване на сложни проекти. Създадени са генератори на случайни числа (наричани по-долу MHF) за решаване на възникващия проблем. С появата си работата по генериране на много видове данни, както псевдо-и истински случайни, значително се е подобрила. Пример за генериране на истински случайни числа е шумът в изображението по-долу.

Генератор на псевдослучайни числа

Традиционният алгоритъм за създаване на МФ комбинира алгоритъма за създаване на непредсказуеми битове и превръщането им в последователност от числа. В произволната C ++ библиотека, която е част от Boost, те разделят тези два механизма. Сега генерирането на случайни числа и създаването им на разпределение (последователност) се извършва поотделно. Използването на дистрибуцията е абсолютно логично. Защото случайно числобез конкретен контекст, няма смисъл и е трудно да се използва. Нека напишем проста функция, която хвърля костите:




# включи
int roll_a_dice () {
std :: default_random_engine e {}; //създаване на произволен генератор
std :: uniform_int_distribution d {1 6} //създаване на разпределение на мини и максимални стойности
връщане d (e);
}

Типична грешка на тези, които изучават случайно, е да игнорират създаването на разпределение и прехода незабавно към създаването на случайни числа по начина, по който са свикнали. Например, нека разгледаме тази функция.

връщане 1 + е ()% 6;

Някои смятат, че използването му е допустимо. Защото C ++ ви позволява да направите това. Въпреки това, на създателите на библиотеката Boost и C ++ 11 се препоръчва да не го правят. В най-добрия случай това просто ще бъде лош код, а в най-лошия случай той ще бъде работещ код, който прави грешки, които са много трудни за улавяне. Използването на дистрибуции гарантира, че програмистът получава това, което се очаква.

Инициализация на генератора и семената

Етапът на обявяване, дефиниране и създаване на обекти често се разглежда като нещо, което не заслужава специално внимание. Но недостатъчно обмислената инициализация на генератора на случайни числа може да повлияе на правилната му работа.

std :: default_random_engine e1; //скрита инициализация към стойността по подразбиране
std :: default_random_engine e2 {}; //изрична инициализация по подразбиране

Първите 2 инициализации са еквивалентни. И в по-голямата част, те се отнасят до вкуса или стандартите за писане на добър код. Но следващата инициализация е коренно различна.


& lt; script type = "text /javascript" & gt;
може да blockSettings2 = {blockId: "R-A-70350-2", renderTo: "yandex_rtb_R-A-70350-2", async:! 0};

if (document.cookie.indexOf ("abmatch =") & gt; = 0) {
blockSettings2 = {blockId: "RA-70350-2", renderTo: "yandex_rtb_R-A-70350- 2 ", statId: 70350async:! 0};
}

Функция (a, b, c, d, e) {a [c] = a [c] || [], a [c] .push (функция () {Ya .Context.AdvManager.render (blockSettings2)}), e = b.getElementsByTagName ("скрипт") , d = b.createElement ("script"), d.type = "text /javascript", d.src = "//an.yandex.ru/system/context.js", d.async =! 0e.parentNode.insertBefore (d, e)} (това, този.документ, "yandexContextAsyncCallbacks");
std :: default_random_engine e3 {31255}; //инициализира се до 31255

"31255" - това се нарича семе (семе, източник) - числото, въз основа на което генераторът създава случайни числа. Ключът тук е, че с такава инициализация, типът семена трябва да бъде същият или да се отнася до вида, с който работи генераторът. Този тип е достъпен чрез decltype (e ()), или result_of, или typename конструкция.

Защо генераторът създава идентични последователности?

Когато дадена програма стартира няколко пъти, генераторът винаги създава една и съща последователност от числа, ако инициализацията му не се променя, т.е. дефиницията на генератора е по същия начин от стартиране до стартиране на програмата. От една страна, такова самовъзпроизвеждане на числа от генератора е полезно, например, при отстраняване на грешки. От друга страна, това е нежелателно и може да създаде проблеми.

Следователно, за да се избегне повтарянето на поредица от числа, генераторът трябва да инициализира различни стойности при всяко стартиране на програмата. Само за тези цели можете да използвате семена. Стандартният начин за инициализиране на DWR е да му се изпрати семената стойност на времето

на файла ctime на заглавката. Това означава, че генераторът се инициализира по стойностравен на броя на секундите от януари 1 00 часа 00 минути 00 секунди, 1970 UTC.

Инициализиране на DVR от друг генератор

Времето за инициализиране може да не е достатъчно за решаване на редица проблеми. След това можете да дефинирате DVR чрез друг генератор. Тук бих искал да направя отстъпление и да говоря за един мощен инструмент, който ви позволява да създадете наистина случайни числа.

Random_device - Генератор на истински случайни числа

Всички генератори на псевдослучайни числа са детерминистични. Това означава, че те имат определение. Или, с други думи, генерирането на случайни числа се основава на математически алгоритми. Random_device не е детерминиран. Той създава числа въз основа на стохастични (произволни от други гръцки) процеси. Такива процеси могат да бъдат промени във фазата или амплитудата на текущите колебания, флуктуациите на молекулярните решетки, движението на въздушните маси в атмосферата и др.


& lt; script type = "text /javascript" & gt;
може blockSettings3 = {blockId: "R-A-70350-3", renderTo: "yandex_rtb_R-A-70350-3", async:! 0};

if (document.cookie.indexOf ("abmatch =") & gt; = 0) {
blockSettings3 = {blockId: "RA-70350-3", renderTo: "yandex_rtb_R-A-70350- 3 ", statId: 70350async: 0};
}

Функция (a, b, c, d, e) {a [c] = a [c] || [], a [c] .push (функция () {Ya .Context.AdvManager.render (blockSettings3)}), e = b.getElementsByTagName ("скрипт") , d = b.createElement ("скрипт"), d.type = "text /javascript", d.src = "//an.yandex.ru/system/context.js", d.async =! 0e.parentNode.insertBefore (d, e)} (това, този.документ, "yandexContextAsyncCallbacks");

Очевидно е, че не във всеки компютър, а не във всяка система, може да се вгради в възможността за получаване на случайно число на базата на стохастичен процес. Следователно, използването на random_device е полезно само ако е необходимо. Неговата работа можеразличават се от система към система, от компютър до компютър и може би е напълно недостъпна. Следователно, когато се използва генератор на случайни числа, е необходимо да се осигури обработка на грешки.

Използване на random_device като семена за DSPF

std :: random_device rd {};
std :: default_random_engine e {rd ()};

В този кодекс няма нищо ново. В същото време, с всеки старт, DIRF се инициализира от случайни стойности, генерирани от истинския генератор на случайни числа rd.

Също така трябва да се отбележи, че стойността на инициализацията на генератора може да бъде рестартирана по всяко време:

e.seed (15027); //се инициализира с номер
e.seed (); //се инициализира до стойността по подразбиране
e.seed (rd ()); //Инициализация от друг генератор

Обобщаваме: генератори и разпределения

Генератор (двигател) е обект, който ви позволява да създавате различни номера на вероятности.

Разпределението е обект, който преобразува поредица от числа, генерирани от генератор, в разпределение съгласно определен закон, например:

  • униформа (униформа);
  • нормално - Гауссово разпределение (нормално);
  • биномен (бином) и др.

Разгледайте генераторите на стандартната C ++ библиотека.

  1. Достатъчно е за начинаещите да използват default_random_engine, оставяйки избор на генератор в библиотеката. Генераторът ще бъде избран въз основа на комбинация от фактори като производителност, размер, качество на случайността.
  2. За напреднали потребители библиотеката предоставя 9 предварително конфигурирани генератора. Те са много различни един от другпроизводителността и размера, но в същото време качеството им на работа е подложено на сериозни тестове. Често се използват осцилатор, наречен Mersenne twister двигатели и неговия екземпляр mt19937 (създаване на 32-битови числа) и mt19937_64 (създаване на 64-битови числа). Генераторът е оптимална комбинация от скорост и степен на случайност. За повечето от предизвикателствата ще бъде достатъчно.
  3. За експерти библиотеката предоставя конфигурирани шаблони за генератори, които позволяват създаването на допълнителни видове генератори.

Разгледайте ключовите аспекти на разпределенията. В стандарта на техния език има 20 броя. В примера по-горе, равномерното разпределение на произволната C ++ библиотека в диапазона [a, b] за цели числа беше използвано uniform_int_distribution. Това разпределение може да се използва за реални числа: uniform_real_distribution със същите параметри a и b като генериране на числа. В същото време се включват границите на пропастта, т.е. [a, b]. Избройте всички 20 дистрибуции и повторете документацията на C ++ в статията, няма смисъл.

Следва да се отбележи, че всяко разпределение отговаря на нейния набор от параметри. За равномерно разпределение това е интервалът от a до b. А за геометричен (geometric_distribution) параметър има вероятност за успех p.

Повечето от разпределенията са дефинирани като шаблон на клас, за който параметърът е типът на последователните стойности. Някои дистрибуции обаче създават последователности само от int стойността или само от реалната стойност. Или например, последователността на Бернули (bernoulli_distribution) дава стойността на типа bool. Както при MHF, библиотечният потребител можесъздайте собствено разпространение и използвайте с вградени генератори или генератори, които ще създадат.

В това си качество библиотеките не са ограничени. Те са много по-широки. Но предоставената информация е достатъчна за използване и основно разбиране на генератора на случайни числа в C ++.

Кратко помощ: Случайни в .Net стил

.Net Frameworkът включва също Random class за създаване на псевдослучайни числа. Да разгледаме пример за генериране на произволно число C ++ /CLI.

За тези, които работят в Visual Studio и не могат да разберат защо пространството от имена на системата не е дефинирано.

За да работи .net е необходимо да свържете CLR. Това се прави по два начина: 1) Създаването на проект не е конзолно приложение на Windows, а CLR - конзолно приложение CLR (CLR Console Application). услуга ") - & gt; конфигурация - & gt; общо - & gt; стойност по подразбиране - & gt; в падащото меню "Поддръжка за общата среда на изпълнение (CLR)" изберете "Поддръжка на CLR среда (/clr)".

# включва "stdafx.h"
# включи

//използвайки пространството от имена система;

int main (масив ^ args)
{
System :: Random ^ rnd1 = gcnew System :: Random (); //създаваме MHF, по подразбиране се инициализира с текущото време
std :: cout rnd1- & gt; Next () "n"; //връща положително цяло число

int upper = 50;
std :: cout rnd1- & gt; Следваща (горна) "n"; //връща положително цяло число не повече от горно

int a = -1000; int b = -500;
std :: cout rnd1- & gt; Следваща (a, b) "n"; //връща цяло число в диапазона [a, b]

int seed = 13977;
Система :: Случайна ^ rnd2 = gcnew Система :: Случайна (семена); //инициализиране на MHFброй семена
std :: cout rnd2- & gt; Следваща (5001000) n; //същия номер ще бъде създаден при всяко стартиране на програмата.

std :: cout std :: endl;

връщане 0;
}

В този случай цялата работа се извършва с функцията Random Next C ++ /CLI.

Струва си да се отбележи, че .net е голяма библиотека с големи възможности и използва своя собствена версия на езика C ++ /CLI от общата езикова инфраструктура. Като цяло това е разширение на C ++ към платформата .Net.

Да разгледаме накрая няколко примера, за да разберем по-добре работата със случайни числа.# включи
# включва
# включва
int main () {
std :: mt19937 e1;
e1.seed (време

);
std :: cout e1 () std :: endl;

std :: mt19937 e2 (време

);

std :: mt19937 e3 {};
std :: uniform_int_distribution uid1 (510), uid2
;
std :: cout uid1 (e2) "," uid2 (e3) std :: endl;

std :: default_random_engine e4 {};
std :: uniform_real_distribution urd (051.2);
std :: normal_distribution nd (502.0); //нормално разпределение със средна стойност 5.0 и средно квадратично отклонение 2.0
std :: cout urd (e4) "," nd (e4) std :: endl;

std :: cout std :: endl;
система ("пауза");
връщане 0;
}


Заключение

Всички технологии и методи постоянно се развиват и усъвършенстват. Това се случи и с механизма за генериране на случайни числа rand (), който е остарял и престава да отговаря на съвременните изисквания. В STL има произволна библиотека, в .Net Framework - Random class за работа със случайни числа. Използването на ранд трябва да откаже ползите от новите методи, защото те отговарят на съвременните парадигми на програмиране, а старите методи ще бъдат изключени от стандарта.

Свързани публикации