Существует ли (семейство) монотонно неубывающая шумовая функция (и)?


10

Мне бы хотелось, чтобы функция оживляла объект, перемещающийся из точки A в точку B с течением времени, так что он достигает B в какое-то фиксированное время, но его положение в любое время беспорядочно изменяется беспрерывно, но никогда не движется назад. Объекты движутся по прямым линиям, поэтому мне нужно только одно измерение.

Математически это означает, что я ищу некоторый непрерывный f (x), x ∈ [0,1], такой что:

  • f (0) = 0
  • f (1) = 1
  • x <y → f (x) ≤ f (y)
  • В «большинстве» точек f (x + d) - f (x) не имеет очевидного отношения к d. (Функция не является равномерно возрастающей или иным образом предсказуемой; я думаю, это также эквивалентно тому, что степень производной не является константой.)

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

Чтобы избежать различных проблем с ошибками накопления, я бы предпочел, чтобы функция не требовала какого-либо внутреннего состояния. То есть я хочу, чтобы это была реальная функция, а не «функция» программирования.


3
Ваше третье и четвертое требование может быть аппроксимировано следующим f'(x)>0образом: нормализованная интеграция абсолютного значения любой шумовой функции будет соответствовать всем вашим требованиям. К сожалению, я не знаю ни одного простого способа рассчитать это, но, может быть, кто-то еще знает. :)
SkimFlux

Будет ли возмущать перпендикуляр вашей функции мгновенный уклон?
Каод

Когда вы говорите «Чтобы избежать различных проблем с ошибками накопления», я думал, что вы беспокоитесь о точности. Судя по вашим многочисленным комментариям, вы обеспокоены затратами на производительность слишком большого количества оценок. Вы должны точно указать, каким ограничениям производительности и памяти мы подчиняемся - требование в любом случае бесполезно, потому что можно создать функции с состоянием, которые не имеют ошибок накопления (что это значит, в любом случае?). Кроме того, ваш 4-й пункт неверен. Тривиальный пример: ни одна производная от e ^ x не является константой, поэтому это не эквивалентно тому, чтобы говорить это.
Superbest

Ответы:


4

Для этого поста y = f (t), где t - параметр, который вы меняете (время / прогресс), а y - расстояние до цели. Поэтому я буду говорить с точки зрения точек на двухмерных графиках, где горизонтальная ось - это время / прогресс, а вертикальная - это расстояние.

Я думаю, что вы можете сделать кубическую кривую Безье с первой точкой в ​​(0, 1) и четвертой (последней) точкой в ​​(1, 0). Две средние точки могут быть расположены случайным образом (x = rand, y = rand) в этом прямоугольнике 1 на 1. Я не могу проверить это аналитически, но, просто поиграв с апплетом (да, продолжайте смеяться), кажется, что кривая Безье никогда не уменьшится при таком ограничении.

Это будет ваша элементарная функция b (p1, p2), которая обеспечивает неубывающий путь от точки p1 к точке p2.

Теперь вы можете сгенерировать ab (p (1) = (0, 1), p (n) = (1, 0)) и выбрать число p (i) вдоль этой кривой так, чтобы 1

По сути, вы генерируете один «общий» путь, а затем разбиваете его на сегменты и восстанавливаете каждый сегмент.

Поскольку вам нужна математическая функция: предположим, что описанная выше процедура упакована в одну функцию y = f (t, s), которая дает вам расстояние в точке t для функции seed s. Тебе понадобится:

  • 4 случайных числа для размещения 2 средних точек основного сплайна Безье (от (0, 1) до (1, 0))
  • n-1 число для границ каждого сегмента, если у вас есть n сегментов (первый сегмент всегда начинается в (0, 1), т.е. t = 0, а последний заканчивается в (1,0), т.е. t = 1)
  • 1 число, если вы хотите рандомизировать количество сегментов
  • Еще 4 числа для размещения средних точек сплайна сегмента, в который вы попали

Таким образом, каждое семя должно обеспечивать одно из следующего:

  • 7 + n действительных чисел от 0 до 1 (если вы хотите контролировать количество сегментов)
  • 7 действительных чисел и одно целое число больше 1 (для случайного числа сегментов)

Я полагаю, что вы можете выполнить любой из них, просто указав в качестве начального числа массив чисел. В качестве альтернативы вы можете сделать что-то вроде предоставления одного числа s в качестве начального числа, а затем вызвать встроенный генератор случайных чисел с помощью rand (s), rand (s + 1), rand (s + 2) и т. Д. (Или инициализировать с помощью и затем продолжайте вызывать rand.NextNumber).

Обратите внимание, что, хотя вся функция f (t, s) состоит из множества сегментов, вы оцениваете только один сегмент для каждого t. Вам будет необходимо повторно вычислить границы сегментов с помощью этого метода, потому что вам придется сортировать их , чтобы убедиться , что никакие два сегмента перекрываются. Вероятно, вы можете оптимизировать и избавиться от этой дополнительной работы и найти только конечные точки одного сегмента для каждого вызова, но это не очевидно для меня сейчас.

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

Я создал образец реализации Matlab.

Функция Безье (векторизация):

function p = bezier(t, points)
% p = bezier(t, points) takes 4 2-dimensional points defined by 2-by-4 matrix
% points and gives the value of the Bezier curve between these points at t.
% 
% t can be a number or 1-by-n vector. p will be an n-by-2 matrix.
    coeffs = [
        (1-t').^3, ...
        3*(1-t').^2.*t', ...
        3*(1-t').*t'.^2, ...
        t'.^3
    ];

    p = coeffs * points;
end

Составная функция Безье, описанная выше (намеренно оставлена ​​безвекторной, чтобы прояснить, сколько оценки требуется для каждого вызова):

function p = bezier_compound(t, ends, s)
% p = bezier(t, points) takes 2 2-dimensional endpoints defined by a 2-by-2
% matrix ends and gives the value of a "compound" Bezier curve between
% these points at t.
% 
% t can be a number or 1-by-n vector. s must be a 1-by-7+m vector of random
% numbers from 0 to 1. p will be an n-by-2 matrix. 
    %% Generate a list of segment boundaries
    seg_bounds = [0, sort(s(9:end)), 1];

    %% Find which segment t falls on
    seg = find(seg_bounds(1:end-1)<=t, 1, 'last');

    %% Find the points that segment boundaries evaluate to
    points(1, :) = ends(1, :);
    points(2, :) = [s(1), s(2)];
    points(3, :) = [s(3), s(4)];
    points(4, :) = ends(2, :);

    p1 = bezier(seg_bounds(seg), points);
    p4 = bezier(seg_bounds(seg+1), points);

    %% Random middle points
    p2 = [s(5), s(6)] .* (p4-p1) + p1;
    p3 = [s(7), s(8)] .* (p4-p1) + p1;

    %% Gather together these points
    p_seg = [p1; p2; p3; p4];

    %% Find what part of this segment t falls on
    t_seg = (t-seg_bounds(seg))/(seg_bounds(seg+1)-seg_bounds(seg));

    %% Evaluate
    p = bezier(t_seg, p_seg);    
end

Скрипт, который строит функцию для случайного начального числа (обратите внимание, что это единственное место, где вызывается случайная функция, случайные переменные для всего остального кода распространяются из этого одного случайного массива):

clear
clc

% How many samples of the function to plot (higher = higher resolution)
points = 1000;

ends = [
    0, 0;
    1, 1;
    ];

% a row vector of 12 random points
r = rand(1, 12);

p = zeros(points, 2);

for i=0:points-1
    t = i/points;
    p(i+1, :) = bezier_compound(t, ends, r);
end

% We take a 1-p to invert along y-axis here because it was easier to
% implement a function for slowly moving away from a point towards another.
scatter(p(:, 1), 1-p(:, 2), '.');
xlabel('Time');
ylabel('Distance to target');

Вот пример вывода:

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

Кажется, соответствует большинству ваших критериев. Однако:

  • Есть "углы". Это может быть исправлено с помощью более подходящих кривых Безье.
  • Это «очевидно» выглядит как сплайны, хотя вы не можете точно догадаться, что он будет делать после нетривиального периода времени, если вы не знаете семя.
  • Он очень редко отклоняется слишком сильно к углу (это можно исправить, играя с распределением генератора семян).
  • Кубическая функция Безье не может достичь области вблизи угла с учетом этих ограничений.

1

Я предполагаю, что вместо смешивания нескольких преобразованных косинусов (как дают точечные произведения в перлин-шуме), вы можете смешать несколько монотонных функций, которые начинаются с f (0) = 0, например, f (x) = x или 2x, или x ^ 2 и т. д. На самом деле, поскольку ваш домен ограничен 0 => 1, вы также можете смешивать триггерные функции, которые соответствуют требованиям, в этом домене, например, cos (90 * x + 270). Чтобы нормализовать методы, заканчивающиеся на 1, вы можете просто разделить взвешенную сумму этих монотонных методов, начинающихся с f (0) = 0, на f (1). Нечто подобное должно быть довольно легко также инвертировать (что, как я понимаю, вы хотите из битов о реальных функциях без сохранения состояния по сравнению с функциями программирования).

Надеюсь это поможет.


1

Можно проанализировать эту грубую картину. введите описание изображения здесь Вы можете получить функцию, которая выполняет анимацию на лету, используя унифицированную функцию rand. Я знаю, что это не точная математическая формула, но на самом деле нет математической формулы для случайной функции, и даже если бы она была, вы бы много программировали, чтобы достичь этого. Учитывая, что вы не указали никаких условий сглаживания, профиль скорости является непрерывным $ C ^ 0 $ (но поскольку вы не имеете дело с роботами, не нужно беспокоиться о прерывистых профилях ускорения).


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

Хм, извините, я подумал, что шумовые функции также используют процедуру генератора случайных чисел и которые также зависят от дискретного набора направляющих / ключевых точек для получения формы (я видел, что упоминался шум Перлина ... что один работает через псевдослучайный генераторы чисел, которые довольно сложно интегрировать, следовательно, нет аналитического решения). Можно ли интегрировать шумовую функцию аналитически? Мне интересно, может ли один из них быть кандидатом на ссылку
teodron

Например, шум Перлина принимает начальное состояние из 255 8-битных чисел, но из этого он генерирует случайный шум на бесконечном расстоянии в трех измерениях; не совсем правильно описывать их как «ориентиры», математически они больше похожи на еще 256 параметров, которые вы не хотите предоставлять. Как вы говорите, это по сути не интегрируемо, но это чистая функция. Страница, на которую вы ссылаетесь, является плохим объяснением шума Перлина (на самом деле он не объясняет шум Перлина). Что касается того, возможно ли это для какой- то шумовой функции ... ну, вот в чем вопрос, не так ли?

1

Обычный способ генерирования возрастающей последовательности из N случайных чисел из [0,1] состоит в том, чтобы сгенерировать N случайных чисел в любом диапазоне, затем разделить их все на их общую сумму, а затем сложить их по одному за раз, чтобы получить последовательность.

Генерация последовательности 2, 2, 5, 8, 6.
Их сумма равна 23, поэтому наши числа для суммирования: 2/23, 2/23, 5/23, 8/23 и 6/23.
Наша последняя последовательность: 2/23, 4/23, 9/23, 17/23, 23/23

Это может быть расширено до 2D путем генерации этих значений для X и Y. Вы можете увеличить N, чтобы получить любую гранулярность, которую вы хотите.


В аналогичном ответе @ teodron вы привели проблемы эффективности с большими временными масштабами. Не зная реальной проблемы, с которой вы сталкиваетесь, я не могу сказать, обоснована ли эта проблема; но другой вариант будет генерировать для малого N и просто сгладить результат. В зависимости от приложения, это может дать лучшие результаты.

введите описание изображения здесь
N = 100, без сглаживания

введите описание изображения здесь
N = 15, со сглаживанием


Что бы вы ни делали для сглаживания, похоже, что результат даже не стал функцией (около x = 0,95); Я не уверен, является ли это артефактом вашей графической программы или ошибкой. Монотонность также, кажется, нарушается в районе 0,7. Во всяком случае, я знаком с «обычным способом» - я задаю этот вопрос, потому что я подозреваю, что обычный путь дерьмовый. В конце концов, до появления перлин-шума ни у кого не было проблем с гигантскими LUT ценностного шума, это был просто «обычный путь». Сегодня у нас есть способ, который значительно более гибок и эффективен.

3
Я согласен с BlueRaja: существуют известные, простые в реализации способы сглаживания без нарушения монотонности, независимо от примера. Например, скользящее среднее или рисование сплайнов. Тем не менее, проблема @JoeWreschnig не имеет значения. Правила и механика игры могут зависеть от объектов, которые никогда не отступают, чтобы функционировать - редко бывает хорошей идеей предположить, что тому, что спрашивает, действительно не нужно то, что, по его словам, ему нужно.
Superbest

1
@BlueRaja: Мои основные жалобы на кусочные подходы, подобные этому, описаны в моем ответе на теодрон. Речь идет не о поиске «самого жесткого и математически точного результата», а об открытии новых возможностей с помощью математического инструмента, ранее неизвестного нам. Опять же, рассмотрим аналогию между LUT гигантского значения шума и шумом Перлина. Не каждый вопрос на сайте нуждается в «достаточно хорошем» ответе, который мог бы получить любой наполовину умный старшекурсник CS между лекциями - иногда, давайте стрелять для того, чтобы сделать что-то оригинальное и профессиональное, хорошо?

1
Или мы могли бы просто позволить этому сайту погрязнуть в 90% элементарной путаницы с матрицами трансформации, 10% «помогите мне прекратить играть в игры!» Это создаст удивительный сайт вопросов и ответов, который понравится каждому профессионалу.

2
@Joe: Это, ну, неуместно. Вы попросили решение, соответствующее вашим критериям, я дал вам одно. То, что это просто, не делает его плохим.
BlueRaja - Дэнни Пфлюгофт

1

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

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

template<int N> struct Trajectory
{
    Trajectory(int seed = 0)
    {
        /* The behaviour can be tuned by changing 0.2 and 0.6 below. */
        if (seed)
            srand(seed);
        for (int i = 0; i < N; i++)
            m_params[i] = 0.2 + 0.6 * (double)(rand() % 4096) / 4096;
    }

    double Get(double t, int depth = N)
    {
        double min = 0.0, max = 1.0;
        for (int i = 0, dir = 0; i < N && i < depth; i++)
        {
            int j = (dir + 1 + i) % N;
            double mid = min + (max - min) * m_params[j];
            if (t < m_params[i])
            {
                dir += 1;
                t = t / m_params[i];
                max = mid;
            }
            else
            {
                dir ^= i;
                t = (t - m_params[i]) / (1.0 - m_params[i]);
                min = mid;
            }
        }
        t = (3.0 - 2.0 * t) * t * t; // Optional smoothing
        return min + (max - min) * t;
    }

    double m_params[N];
};

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

Это быстрый пример:

пять разных траекторий

Пример был получен с помощью следующего кода:

for (int run = 0; run < 5; run++)
{
    /* Create a new shuffled trajectory */
    Trajectory<12> traj;

    /* Print dots */
    for (double t = 0; t <= 1.0; t += 0.0001)
        printf("%g %g\n", t, traj.Get(t));
}

0

Мышление вслух и признание исчисления не моя сильная сторона ... разве это возможно? Чтобы избежать какой-либо очевидной закономерности, среднее значение шумовой функции по любому изменению x должно быть близко к нулю, и для обеспечения монотонности амплитуда шума по сравнению с этим изменением x должна быть меньше, чем изменение x, поскольку любая большая амплитуда может привести к более низкому значению в х 'относительно х. Но это будет означать, что когда вы уменьшаете dx до 0, такая функция также должна уменьшать dA (где A - амплитуда) до нуля, а это означает, что вы не получите никакого вклада от любой соответствующей функции шума.

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


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

Например, любая функция, которая замедляется по мере приближения x к 1, имеет эквивалентную «обращенную» функцию g(x) = 1 - f(1 - x), которая вместо этого ускоряется при

Конечно, функции существуют - вы можете нарисовать одну, как теодрон, - но являются ли они «шумовыми» функциями? Под шумом подразумевается непрерывная функция, основанная на псевдослучайном входе с неявной амплитудой относительно базовой линии. И если эта амплитуда слишком велика, вы не можете гарантировать, что разница между ступенями достаточно мала, чтобы поддерживать выходной сигнал монотонным. Но мне приходит в голову, что плотность шума и шаг интерполяции могут быть созданы в соответствии с вашими требованиями, о которых я собираюсь подумать немного больше.
Kylotan

Шум просто означает, что он «непредсказуем», он ничего не говорит о методах генерации (или даже, технически, непрерывности, хотя для анимации почти всегда требуется когерентный шум). Это правда, что фиксированные конечные точки несколько ограничивают возможную амплитуду этой функции, но не полностью. Другие шумовые функции имеют аналогичные свойства, например, Perlin (x) = 0 для любого целого числа x. Монотонность - более сильная гарантия, но я не думаю, что она настолько сильна, что делает ее невозможной.

@JoeWreschnig Я уверен, что вы знаете, что функция шума Перлина явно нарушает некоторые из ваших критериев. Во-первых, он проходит через 0 в узлах сетки, поэтому f (x + d) -f (x) является постоянным кратным d для некоторого определенного (равномерно расположенного) x. Кроме того, из-за этой хитрой уловки кэширования это будет повторяться для больших сеток. Для классического шума я думаю, что эталонная реализация должна иметь мозаичную решетку (x, y), идентичную мозаичной (x + 256, y + 256). Вы должны указать, является ли это приемлемым и в какой степени.
Superbest
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.