Чи існує (сімейство) монотонно не спадаючих шумових функцій?


10

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

Математично це означає, що я шукаю деякий безперервний 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

Чи може турбувати перпендикуляр вашої функції миттєвий нахил?
kaoD

Коли ви говорили "Щоб уникнути різних проблем із помилками накопичення", я подумав, що ви переживаєте за точність. Здається, виходячи з ваших численних коментарів, ви стурбовані витратами на надмірно багато оцінок. Ви повинні точно вказати, яким обмеженням продуктивності та пам’яті ми підпадаємо - вимога все одно не є корисною, оскільки можна, здавалося б, побудувати функції зі станом, які не мають помилок накопичення (що це означає взагалі?). Також ваш 4-й пункт неправильний. Тривіальний приклад: жодна похідна e ^ x не є постійною, тому це не рівнозначно, як це сказати.
Супербест

Відповіді:


4

Для цієї посади y = f (t), де t - параметр, який ви змінюєте (час / хід), а y - відстань до цілі. Тож я буду говорити з точки зору на 2D-графіках, де горизонтальна вісь - час / хід, а вертикаль - відстань.

Я думаю, ви можете зробити кубічну криву Безьє з першою точкою (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 для функції насіння 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. Крім того, ви можете зробити щось на зразок подання одного числа s як насіння, а потім викликати вбудований генератор випадкових чисел з rand (s), rand (s + 1), rand (s + 2) тощо (або ініціалізувати з s, а потім продовжуйте дзвонити 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). Щоб нормалізувати свої методи до кінця, ви можете просто розділити зважену суму цих монотонних методів, починаючи з f (0) = 0 на f (1). Щось подібне також має бути досить легко інвертувати (що, на вашу думку, ви хочете з невеликих даних про реальні функції без громадянства порівняно з функціями програмування).

Сподіваюсь, це допомагає.


1

Можна проаналізувати цю грубу картину. введіть тут опис зображення Ви можете закінчити функцію, яка виконує вашу анімацію на льоту, використовуючи єдину функцію rand. Я знаю, що це не точна математична формула, але насправді немає математичної формули для випадкової функції, і навіть якби вона була, ви б кодували багато для цього. Зважаючи на те, що ви не вказали жодних умов плавності, профіль швидкості становить $ C ^ 0 $ безперервно (але, оскільки ви не маєте справу з роботами, не потрібно турбуватися про переривчасті профілі прискорення).


"фактично немає математичної формули для випадкової функції" Я хочу функцію шуму, а не випадкову функцію. Функції шуму добре підтверджені для існування. Окремі визначення, подібні до цього, також мають тенденцію створювати або неефективність (оцінювання стає O (шматки), що стає проблемою, коли у вас є тривалі шкали часу), нечисті функції (оцінюйте в O (1), але потрібно утримувати попереднє положення), або пере- обмежують можливі функції (наприклад, всі точки перегину знаходяться з фіксованими інтервалами).

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

Як приклад, шум Перліна приймає стан зародження 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 не має жодного значення. Правила гри та механіка можуть залежати від об'єктів, які ніколи не відступають у свою функцію - рідко є ідеєю припустити, що речі, які запитують, насправді не потребують того, що, на його думку, потрібно.
Супербест

1
@BlueRaja: Мої основні скарги на подібні підходи описані у моїй відповіді на теодрон. Йдеться не про те, щоб знайти "найжорсткіший і математично точний результат" - це відкрити нові можливості з невідомим нам раніше математичним інструментом. Знову ж таки, розглянемо аналогію між гігантськими значеннями LUT та шумом Перліна. Не на кожне запитання на веб-сайті потрібна відповідь на "манжетну" відповідь, коли будь-який на півдорозі інтелігентний піднижник CS міг би пробиватися між лекціями - іноді давайте зніматимемось, щоб зробити щось оригінальне та професійне, гаразд?

1
Або ми можемо просто продовжувати дозволяти цьому сайту проникати в 90% елементарній плутанині щодо матриць перетворення, 10% "допоможіть мені перестати грати в ігри!" Це зробить приголомшливий веб-сайт із питань і запитів, до якого любить завітати кожен професіонал.

2
@Joe: Це, ем, не вимагається. Ви попросили рішення, яке відповідатиме вашим критеріям, я дав вам одне. Просто тому, що це просто, це не робить це погано.
BlueRaja - Danny Pflughoeft

1

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

Ви можете бачити це як кускова реалізація, яка вимагає лише часу O (log (pieces)) . Масив параметрів використовується як для положення поділу та перемоги, так і для пройденої відстані при досягненні стрижня.

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, оскільки будь-яка більша амплітуда могла приводять до нижчого значення на x 'щодо x. Але це означає, що при зменшенні dx до 0 така функція також повинна зменшувати dA (де А амплітуда) до нуля, тобто ви не отримуєте жодного внеску від будь-якої сумісної функції шуму.

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


1
Я можу намалювати мільйони графіків таких функцій, і як каже SkimFlux, інтеграція функції шуму дає практично еквівалентну функцію, якщо ви її нормалізуєте. Отже, функції існують , це лише питання про те, чи вони можуть бути кодованими . Звідси питати тут замість math.se.

Наприклад, будь-яка функція, яка сповільнюється, коли х наближається до 1, має еквівалентну функцію "перевернутої" g(x) = 1 - f(1 - x), яка замість цього прискорюється, коли х відходить 0.

Звичайно, функції існують - ви можете намалювати таку, як теодрон, - але це "шумові" функції? Шум передбачає безперервну функцію, засновану на псевдовипадковому введенні з неявною амплітудою відносно базової лінії. І якщо ця амплітуда занадто велика, то ви не можете гарантувати, що різниця між кроками є достатньо низькою, щоб зберегти вихід монотонним. Але мені трапляється, що щільність шуму та крок інтерполяції можуть бути створені відповідно до ваших специфікацій, про які я збираюся трохи більше подумати.
Кілотан

Шум просто означає, що він "непередбачуваний", він нічого не говорить про методи генерації (або навіть, технічно, про безперервність, хоча для анімації майже завжди потрібно узгоджений шум). Це правда, що фіксовані кінцеві точки дещо обмежують можливу амплітуду цієї функції, але не повністю. Інші функції шуму мають подібні властивості, наприклад, Perlin (x) = 0 для будь-якого цілого числа x. Монотонність є більш сильною гарантією, ніж це, але я не думаю, що це настільки сильніше, що робить це неможливим.

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