Изменить случайные функции распределения :: Уменьшить вероятность получения нескольких похожих значений в последовательности.


8

Я хочу создать последовательность чисел для процедурно генерирующих планет в галактическом секторе. Каждая планета должна располагаться случайным образом, однако очень маловероятно, что две планеты находятся непосредственно рядом друг с другом. Как я могу этого достичь?

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

Gif показывает идею изменения кривой вероятности в зависимости от уже сгенерированных значений.


2
Простое добавление минимального расстояния позволит убедиться, что планета не находится рядом с другой. Я полагаю, что это просто, так что вы могли бы уточнить еще?
Madmenyo

@MennoGouw Да, это решило бы это для этого конкретного случая, хотя я хочу улучшить мое понимание вероятности, поэтому я ищу "более мягкое" решение без жестких ограничений / отбрасывания сгенерированных чисел.
API-Beast

Уточнить «более мягкий» раствор. Все дело в установлении правил. Когда вам нужны определенные правила для процедурной генерации, вам нужно добавить эти правила. Если у вас есть особые случаи, вы устанавливаете больше или другие правила для них.
Madmenyo

Я не уверен, почему вы не просто используете генератор, который имеет отличную репутацию по поводу его распространения? (Я думаю, что твистер Мерсенна не плохой.)
Vaillancourt

2
Согласен. Само случайное поколение не является проблемой. Это может даже сломать ваш генератор случайных чисел, сделав его предсказуемым. Генерация правил - это путь.
ashes999

Ответы:


8

Если вы знаете, какое распределение вы хотите, вы можете использовать выборку отклонения .

Самый простой способ: на графике выше выбирайте точки случайным образом, пока не найдете точку ниже кривой. Тогда просто используйте x-координату.

Для фактического распространения существуют различные вероятные подходы. Например, для номера планеты iв местоположении pи некоторого параметра прочности k(например 0.5), определите функцию f_i(x)=abs(p-x)^k, затем используйте функцию распределения g(x)=f_1(x)*f_2(x)*...*f_n(x).

На практике вычислить и сохранить результаты g(x)для array t( t[x]=g(x)); помните также самое высокое значение h. Выберите случайное положение xв t, выбрать случайное значение yмежду 0и hповторите if y>t[x]; в противном случае возвращается значение x.


Не могли бы вы немного углубиться в определение функции распределения? Остальное должно быть довольно ясно.
API-Beast

Например, если текущие планеты находятся в положениях 0,1, 0,3 и 0,8, г (х) = (абс (х-0,1) * абс (х-0,3) * абс (х-0,8)) ^ 0,5, где "^" означает возведение в степень. (Это немного отличается от предыдущей формулы, но эквивалентно.) Эта функция распределения выглядит примерно как gif в вашем вопросе и не основана ни на чем конкретно. (Строка запроса для WolframAlpha: «график от 0 до 1 (абс (х-0,1) * абс (х-0,3) * абс (х-0,8)) ^ 0,5»)
ярр

Вау, эта функция чертовски крута. Не знал, что такая функция на самом деле так проста :) Ссылка для ленивых: bit.ly/1pWOZMJ
API-Beast

1

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

В любом случае, как вы понимаете, функция распределения меняется после каждого сгенерированного числа и имеет память (то есть: она не является марковской ), и любой из этих методов может оказаться непрактичным, когда «память» (число ранее нарисованных чисел) получает очень большой.

  1. Просто:
    генерируйте случайное число из плоского распределения, сравните с ранее нарисованными числами, повторите, если «слишком близко»

  2. Этот ответ больше похож на вашу фигуру (при условии, что мы хотим извлечь из 0..1):

    • создайте новый упорядоченный список, вставьте 0 и 1
    • генерировать случайное число из плоской функции распределения: N_0
      • добавить этот номер в список
    • при следующем вызове нарисуйте еще один номер N_1,
    • если N_1> N_0
      • Нарисуйте новое гауссово случайное число со средним значением = 1 и стандартным отклонением o того, что вы хотите, меньшее число (по сравнению с 1-N_1) будет держать случайные числа дальше на расстоянии друг от друга. Это не гарантирует минимальное расстояние между розыгрышами, но опять же ваша фигура тоже не кажется.
    • противоположный случай N_1 <N_0 обрабатывается аналогично
    • на последующих розыгрышах продолжайте генерировать случайное число (N_i) из плоского распределения
    • Пройдите по списку, чтобы увидеть, между какими двумя ранее нарисованными числами новый номер лежит между (N_-, N_ +)
    • создать новое гауссово случайное число со средним (N_- + N _ +) / 2
    • добавьте плоский номер распределения (N_i) в свой список (упорядоченный список)

Контейнеры конечных точек - это особый случай, но он должен быть достаточно простым, чтобы вы могли понять, как с ними работать.


0

Подумайте о разнице между 1 кубиком и 3 кубиками . 1 кубик дает вам равную вероятность для всех значений, в то время как 3 кубика будут иметь более высокую вероятность для значений ближе к середине.

Чем больше «кубиков» в вашем уравнении, тем больше у вас шансов получить что-то к центру. Итак, давайте определим функцию, которая может обрабатывать любое число равномерно :

// Takes a random number between floor and ceil
// pow defines how strongly these results should gravitate towards the middle
// We also define a function TrueRand(floor, ceil) elsewhere where you should substitute your own random function
int CenterRandom(int floor, int ceil, int pow = 3)
{
    if(ceil == floor)
        return ceil; // don't care to compare

    int total = 0;
    for(int x = 0; x < pow; x++)
    {
       total += TrueRand(floor, ceil);
    }
    return total / pow;
}

Теперь мы можем определить пример функции, чтобы использовать это:

// Distribues a number of points between floor and ceil
// We assume a function PlotPoint(int) exists to aid in creating the planet, etc...
void DistributePoints(int floor, int ceil, int numPoints)
{
    // Could easily output this in the function parameters, but language wasn't specified
    int[numPoints] breaks;
    int numBreaks = 0;

    // Special case for first pair
    breaks[0] = CenterRandom(floor, ceil);
    numBreaks++;

    for(int x = 0; x < numPoints - 1; x++)
    {
        // Generate a random number linearly, this will be used for picking
        // This way we have a greater chance of choosing a random value between larger pairs
        int picker = TrueRandom(floor, ceil);

        // Now we first find the pair of points that our picker exists on
        // For simplicity, we handle the first and last pair separately

        if(picker >= floor && picker < breaks[0])
        {
            breaks[x] = CenterRandom(floor, breaks[0] - 1);
        }
        for(int i = 0; i < numBreaks; i++)
        {
            if(picker > breaks[i] && picker < breaks[i+1])
            {
                breaks[x] = CenterRandom(breaks[i] + 1, breaks[i+1] - 1);
            }
        }
        if(picker > breaks[numBreaks] && picker <= ceil)
        {
            breaks[x] = CenterRandom(breaks[numBreaks] + 1, ceil);
        }

        PlotPoint(breaks[x]); // Plot the point
    }
}

Теперь первое, что следует отметить, - этот код действительно не проверяет, соответствует ли сборщик одному из пунктов. Если это произойдет, то просто не будет генерировать точку, возможно, то, что вам может понравиться.

Чтобы объяснить, что здесь происходит, это то, что CenterRandom создает своего рода кривую колокола. Эта функция разбивает плоскость на несколько кривых колокольчиков, по одной на пару существующих точек. Сборщик говорит нам, из какой кривой колокола генерировать. Поскольку мы выбираем линейно, мы можем гарантировать, что пары с большими промежутками между ними будут выбираться чаще, но мы все равно оставляем их совершенно случайными.

Надеюсь, что это указывает вам в правильном направлении.


0

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

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

В двух измерениях я видел несколько подходов, которые генерируют «синий шум», но я не знаю, как создать интервал с произвольным распределением. В этой статье описывается стандартный подход «попробуйте разместить его и отклонить, если он слишком близок», но вы можете сгенерировать их все сразу, с помощью «более мягкого» решения, разместив все свои точки, а затем с помощью Lloyd Relaxation переместить все планеты к более желательные позиции. Это раздвинет слишком близкие планеты дальше друг от друга. Рекурсивные плитки Ван - еще один подход, который может оказаться полезным. Эта бумагарасширяет задачу создания планет с одной плотностью и некоторых других объектов, таких как астероиды с другой плотностью. Вы также можете генерировать синий шум, используя ряды Фурье; Я не уверен. Подход ряда Фурье также позволил бы вам использовать произвольные распределения вместо только синего шума.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.