Для цієї посади 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');
Ось зразок виводу:
Здається, вона відповідає більшості ваших критеріїв. Однак:
- Є "куточки". Це може бути полегшеним, якщо правильніше використовувати криві Безьє.
- Це "очевидно" схоже на сплайни, хоча ви не можете дійсно здогадатися, що це зробить через нетривіальний період часу, якщо ви не знаєте насіння.
- Це дуже рідко занадто сильно відхиляється в бік кута (можна зафіксувати, граючи з розподілом генератора насіння).
- Кубічна функція Безьє не може досягти області біля кута, враховуючи ці обмеження.
f'(x)>0
такий, що нормалізована інтеграція абсолютного значення будь-якої функції шуму виконає всі ваші вимоги. На жаль, я не знаю жодного легкого способу обчислити це, але, можливо, це робить хтось інший. :)