В основному те, що ви просите, це "напіввипадковий" генератор подій, який генерує події з такими властивостями:
Середня швидкість, з якою відбувається кожна подія, зазначається заздалегідь.
Одна і та ж подія рідше відбудеться двічі поспіль, ніж це було б навмання.
Події не є повністю передбачуваними.
Один із способів зробити це спочатку реалізувати генератор випадкових подій, який задовольняє цілі 1 і 2, а потім додати деяку випадковість для задоволення мети 3.
Для генератора невипадкових подій ми можемо використовувати простий алгоритм відмирання . Зокрема, нехай p 1 , p 2 , ..., p n - відносна ймовірність подій 1 до n , а s = p 1 + p 2 + ... + p n - сума ваг. Потім ми можемо генерувати невипадкову максимально рівнорозподілену послідовність подій за допомогою наступного алгоритму:
Спочатку нехай e 1 = e 2 = ... = e n = 0.
Щоб генерувати подію, збільшуйте кожен е i на p i та виводите подію k, для якої e k найбільша (розрив зв'язків будь-яким способом).
Зменшення e k на s і повторіть з кроку 2.
Наприклад, з урахуванням трьох подій A, B і C, с p A = 5, p B = 4 і p C = 1, цей алгоритм генерує щось на зразок наступної послідовності виходів:
A B A B C A B A B A A B A B C A B A B A A B A B C A B A B A
Зверніть увагу, як ця послідовність з 30 подій містить рівно 15 As, 12 Bs і 3 Cs. Це не зовсім оптимально розподіляє - є кілька випадків з двох Як підряд, яких можна було б уникнути - але воно наближається.
Тепер, щоб додати випадковість до цієї послідовності, у вас є кілька (не обов'язково взаємовиключних) варіантів:
Ви можете дотримуватися порад Філіпа і підтримувати "колоду" з N майбутніх подій для деякого числа N відповідного розміру . Кожен раз, коли вам потрібно генерувати подію, ви вибираєте випадкову подію з колоди, а потім замінюєте її на наступний висновок події алгоритмом дитингування вище.
Застосовуючи це до наведеного вище прикладу, з N = 3, наприклад:
A B A B C A B B A B A B C A A A A B B A B A C A B A B A B A
тоді як N = 10 дає більш випадковий вигляд:
A A B A C A A B B B A A A A A A C B A B A A B A C A C B B B
Зверніть увагу, як загальні події A і B закінчуються набагато більше пробігів через перетасування, тоді як рідкісні події C все ще досить добре розподілені.
Ви можете ввести деяку випадковість безпосередньо в алгоритм відмирання. Наприклад, замість збільшення e i на p i на кроці 2, ви можете збільшити його на p i × випадковий (0, 2), де випадковий ( a , b ) - рівномірно розподілений випадкове число між a і b ; це дасть вихід таким чином:
A B B C A B A A B A A B A B A A B A A A B C A B A B A C A B
або ви могли б збільшити e i на p i + випадково (- c , c ), що призведе до отримання (для c = 0,1 × s ):
B A A B C A B A B A B A B A C A B A B A B A A B C A B A B A
або, для c = 0,5 × s :
B A B A B A C A B A B A A C B C A A B C B A B B A B A B C A
Зверніть увагу на те, як схема добавок має набагато сильніший рандомізуючий ефект для рідкісних подій С, ніж для загальних подій А і В, порівняно з мультиплікативною; це може бути або не бажано. Звичайно, ви також можете використовувати якусь комбінацію цих схем або будь-яке інше коригування приростів, якщо це зберігає властивість, середня приріст e i дорівнює p i .
Крім того, ви можете перешкодити виведенню алгоритму відмирання, іноді замінюючи вибрану подію k випадковою (обрану відповідно до необмеженої ваги p i ). Поки ви також використовуєте той же k на кроці 3, який ви виходите на етапі 2, процес дифірування все ще буде мати тенденцію до вирівнювання випадкових коливань.
Наприклад, ось деякий приклад результату, з 10% шансом на те, що кожна подія буде обрана випадковим чином:
B A C A B A B A C B A A B B A B A B A B C B A B A B C A B A
і ось приклад з 50% шансом, що кожен вихід буде випадковим:
C B A B A C A B B B A A B A A A A A B B A C C A B B A B B C
Ви також можете розглянути можливість подачі суміші чисто випадкових і пошкоджених подій у колоду / пулу змішування, як описано вище, або, можливо, рандомізувати алгоритм відмирання, вибравши k випадковим чином, як зважили e i s (трактуючи негативні ваги як нуль).
Пс. Ось декілька абсолютно випадкових послідовностей подій, з однаковими середніми показниками для порівняння:
A C A A C A B B A A A A B B C B A B B A B A B A A A A A A A
B C B A B C B A A B C A B A B C B A B A A A A B B B B B B B
C A A B A A B B C B B B A B A B A A B A A B A B A C A A B A
Дотична: Оскільки в коментарях відбулася певна дискусія щодо того, чи потрібно, щоб рішення на основі палуби дозволяли колоді спорожнятися до її поповнення, я вирішив зробити графічне порівняння декількох стратегій заповнення колоди:
Сюжет декількох стратегій для генерування напів випадкових монетних перегородок (із співвідношенням головок до хвостів у середньому 50:50). Горизонтальна вісь - це кількість обертів, вертикальна вісь - це сукупна відстань від очікуваного відношення, вимірюється як (голови - хвости) / 2 = голови - відкидні / 2.
Червоні та зелені лінії на графіку показують два алгоритми, що не базуються на палубі, для порівняння:
- Червона лінія, детерміновані відмирання : результати з парними нумераціями завжди є головами, результати з непарними номерами - це завжди хвости.
- Зелена лінія, незалежні випадкові повороти : кожен результат вибирається самостійно, навмання, з 50% шансом на голову та 50% шансом на хвости.
Інші три рядки (синій, фіолетовий та блакитний) показують результати трьох стратегій на основі палуби, кожна реалізована за допомогою колоди з 40 карт, яка спочатку заповнюється 20 картками «голови» та 20 картками «хвости»:
- Синя лінія, заповнення, коли порожня : Картки малюються випадковим чином, поки колода не буде порожньою, потім колоду поповнюють картками 20 «головок» та 20 картками «хвости».
- Фіолетова лінія, заповнення, коли наполовину порожня : Картки витягуються випадковим чином, поки на колоді не залишиться 20 карт; потім настилають колоду з 10 картками «голова» та 10 картками «хвости».
- Блакитна лінія, заповнення безперервно : Картки оформляються навмання; малюнки з парними нумераціями одразу замінюються карткою «голови», а непарні номери - карткою «хвости».
Звичайно, сюжет, наведений вище, - це лише одна реалізація випадкового процесу, але він є досить репрезентативним. Зокрема, ви бачите, що всі процеси, що базуються на палубі, мають обмежений ухил і залишаються досить близько до червоної (детермінованої) лінії, тоді як чисто випадкова зелена лінія зрештою відхиляється.
(Насправді відхилення синіх, фіолетових та блакитних ліній від нуля суворо обмежено розміром колоди: синя лінія ніколи не може відходити більше ніж на 10 кроків від нуля; фіолетова лінія може отримати лише 15 кроків від нуля і синя лінія може відхилятися не більше ніж за 20 кроків від нуля. Звичайно, на практиці будь-яка з ліній, що насправді досягають своєї межі, є вкрай малоймовірною, оскільки існує сильна тенденція повернутися ближче до нуля, якщо вони блукають занадто далеко вимкнено.)
На перший погляд, між різними стратегіями, що базуються на палубі, немає явної різниці (хоча, в середньому, синя лінія тримається дещо ближче до червоної лінії, а синя лінія тримається дещо далі), але більш уважний огляд синьої лінії виявляє чітку детерміновану схему: кожні 40 малюнків (позначені пунктирними сірими вертикальними лініями) синя лінія точно відповідає червоній лінії в нулі. Фіолетові та сині лінії не настільки суворо обмежені, і можуть триматися подалі від нуля в будь-якій точці.
Для всіх стратегій, що базуються на палубі, важливою особливістю, яка обмежує їх зміни, є той факт, що, хоча картки витягуються з колоди випадковим чином, колода поповнюється детерміновано. Якби картки, які використовувались для поповнення колоди, самі були обрані випадковим чином, усі стратегії, що базуються на палубі, стали б не відрізнятись від чистого випадкового вибору (зелена лінія).