Необхідність передбачуваного генератора випадкових випадків


151

Я розробник веб-ігор, і у мене виникла проблема зі випадковими числами. Скажімо, у гравця є 20% шансу отримати критичний удар своїм мечем. Це означає, що 1 з 5 звернень має бути критичним. Проблема в тому, що я отримав дуже погані реальні результати - іноді гравці отримують 3 критики за 5 хітів, іноді жоден за 15 хітів. Битви досить короткі (3-10 ударів), тому важливо отримати хороший випадковий розподіл.

В даний час я використовую PHP mt_rand(), але ми просто переміщуємо наш код на C ++, тому я хочу вирішити цю проблему в новому двигуні нашої гри.

Я не знаю, чи є рішення яким-небудь рівномірним випадковим генератором чи, можливо, запам'ятати попередні випадкові стани, щоб примусити правильний розподіл.


58
Існує приблизно 0,5% шансів на рівно 3 критичні удари та 2 некритичні та 3,5% шанси на 15 некритичних потраплянь поспіль, якщо вважати справжні випадкові числа.
Ніксуз

10
+1 вище. Однією з характеристик випадкових чисел є те, що ви отримуєте аутлієри.
ЗанепокоєнийOfTunbridgeWells

45
@Nixus: Ні, це приблизно 5% шанс на 3 критичні удари та 2 некритичні, ви забудете помножити на (5! / (3! * 2!)) = 10. З 95% рівнем довіри це не є статистично малоймовірним, що 3 критичні удари відбудуться за 5 ударів.
erikkallen

7
Спочатку я подумав, що це дурне питання ... в черговий раз мене принижує SO.
SergioL

Відповіді:


39

Я погоджуюся з попередніми відповідями, що реальна випадковість у невеликих запусках деяких ігор небажана - це здається занадто несправедливим для деяких випадків використання.

Я написав простий мішок Shuffle, як реалізація в Ruby, і провів тестування. Реалізація зробила це так:

  • Якщо це все-таки здається справедливим або ми не досягли порогу мінімальних рулонів, він повертає справедливий удар на основі звичайної ймовірності.
  • Якщо спостережувана ймовірність минулих роликів здається несправедливою, вона повертає хіт "справедливого".

Вважається несправедливим на основі граничних ймовірностей. Наприклад, при ймовірності 20% ви можете встановити 10% як нижню межу і 40% як верхню межу.

Використовуючи ці межі, я виявив, що при виконанні 10 звернень 14,2% часу справжня псевдовипадкова реалізація дала результати, що вийшли за ці межі . Близько 11% випадків за 10 спроб було забито 0 критичних ударів. 3,3% часу, 5 або більше критичних хітів були висаджені з 10. Природно, використовуючи цей алгоритм (з мінімальним рахунком 5), значно менша кількість (0,03%) пробігів "Fairish" вийшла за межі . Навіть якщо нижченаведена реалізація непридатна (звичайно, можна зробити більш розумні речі), варто відзначити, що помітно часто ваші користувачі відчують, що це несправедливо з реальним псевдовипадковим рішенням.

Ось м'ясо мого, FairishBagнаписане в Рубі. Повна реалізація та швидке моделювання Монте-Карло доступне тут (суть) .

def fire!
  hit = if @rolls >= @min_rolls && observed_probability > @unfair_high
    false
  elsif @rolls >= @min_rolls && observed_probability < @unfair_low
    true
  else
    rand <= @probability
  end
  @hits += 1 if hit
  @rolls += 1
  return hit
end

def observed_probability
  @hits.to_f / @rolls
end

Оновлення: Використання цього методу збільшує загальну ймовірність критичного удару до приблизно 22%, використовуючи межі вище. Ви можете компенсувати це, встановивши його "реальну" ймовірність трохи нижче. Імовірність 17,5% із справедливою модифікацією дає спостережувану довгострокову ймовірність близько 20%, а також підтримує короткострокові перспективи.


Я думаю, що це найкраще рішення, яке відповідає моїм потребам. Мішок перемішання, згаданий у кращій загостреній відповіді, не є поганим, але потребує безлічі обчислень, і мені подобається найпростіше рішення, яке веде до мети.
Мислитель

Стів Рабін написав цікаву статтю про випадковість в іграх. Коротше кажучи, справжня "випадкова" поведінка насправді не "почувається" випадковою для більшості людей, і дослідження підтвердили це. Його стаття називається «Фільтрована випадковість для рішень AI та логіка ігор» та з’являється у «Мудрості програмування ігор AI 2» (2003). Ви повинні це перевірити, напевно, вам буде корисно.
Джефф Такер

@IanTerrell Було б добре зазначити, наскільки великий розмір вибірки ваших тестів, тобто скільки битв, щоб визначити ці ймовірності.

@ user677656: Це в суті, але це 100 тис.
Ian Terrell

223

Це означає, що 1 з 5 звернень має бути критичним. Проблема в тому, що я отримав дуже погані реальні результати - іноді гравці отримують 3 критики за 5 хітів, іноді жоден за 15 хітів.

Те, що вам потрібно, - мішок з перетасуванням . Він вирішує проблему, коли справжній випадковий спосіб є занадто випадковим для ігор.

Алгоритм приблизно такий: Ви поміщаєте 1 критичний і 4 некритичні удари в мішок. Потім ви рандомізуєте їх замовлення в сумці і вибираєте їх по одному. Коли сумка порожня, ви знову наповнюєте її тими самими значеннями і рандомізуєте її. Таким чином ви отримаєте в середньому 1 критичний удар на 5 звернень, і максимум 2 критичних та 8 некритичних звернень поспіль. Збільште кількість предметів у сумці для більшої випадковості.

Ось приклад реалізації (на Java) та її тестових випадків, про які я писав деякий час тому.


21
+ 1 за гарну ідею без критики. Масштабуйте сумку для вищого ступеня випадковості та
вирішуйте

16
У вас може бути розмір сумки 10. Поставте 1 удар, плюс 30% шансу на секунду. Якщо критичний шанс зміниться, ви можете просто викинути сумку і почати новий. Зауважте, що будь-яка така схема ризикує, що якщо ви (або ваш опонент) знаєте критичну ймовірність удару та розмір мішка, іноді ви можете точно знати, що ви не отримаєте іншого критичного значення для певної кількості рулонів. Це може вплинути на тактику.
Стів Джессоп

3
Так ... у чомусь подібному ви виконуєте риски, подібні до підрахунку карт. Чи ризикую я піоном чи займатись великим вбивством ... невеликий фіксований набір потенційних результатів може зменшити ризик втрати та збільшити шанс бути "ігровим"
Matthew Whited

8
Мені подобається ідея мішка перетасування, але я не думаю, що це відповідає грі "дух", тому що ваша критична ймовірність удару в 20% (що означає, що ви не можете виконати жодне за 10 ударів) вже не є ймовірністю. Це стає рівно 1 хітом кожні 5 звернень. Більше того, перемотка мішка, якщо критична зміна ймовірності удару спричинить збої в грі. Якщо мій критичний удар вдалося зробити, я заклинаю себе, щоб отримати наступного критика раніше: p
Michaël Carpentier

2
@Jonathan: зробивши сумку великого розміру, ви даєте їй фактично скасовує всю ідею мішка: переконатися, що щось станеться (критичне, що досягається) в межах прийнятної кількості кидків. Зробити мішок 50000 великим - це приблизно те саме, що використовувати генератор випадкових чисел.
dstibbe

113

У вас є непорозуміння, що означає випадкові.

Хто з них є більш випадковим?

введіть тут опис зображення введіть тут опис зображення

Хоча другий сюжет виглядає більш рівномірно, тим більш випадковим є насправді перший сюжет. Людський розум часто бачить візерунки у випадковості, тому ми бачимо скупчення на першому сюжеті як візерунки, але їх немає - вони просто частина випадково вибраного зразка.


25
Гарне пояснення Numb3rs!
RMAAlmeida

9
Технічно кажучи, ви не можете виміряти випадковість. Обидва розподіли для мене виглядають досить довільно, хоча, на мою думку, обидва були створені алгоритмічно. Ви можете виконати ряд тестів на першому сюжеті і визначити, що він, ймовірно, виходить із процесу, який розміщує точки відповідно до рівномірного розподілу, але ви не зможете зробити висновок, що він є більш випадковим. Як контрприклад, ви можете зробити сюжет, як перший, використовуючи лінійний конгруентний генератор, а потім зробити сюжет, як другий, використовуючи посилений шум від ценерового діода. Спробуйте слово неспоріднене замість випадкового.
Дітріх Епп

8
Ви можете виміряти ймовірність того, що вказаний розподіл є випадковим.
ceejayoz


2
Справедливості, хоча ОР, можливо, не використовує правильних термінів, він розуміє, що генератор випадкових чисел дає йому щось більше, як перший графік, коли він хоче щось більше, як другий графік, тому що він відчуває себе більш «справедливим» до користувача.
Кіп

88

Зважаючи на поведінку, про яку ви просите, я думаю, ви рандомізуєте неправильну змінну.

Замість того, щоб рандомізувати, чи буде цей удар критичним, спробуйте рандомізувати кількість витків до наступного критичного удару. Наприклад, просто виберіть число від 2 до 9 кожного разу, коли гравець отримує критичне значення, а потім дайте їм наступний критичний результат після того, як пройде багато раундів. Ви також можете використовувати методи кістки, щоб наблизитись до нормального розподілу - наприклад, наступний критичний результат ви отримаєте за 2D4 витки.

Я вважаю, що ця методика застосовується і в RPG, які також мають випадкові зустрічі у світовому світі - ви рандомізуєте лічильник кроків, і після цього багато кроків ви знову потрапляєте. Це відчуває себе набагато справедливіше, бо ти майже ніколи не потрапляєш у дві зустрічі поспіль - якщо це трапляється навіть один раз, гравці стають роздратованими.


Я думаю, що це чудове рішення, але як щодо 80% шансів?
Мислитель

Мені подобається часом використовувати багатовимірні випадкові генератори. Шанс удару + Шанс на владу + Шанс до критичного. Так само, як
котити

Мені подобається ця ідея, і ти абсолютно правильний щодо того, що стосується крокової лічильника, яка використовується у фінальній фантазії дуже давно, наприклад
Ед Джеймс,

Одна з проблем цього полягає в тому, що воно працює лише в тому випадку, якщо ймовірність критичного значення є приблизно постійною між зверненнями. Припустимо, що в середині бою гравець кидає заклинання, що подвоює їхню ймовірність критичного удару. Тоді як ви регулюєте кількість витків?
Alex319

+1 Дуже добре, також обробляє менше ймовірностей кругового удару, ніж 20%, дуже легко.
Еліс Перселл

53

Спочатку визначте «правильний» розподіл. Випадкові числа - ну, випадкові - результати, які ви бачите, повністю відповідають (псевдо) випадковістю.

Розширюючи це, я припускаю, що ви хочете - це відчуття «справедливості», тому користувач не може пройти 100 оборотів без успіху. Якщо так, я б відслідковував кількість відмов з моменту останнього успіху і зважував на отриманий результат. Припустимо, ви хочете, щоб 1 - 5 рулонів "досягли успіху". Таким чином, ви випадково генеруєте число від 1 до 5, а якщо це 5 - чудово.

Якщо ні, запишіть провал, і наступного разу створіть число від 1 до 5, але додайте, скажімо, підлогу (numFailures / 2). Тож цього разу вони знову мають шанс 1 на 5. Якщо вони провалюються, наступний раз переможний інтервал - 4 та 5; 2 з 5 шансів на успіх. З цим вибором, після 8 невдач, вони впевнені в успіху.


У цій примітці ... чи вплине діапазон випадкових чисел на розподіл ... наприклад, вибір Random r = new Random (); r.Next (1,5) vs. r.Next (1, 1000000)% 200000
Eoin Campbell

23
+1 за те, що бачив проблему в запиті замість того, щоб повідомити ОП, що він неправильно розуміє випадково.
Борис Калленс

4
Зауважте, що якщо ви це зробите, то їх загальна частка успіхів буде більшою, ніж 1 на 5. Шлях, який полягає у тому, щоб (наприклад) обрати 20 випадкових чисел навмання з діапазону 1..100, і заздалегідь визначити, що ці бути їх критикою. Хоча це ще велика кількість бухгалтерій.
Стів Джессоп

"буде більшим, ніж 1 на 5" - я маю на увазі, що це буде в перспективі.
Стів Джессоп

Ви можете трохи зменшити початкову ймовірність, щоб загальна частка зменшилася до 1/5. Я не розробив, на скільки вам доведеться зменшити це, але все безперервно, тому повинна бути правильна відповідь.
Стів Джессоп

35

Як щодо заміни mt_rand () на щось подібне?

Комічний XKCD (RFC 1149.5 вказує 4 як стандартне випадкове число, перевірене IEEE.)

(RFC 1149.5 вказує 4 як стандартне випадкове число, перевірене IEEE.)

Від XKCD .


Хороший, але це вирішить проблему розподілу випадкових чисел OPs? ;-)
Арджан Ейнбу

Не зовсім; Він просить не випадкову RNG, для якої він міг би використовувати цю функцію; але чого він насправді хоче, краще пояснити поточною верхньою відповіддю ( stackoverflow.com/questions/910215/910224#910224 )
Колін Пікард

28
Це більш випадково, ніж хоче ОП
Шагдаш Текін,

-1 тому, що RFC1149 не має розділу 5 (і поперемінно, немає 1149,5); +1 для справедливої ​​випадковості.
greyfade

34

Сподіваємось, ця стаття допоможе вам: http://web.archive.org/web/20090103063439/http://www.gamedev.net:80/reference/design/features/randomness/

Цей метод генерації "випадкових чисел" поширений в іграх rpg / mmorpg.

Проблема, яку вона вирішує, така (витяг):

Павук леза у вас в горлі. Це б’є, і ти сумуєш. Він б’є знову, і ти знову сумуєш. І знову і знову, поки від вас нічого не залишилося бити. Ти мертвий, а над твоїм трупом гріється двотонний павукоподібний. Неможливо? Ні. Неймовірне? Так. Але якщо враховувати достатню кількість гравців і давати достатньо часу, неймовірне стає майже певним. Справа не в тому, що лезо павука було важким, це просто невдача. Як засмучує. Досить змусити гравця захотіти вийти.


1
Я чув про це варіант - "подія на мільйон трапляється 6000 людей у ​​світі".
ceejayoz

19

Те, що ви хочете, - це не випадкові числа, а числа, які здаються людині випадковими. Інші вже запропонували індивідуальні алгоритми, які можуть вам допомогти, як Shuffle Bad.

Щоб отримати детальний та розгорнутий аналіз цього домену, див. Програмування WI Game Wisdom 2 . Усю книгу варто прочитати для будь-якого розробника ігор, ідея "начебто випадкових чисел" розглядається в главі:

Фільтрована випадковість для рішень AI та логіки гри :

Анотація: Звичайна мудрість говорить про те, що чим краще генератор випадкових чисел, тим більш непередбачуваною буде ваша гра. Однак, згідно з психологічними дослідженнями, справжня випадковість за короткий термін часто виглядає для людей безсумнівною. У цій статті показано, як приймати випадкові рішення AI та логіку гри виглядають більш випадковими для гравців, зберігаючи при цьому сильну статистичну випадковість.

Також вам може бути цікава інша глава:

Статистика випадкових чисел

Анотація: Випадкові числа найбільше використовуються штучним інтелектом та іграми загалом. Ігнорувати їх потенціал - це зробити гру передбачуваною і нудною. Неправильне використання їх може бути так само погано, як ігнорування їх прямо. Розуміння того, як генеруються випадкові числа, їх обмеження та можливості, може усунути багато труднощів використання їх у грі. Ця стаття пропонує розуміння випадкових чисел, їх генерування та способів відокремлення хороших від поганих.


8

Невже будь-яке генерація випадкових чисел має шанс створити такі пробіги? Ви не збираєтеся отримати достатньо великий зразок у 3-10 рулонах, щоб побачити відповідні відсотки.

Можливо, те, що ви хочете, - це поріг милосердя ... згадайте останні 10 рулонів, і якщо вони не мали критичного удару, дайте їм халяву. Згладьте стропи та стрілки випадковості.


8

Найкращим рішенням може бути тестування гри з декількома різними не випадковими схемами і вибрати ту, яка робить гравців щасливішими.

Ви також можете спробувати політику відшкодування на ту саму кількість у даній зустрічі, наприклад, якщо гравець прокатить «А» 1на першому ходу, його прийняти. Щоб отримати ще один, 1їх потрібно розкачати 2 1с підряд. Щоб отримати третину, 1їм потрібно 3 поспіль, ad infinitum.


7

На жаль, те, що ви просите, - це фактично генератор випадкових чисел - адже ви хочете, щоб попередні результати були враховані при визначенні наступного числа. Боюся, це не так, як працюють генератори випадкових чисел.

Якщо ви хочете, щоб 1 з кожних 5 звернень був критичним, просто виберіть число від 1 до 5 і скажіть, що це звернення буде критичним.


1
Він хоче грати випадковим випадковим чином, якщо ви використовуєте суворі випадкові згенеровані числа в деяких випадках, ви отримуєте "корейські випадкові" результати. Це випадкові результати, які змушують гравців занадто часто сердитися і розчаровуватися (запитайте будь-якого гравця лінії 2);)
Juan Techera

Отже, якщо перший удар стане критичним, наступних чотирьох не буде. Це звучить так, як цього хоче ОП, але коли ви це так вимовляєте, це звучить відстало. Ви отримаєте мій УФ за це.
belgariontheking

-1 ви, мабуть, плутаєте "випадковий" з "безпам'ятним" - en.wikipedia.org/wiki/Memorylessness
Еліс Перселл

7

mt_rand () заснований на реалізації Mersenne Twister , а це означає, що він дає один з найкращих випадкових розподілів, які ви можете отримати.

Мабуть, те, що ви хочете, - це зовсім не випадковість, тому вам слід почати з конкретизації того, що саме ви хочете. Ви, мабуть, зрозумієте, що у вас є суперечливі очікування - що результати мають бути справді випадковими і не передбачуваними, але в той же час вони не повинні демонструвати локальні відмінності від заявленої ймовірності - але тоді вони стають передбачуваними. Якщо ви встановите максимум 10 некритиків поспіль, то ви щойно сказали гравцям, "якщо у вас 9 некритиків підряд, наступний буде критично зі 100% впевненістю" - ви можете добре не турбуватися випадковістю взагалі.


6

Протягом такої невеликої кількості тестів слід очікувати таких результатів:

Справжня випадковість передбачувана лише при величезному наборі розмірів, таким чином, що цілком можливо перевернути монету та отримати голову 3 рази поспіль, однак за кілька мільйонів обертів ви отримаєте приблизно 50-50.


7
Хоча є ймовірність, що через кілька мільйонів перевернень ви все одно побачили лише одну сторону монети. Хоча якщо це колись трапиться, ви, мабуть, сидите занадто близько до нескінченного приводу неймовірності: P
Грант Петерс,

ха-ха, але шанс настільки неймовірно низький, що закони математики говорять, що ви повинні бачити рівномірне (іш) розподіл.
Ед Джеймс

6

Я бачу багато відповідей, які пропонують відстежувати раніше створені числа або переміщувати всі можливі значення.

Особисто я не згоден, що 3 критика поспіль - це погано. Я також не згоден, що 15 некритиків поспіль - це погано.

Я б вирішив проблему, змінивши критичний шанс самостійно, після кожного числа. Приклад (для демонстрації ідеї):

int base_chance = 20;
int current_chance = base_chance;

int hit = generate_random_number(0, 100) + 1; // anything from 1 to 100
if(hit < current_chance)//Or whatever method you use to check
{
    //crit!
    if(current_chance > base_chance)
        current_chance = base_chance; // reset the chance.
    else
        current_chance *= 0.8; // decrease the crit chance for the NEXT hit.
}
else
{
    //no crit.
    if(current_chance < base_chance)
        current_chance = base_chance; // reset the chance.
    else
        current_chance *= 1.1; // increase the crit chance for the NEXT hit.
    //raise the current_chance
}

Чим довше ви не отримаєте критику - тим більше шансів на наступну дію до критики. Скидання, яке я включив, є цілком необов’язковим, і йому знадобиться тестування, щоб визначити, потрібен він чи ні. Після довгого некритичного ланцюжка дій може бути, або не бажано, щоб дати більш високу ймовірність виникнення критерію для більш ніж однієї дії поспіль.

Просто кидаю мої 2 копійки ...


Мені подобається такий підхід. Я, мабуть, зробив би це інакше. Почніть з нижчого шансу і нарощуйте максимум до 20% + деякий доданий відсоток, поки він не потрапить і знову скиньте його на якусь низьку суму.
Матвій

5

Кілька найкращих відповідей - це чудові пояснення, тому я просто зосередитимусь на алгоритмі, який дає вам змогу контролювати ймовірність "поганих смуг", але ніколи не стає детермінованим. Ось що я думаю, що вам слід зробити:

Замість вказівки p , параметр розподілу Бернуллі, який є вашою ймовірністю критичного попадання, вкажіть a і b , параметри бета-розподілу, "кон'югат попереднього" розподілу Бернуллі. Вам потрібно стежити за A і B , кількістю критичних і некритичних звернень досі.

Тепер, щоб вказати a і b , переконайтесь, що a / (a ​​+ b) = p, шанс критичного удару. Акуратне в тому, що (a + b) кількісно визначає, наскільки близько ви хочете, щоб A / (A + B) було p в цілому.

Ви робите вибірку так:

нехай p(x)буде функція щільності ймовірності бета-розподілу. Він доступний у багатьох місцях, але ви можете знайти його в GSL як gsl_ran_beta_pdf.

S = A+B+1
p_1 = p((A+1)/S)
p_2 = p(A/S)

Виберіть критичне враження шляхом вибірки з розподілу Бернуллі з вірогідністю p_1 / (p_1 + p_2)

Якщо ви виявите, що випадкові числа мають занадто багато "поганих смуг", збільшують значення a і b , але в межах, як a і b переходять до нескінченності, ви матимете описаний раніше підхід до переміщення сумки.

Якщо ви реалізуєте це, будь ласка, дайте мені знати, як це відбувається!


5

Якщо ви хочете, щоб розподіл, який відлякує повторення значень, може скористатися простим алгоритмом відхилення повтору.

напр

int GetRand(int nSize)
{
    return 1 + (::rand() % nSize);
}
int GetDice()
{
    static int nPrevious=-1;
    while (1) {
        int nValue = GetRand(6);
        // only allow repeat 5% of the time
        if (nValue==nPrevious && GetRand(100)<95)
            continue;
        nPrevious = nValue;
        return nValue;
    }
}

Цей код відкидає значення повторів у 95% часу, що робить повторення малоймовірними, але не неможливими. Статистично це трохи некрасиво, але це, ймовірно, призведе до бажаних результатів. Звичайно, це не завадить розповсюдженню типу "5 4 5 4 5". Ви можете стати більш фантазійним і відхилити другий останній (скажімо) 60% часу і третій останній (скажімо) 30%.

Я не рекомендую це як гарний дизайн гри. Просто запропонувавши, як досягти того, що ви хочете.


Деякі значення в моїй грі, такі як критичний удар, не можуть мати більше 50% шансів, тому я блокую повторення зовсім, але це знижує шанс події на деякий відсоток.
Мислитель

4

Не зовсім зрозуміло, чого ти хочеш. Можна створити таку функцію, що перші 5 разів, коли ви її зателефонуєте, вона повертає числа 1-5 у випадковому порядку.

Але це насправді не випадково. Гравець буде знати, що він отримає рівно один 5 в наступних 5 атак. Це може бути те, що ви хочете, хоча, і в цьому випадку вам просто доведеться це кодувати самостійно. (створити масив, що містить числа, а потім перемістити їх)

Крім того, ви можете продовжувати використовувати поточний підхід і припускати, що ваші поточні результати зумовлені поганим генератором випадкових випадків. Зауважте, що з вашими поточними номерами нічого не може бути. Випадкові значення є випадковими. іноді ви отримуєте 2, 3 або 8 однакового значення підряд. Тому що вони випадкові. Хороший випадковий генератор просто гарантує, що в середньому всі числа будуть повертатися однаково часто.

Звичайно, якщо ви використовуєте поганий генератор випадкових випадків, це може призвести до перекосу ваших результатів, і якщо так, просто переключення на кращий випадковий генератор має вирішити проблему. (Перевірте бібліотеку Boost.Random для кращих генераторів)

Крім того, ви можете запам'ятати останні N значень, повернених випадковою функцією, і зважити результат за ними. (простий приклад - "для кожного виникнення нового результату є 50% шансів, що ми повинні відкинути значення та отримати новий"

Якби мені довелося здогадатися, я б сказав, що дотримуватися "фактичної" випадковості - це найкраща ставка. Переконайтеся, що ви використовуєте хороший випадковий генератор, а потім продовжуйте так, як це робите зараз.


Насправді функція, яку він використовує, така сама, як і найкраща RNG в бібліотеці підвищення.
Майкл Боргвардт

MT не є "найкращим", востаннє я перевірив. Це приємно, просто і швидко, але не дає найкращого розповсюдження. У будь-якому випадку візьміть мільйон випадкових чисел і перевірте розподіл. З’ясуйте, чи насправді ваша випадкова функція дає рівномірний розподіл чи ні. Якщо цього не відбувається, знайдіть кращий генератор. Якщо це так, або висмоктуйте його, і приймайте випадкові ряди критик, або обманюйте і робите результати менш випадковими та більш передбачуваними.
jalf

4

Ви можете створити список, що містить числа від 1 до 5, і сортувати їх за випадковістю. Потім просто перегляньте створений вами список. У вас є гарантія, що ви хочете хоча б один раз перейти на кожне число ... Коли ви закінчите з першими 5, просто створіть ще 5 номерів ...


4

Я рекомендую прогресивну систему відсотків, як використовує Blizzard: http://www.shacknews.com/onearticle.x/57886

Як правило, ви скочуєте RNG, а потім порівнюєте його зі значенням, щоб визначити, вдався чи ні. Це може виглядати так:

if ( randNumber <= .2 ) {
   //Critical
} else {
   //Normal
}

Все, що вам потрібно зробити, - це поступово збільшувати базовий шанс ...

if (randNumber <= .2 + progressiveChance ) {
   progressiveChance = 0;
   //Critical
} else {
   progressiveChance += CHANCE_MODIFIER;
   //Normal hit
}

Якщо вам це потрібно, щоб бути більш вигадливим, додайте більше. Ви можете обмежити суму, яку може отримати прогресивнийChance, щоб уникнути 100% критичного шансу або скинути його на певні події. Ви також можете мати прогресивне збільшення в менших розмірах, що збільшує кожен прискорення чимось на кшталт progressiveChance + = (1 - progressiveChance) * SCALE, де SCALE <1.


4

Ну, якщо ви трохи вивчаєте математику, ви, можливо, можете спробувати Експоненціальний розподіл

Наприклад, якщо лямбда = 0,5, очікуване значення дорівнює 2 (перечитайте цю статтю!), Це означає, що ви, швидше за все, потрапите на / критично / що завгодно кожного другого ходу (наприклад, 50%, так?) Але при такому розподілі ймовірностей ви визначите, що на 0-му ходу будете пропускати (або робити навпаки, що завгодно) (той, у якому подія вже відбувся і перезавантажений поворот), матиме 40% шансів потрапити на наступну чергу, приблизно 65% шанс зробити це другий (наступний за наступним) поворот, приблизно 80%, щоб потрапити на 3-й т. ін.

Вся мета цього розподілу полягає в тому, що якщо у вас є 50% шансів на потрапляння, і він пропускає 3 рази поспіль, він буде шалено (ну, більше 80% шансів, і це збільшується з кожним наступним ходом). Це призводить до більш «справедливих» результатів, зберігаючи шанси в 50% без змін.

Якщо у вас є 20% шанс на критику, у вас є

  • 17% до 1-ї черги
  • 32% до критичного другого ходу, якщо жоден критик не виникає у всіх попередніх.
  • 45% до критичного 3-го розвороту, якщо жоден критик не виникає у всіх попередніх.
  • 54% до критичного четвертого ходу, якщо жоден критик не виникає у всіх попередніх.
  • ...
  • 80% до 8-го розвороту, якщо у всіх попередніх не виникає жодних.

Це ще близько 0,2% (проти тих 5%) шанс на 3 критерії + 2 некритики за 5 наступних оборотів. І є 14% шансів на 4 наступні некритика, 5% на 5, 1,5% на 6, 0,3% на 7, 0,07% на 8 наслідкових некритиків. Я ставлю ставку на її "більш справедливу", ніж 41%, 32%, 26%, 21% і 16%.

Сподіваюсь, ви все ще не нудьгуєте до смерті.


це досить схоже на моє рішення, за винятком того, що воно лише «пам’ятає» час з моменту останнього критичного удару. Згідно з цим рішенням, рядок з 4 критичних звернень - це те саме, що і рядок з 1 критичним зверненням, наскільки можливим є майбутнє. Отже, якщо критичні удари хороші, це рішення обмежує ваш ризик зворотного боку, але не ваш перевернутий. Моє рішення стосується обох.
Ніл Г

Очевидно, що різні рішення мають свої переваги. Цей фокусується на збереженні випадковості чистими з точки зору науки. Це не означає, що це якось краще, ніж сумка для переміщення або щось інше. Це просто рішення, яке, здається, варто спробувати.
Темний

3

Що щодо створення шансу критично залежати від останніх N атак. Одна проста схема - це якась маркова ланцюг: http://en.wikipedia.org/wiki/Markov_chain, але код все одно дуже простий.


IF turns_since_last_critical < M THEN 
   critial = false
   turns_since_last_critical++;
ELSE
   critial = IsCritical(chance);
   IF Critial THEN
       turns_since_last_critica = 0;
   ELSE
       turns_since_last_critica++;
   END IF;
END IF;

Звичайно, ви повинні зробити свою математику, тому що шанс критичного є нижчим, ніж шанс критичного, коли ви дізнаєтесь, що з останнього було достатньо оборотів


Ви отримуєте більшу частину ефекту, просто враховуючи останню атаку. Нехай P - спостережувана частота ударів, R - частота потрапляння після промаху і R / 2 - частота потрапляння після удару. За визначенням, у будь-який момент у вас є шанс потрапити на P = P * R + (1-P) * (R / 2). Це означає P = R / (2-R)
MSalters

2

ОП,

Досить багато, якщо ви хочете, щоб це було справедливо, це не буде випадковим.

Проблема вашої гри - фактична тривалість матчу. Чим довше збіг, тим менше випадковостей ви будете бачити (критичні показники, як правило, становитимуть 20%), і це буде наближатися до заданих значень.

У вас є два варіанти, попередньо обчисліть атаки на основі попередніх рулонів. Який ви будете отримувати один крит кожні 5 атак (виходячи з ваших 20%), але ви можете зробити порядок, коли це відбувається випадковим чином.

listOfFollowingAttacks = {Хіт, Хіт, Хіт, Міс, Крит};

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

Це шаблон, який я створив для своєї гри, він працює досить добре, для того, що я хочу зробити.

Вашим другим варіантом буде, збільшити шанс критикувати, ви, мабуть, побачите більш рівне число в кінці всіх атак (припускаючи, що ваші поєдинки закінчуються досить швидко). Чим менше відсотків шансів, тим більше RNG ви отримаєте.


2

Ви дивитеся на лінійний розподіл, коли, напевно, хочете нормального розподілу.

Якщо ви ще в молодості пам’ятаєте, що грали в D&D, вас попросили прокатати декілька n-sided die, а потім підсумуйте результати.

Наприклад, прокатка 4-х 6-гранних штампів відрізняється від прокатки 1х 24-сторонніх кісток.


2

Місто Героїв насправді має механіку під назвою "проривник смужок", який вирішує саме цю проблему. Як це працює, це те, що після ряду пропусків довжини, пов’язаних з найнижчою ймовірністю удару в рядку, наступна атака гарантовано буде ударом. Наприклад, якщо ви пропустите атаку з більш ніж 90% для випадкового удару, то наступна атака автоматично вдариться, але якщо ваш шанс потрапити нижче, як 60%, то вам знадобиться мати кілька послідовних промахів, щоб спровокувати "прорив смуги" (я не знаю точних цифр)


2

alt текст

це справді передбачувано ... але ви ніколи не можете бути впевнені.


Це повинні бути мої шпалери для робочого столу !!

0

Як щодо зважування вартості?

Наприклад, якщо у вас є 20% шансу на критичний удар, створіть число від 1 до 5 з одним числом, яке представляє критичне потрапляння, або число від 1 до 100, при цьому 20 цифр є критичним ударом.

Але поки ви працюєте з випадковими чи псевдовипадковими числами, немає жодного способу потенційно уникнути результатів, які ви зараз бачите. Це природа випадковості.


І навіщо це матиме значення? Існує абсолютно однаковий шанс отримати критичне значення для обох наборів чисел.
samjudson

Саме так. Я лише представляю два варіанти для нього на прикладі 20%. Хоча 100, ймовірно, буде працювати краще, якщо ви маєте справу з цілими відсотковими числами, так як вам потрібно було б просто імітувати один "помер", якщо ви хочете думати про це так.
Томас Оуенс

Варіанти, які ви представляєте, - це саме те, що він уже робить.
ceejayoz

2
Не для того, що він хоче. Він хоче генератор невипадкових чисел, навіть якщо він думає, що це називається генератором випадкових чисел.
ceejayoz

0

Реакція на тему: "Проблема в тому, що я отримав дуже погані реальні результати - іноді гравці отримують 3 критики за 5 хітів, іноді жоден за 15 хітів".

У вас є шанс десь від 3 до 4% отримати нічого за 15 звернень ...


Коли у вас в мережі 3500 гравців, які ведуть 10000 боїв за хвилину, проблема, яка виникає в 3% боїв, є дуже поширеною проблемою.
Мислитель

Знову ж таки, невдача, яка трапляється в 3% битв, - це все-таки лише невдача.
MSalters

0

Я б запропонував наступне "випадково відкладене відключення":

  • Підтримуйте два масиви, один ( in-array), спочатку заповнений значеннями від 0 до n-1, інший ( out-array) порожнім
  • Коли запитується результат:
    • повернути випадкове значення з усіх визначених значень уin-array
    • перемістити це значення з in-arrayнаout-array
    • перемістити один випадковий (над усіма елементами, включаючи невизначений!) елемент out-arrayназад вin-array

Це властивість, що вона буде "реагувати" повільніше, чим більша n . Наприклад, якщо ви хочете 20% шансу, встановити n до 5 і натиснути на 0 - "менш випадково", ніж встановити n в 10 і натиснути на 0 або 1, і зробити 0 до 199 з 1000 буде майже відмінні від справжньої випадковості на невеликому зразку. Вам доведеться відрегулювати n під розмір вибірки.


0

Попередньо обчисліть випадковий критичний удар для кожного гравця.

// OBJECT
//...
// OnAttack()
//...
c_h = c_h -1;
if ( c_h == 0 ) {
 // Yes, critical hit!
 c_h = random(5) + 1 // for the next time
 // ...
}

0

Я думаю, можливо, ви використовуєте неправильну функцію випадкового розподілу. Напевно, ви не хочете рівномірного розподілу за номерами. Спробуйте скористатися звичайним розподілом, щоб критичні звернення стали рідше, ніж "регулярні" звернення.

Я працюю з Java, тому я не впевнений, де ви можете знайти щось для C ++, яке дає вам випадкові числа з нормальним розподілом, але там щось має бути.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.