Безперервне зважене випадкове розподіл, упереджене до одного кінця


28

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

Мій рівномірний випадковий розподіл по лінії або вздовж прямокутної області працює добре - немає проблем.

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

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


~

Хіба ніхто не збирається згадувати обчислення?
Alec Teal

Відповіді:


42

Подивіться на цю картинку:

Зображення кривих

Він показує процес відображення (випадкового) значення кривої. Припустимо, ви генеруєте рівномірно розподілене випадкове значення X, яке коливається від 0 до 1. Зобразуючи це значення на криву - або, іншими словами, використовуючи f (X) замість X - ви можете перекрутити розподіл будь-яким способом, який вам подобається .

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

Наприклад, перша крива трохи нагадує квадратний корінь, а друга - як квадрат. Третій трохи схожий на куб, тільки перекладений. Якщо ви вважаєте квадратний корінь занадто повільним, перша крива також виглядає як f (X) = 1- (1-X) ^ 2 - інверсія квадрата. Або гіпербола: f (X) = 2X / (1 + X).

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

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


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

5
FYI: Вони називаються квантильними функціями: en.wikipedia.org/wiki/Quantile_function
Neil G

8

Більш тривале пояснення:

Якщо у вас є бажаний розподіл ймовірностей, такий як градієнт @didito, про який ви просили, ви можете описати це як функція. Скажімо, ви хочете трикутний розподіл, де ймовірність при 0 дорівнює 0,0, і ви хочете вибрати випадкове число від 0 до 1. Ми можемо записати його як y = x.

Наступний крок - обчислення інтегралу цієї функції. У цьому випадку це x=1x2 . Оцінено від 0 до 1, це ½. Це має сенс - це трикутник з основою 1 і висотою 1, тому його площа дорівнює 1/2.

Потім ви вибираєте випадкову точку рівномірно від 0 до області (½ у нашому прикладі). Назвемо це z. (Ми збираємо рівномірно із кумулятивного розподілу .)

Наступний крок - повернутися назад, щоб знайти, яке значення x (ми будемо називати його x̂) відповідає площі z. Ми шукаємо x=1x2 , що оцінюється від 0 до x̂, дорівнює z. Коли ви вирішите за1x̂2=z, ви отримаєтеx̂=2z .

У цьому прикладі ви вибираєте z від 0 до ½ і тоді бажане випадкове число дорівнює 2z . Спрощено, ви можете записати його якrand(0,1) - саме те, що рекомендував електронний бізнес.


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

Це круто. Я завжди заробляв sqrt(random())усе своє життя, але прийшов до цього емпірично. Намагаючись прив’язати до кривої випадкове число, і це спрацювало. Тепер, коли я трохи більше кваліфікований математик, знаючи, чому це працює, дуже цінно!
Gustavo Maciel

5

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

Зробіть х на основі чогось на кшталт 1- (rnd ^ значення) (якщо припустити, що rnd становить від 0 до 1), і ви отримаєте кілька різних типів поведінки перекосу зліва направо на основі того, що ви використовуєте. Чим більше значення, ви отримаєте більш косий розподіл

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

EDIT

Для щось подібне до системи частинок, де час процесора на частину дуже важливий, використання Math.Pow (або мовного еквівалента) безпосередньо може призвести до зниження продуктивності. Якщо потрібна більша продуктивність, а значення не змінюється під час виконання, розгляньте можливість переключення на еквівалентну функцію, таку як x * x замість x ^ 2.

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


1
Замість використання графічної програми ви можете просто побудувати розподіл Beta, оскільки це особливий випадок. Для даної valueце Beta (значення, 1).
Ніл Г

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

@Neil G дякую за пораду з "бета-розподілом" - це звучить цікаво і корисно ... я проведу кілька досліджень на цю тему
didito

3

Термін, який ви шукаєте, - Weighted Random Numbersбільшість алгоритмів, які я бачив, використовують тригені функції, але, думаю, я зрозумів спосіб, який буде ефективним:

Створіть таблицю / масив / список (що завгодно), що містить значення множника для випадкової функції. Заповніть його вручну або програмно ...

randMulti= {.1,.1,.1,.1,.1,.1,.2,.2,.3,.3,.9,1,1,1,} 

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

weightedRandom = math.random()*randMulti[Math.random(randMulti.length)]*maxValue

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


2
Якщо ви можете пожертвувати пам'яттю, таблиця зі 100 попередньо обчисленими значеннями була б швидшою (і трохи точнішою). Сумніваюсь, користувач зміг би розрізняти повну та попередньо обчислену версії.
Даніель Блезек

@Daniel це було б швидше, але зі 100 випадковими значеннями досить легко побачити повторювані зразки.
AttackingHobo

Тільки тому, що, мабуть, є повторювана закономірність, це не означає, що вона не є випадковою. Суть випадковості полягає в її непередбачуваності, що буквально означає, що стільки, скільки не можна передбачити, що не буде закономірності, так і не можна передбачити, що вона може бути (принаймні на короткий час). Вам доведеться провести тестування, але якщо ви знайдете зразки з декількома тестами з використанням різних насінин, можливо, вам потрібно буде переглянути алгоритм генерації псевдовипадкових чисел.
Рендольф Річардсон

@AttackingHobo thx за цей трюк. Мені подобається використання LUT. а формулу зрозуміти досить просто. я не думав про це таким чином раніше. не бачачи деревини для дерев ... :) Також я думаю, що слід уникати повторних моделей, але, мабуть, не визнавали б у цьому випадку. все-таки попередньо обчисливши всі значення, зашкодить візуальному досвіду. у будь-якому випадку, THX, щоб нагадати мені, що це фактор, який слід враховувати на тему випадковості ...
didito

також дякуємо, що вивели термін зваженого "Випадкові номери"!
дидіто

2

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

[position] = sqrt(rand(0, 1))

Це дасть розподіл у полі одного виміру, [0, 1]де ймовірність позиції еквівалентна цій позиції, тобто "трикутний розподіл".

Чергове покоління без прямокутних:

[position] = 1-abs(rand(0, 1)-rand(0, 1))

Квадратний корінь в оптимальній реалізації - це лише кілька команд множення та суми без гілок. (Див .: http://en.wikipedia.org/wiki/Fast_inverse_square_root ). Яка з цих двох функцій швидша, може змінюватися залежно від платформи та випадкового генератора. Наприклад, на платформі x86 знадобиться лише кілька непередбачуваних гілок у випадковому генераторі, щоб зробити другий метод повільніше.


Імовірність позиції не дорівнює позиції (це математично неможливо - тривіально, домен і діапазон функції включає в себе і 0,50, і 0,51), і не є трикутним розподілом. ( en.wikipedia.org/wiki/Triangular_distribution )

1
Незважаючи на те, що sqrt дає цікаві зразки, системам частинок, як правило, потрібно бути дуже легким процесором на частинку, тому я рекомендую уникати квадратних коренів (які обчислювально повільні), де це можливо. Іноді ви можете піти просто заздалегідь обчисливши їх, але це може змусити ваші частинки з часом помітні малюнки.
Лунін

1
@Joe Wreschnig, чи ти сам читав цю статтю у Вікіпедії, вкладай a = 0, b = 1, c = 1 у формулу покоління, і ти отримуєш формулу в моєму дописі.
aaaaaaaaaaaa

3
@Lunin, чому ти скаржишся на квадратний корінь, коли у тебе у відповіді є показник?
aaaaaaaaaaaa

1
@Lunin: Теорія ефективності - це досить занедбане поле, багато того, що люди думають, що вони знають, де приблизно правильно 30 років тому, коли АЛУ були великими дорогими та повільними. Навіть експонентна функція, яку ви нещодавно виявили як досить повільну арифметичну функцію, рідко є грішником з високою значимістю. Розгалуження (використання оператора if) та пропуски кешу (читання фрагмента даних, який наразі не знаходиться в кеші), як правило, коштують найбільшої продуктивності.
aaaaaaaaaaaa

1

Просто використовуйте бета-версію:

  • Бета (1,1) є плоскою
  • Бета (1,2) - лінійний градієнт
  • Бета (1,3) є квадратичною

тощо.

Два параметри форми не повинні бути цілими числами.


THX за вашу допомогу. як було сказано вище, бета-розподіл звучить цікаво. але я ще не можу зрозуміти зміст сторінки вікіпедії. або формула / код. ну, я також не маю часу зараз досліджувати далі: я бачу, що в boost є код для бета-розподілів, але це було б надмірним. ну, мабуть, мені потрібно спершу пройти його, а потім написати власну спрощену версію.
didito

1
@didito: Це не так складно. Ви просто заміните свій uniform_generator()дзвінок на gsl_ran_beta(rng, a, b). Дивіться тут: gnu.org/software/gsl/manual/html_node/…
Neil G

THX для підказки. я не використовую GSL (насправді раніше про це не чули), але добре дзвоніть. я перевірю джерело!
didito

@didito: У такому випадку я б пішов з рішенням Луніна. Удачі.
Ніл Г

0

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

Або, ще простіше, де X є результатом ГСЧ, по- перше double y = double(1/x);, x = y*[maximum return value of rng];. Це призведе до ваги чисел в експоненціальному відношенні до нижчих чисел.

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

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

Ви можете знайти всілякі дослідження цього щодо кривих дзвіночків з кістки. Насправді Anydice.com - це хороший сайт, який генерує графіки для різних методів прокатки кісток. Незважаючи на те, що ви використовуєте RNG, приміщення є таким же, як і результати. Тож це гарне місце для перегляду розподілу, перш ніж його навіть кодувати.

* Крім того, ви можете "скласти" розподіл результатів по осі, взявши вісь і віднімаючи усереднений результат, потім додаючи вісь. Наприклад, ви хочете, щоб більш низькі значення були більш поширеними, і дозволяємо сказати, що ви хочете, щоб 15 було вашим мінімальним значенням, а 35 - вашим максимальним значенням, діапазон 20. Таким чином, ви генеруєте і середнє значення двох значень з діапазоном 20 ( вдвічі більше потрібного діапазону), що дасть дзвінку з центром на 20 (віднімаємо п’ять в кінці, щоб зрушити діапазон з 20 на 40, на 15 до 35). Візьміть утворені числа X і Y.

Кінцевий номер,

z =(x+y)/2;// average them
If (z<20){z = (20-z)+20;}// fold if below axis
return z-5;// return value adjusted to desired range

Якщо нуль - це ваш мінімум, ще краще, зробіть це замість цього,

z= (x+y)/2;
If (z<20){z = 20-z;}
else {z = z - 20;}
return z;
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.