Per questo post, y = f (t) dove t è il parametro che si varia (tempo / avanzamento) e y è la distanza dall'obiettivo. Quindi parlerò in termini di punti su grafici 2D in cui l'asse orizzontale è tempo / progresso e la verticale è distanza.
Penso che puoi creare una curva cubica di Bezier con il primo punto in (0, 1) e il quarto (ultimo) punto in (1, 0). I due punti centrali possono essere posizionati casualmente (x = rand, y = rand) all'interno di questo rettangolo 1 per 1. Non riesco a verificarlo analiticamente, ma solo giocando con un'applet (sì, vai avanti e ridi) sembra che la curva di Bezier non diminuirà mai con un tale vincolo.
Questa sarà la tua funzione elementare b (p1, p2) che fornisce un percorso non decrescente dal punto p1 al punto p2.
Ora puoi generare ab (p (1) = (0, 1), p (n) = (1, 0)) e scegliere un numero di p (i) lungo questa curva in modo che 1
In sostanza, stai generando un percorso "generale", quindi lo spezzi in segmenti e rigeneri ogni segmento.
Poiché vuoi una funzione matematica: Supponi che la procedura sopra descritta sia impacchettata in una funzione y = f (t, s) che ti dà la distanza at per la funzione di seme s. Avrai bisogno:
- 4 numeri casuali per posizionare i 2 punti centrali della spline di Bezier principale (da (0, 1) a (1, 0))
- n-1 numeri per i limiti di ciascun segmento se si hanno n segmenti (il primo segmento inizia sempre da (0, 1) cioè t = 0 e l'ultimo termina da (1,0) cioè t = 1)
- 1 numero se si desidera randomizzare il numero di segmenti
- Altri 4 numeri per posizionare i punti medi della spline del segmento in cui atterra
Quindi ogni seme deve fornire uno dei seguenti:
- 7 + n numeri reali compresi tra 0 e 1 (se si desidera controllare il numero di segmenti)
- 7 numeri reali e un numero intero maggiore di 1 (per un numero casuale di segmenti)
Immagino che tu possa realizzare uno di questi semplicemente fornendo una serie di numeri come seme. In alternativa, potresti fare qualcosa come fornire un numero s come seme, quindi chiamare il generatore di numeri casuali incorporato con rand (s), rand (s + 1), rand (s + 2) e così via (o inizializzare con se continua a chiamare rand.NextNumber).
Si noti che anche se l'intera funzione f (t, s) è composta da molti segmenti, si sta valutando solo un segmento per ogni t. Si avrà bisogno di calcolare più volte i confini di segmenti con questo metodo, perché si dovrà ordinare loro di non fare sicuro due segmenti si sovrappongono. Probabilmente puoi ottimizzare e sbarazzarti di questo lavoro extra e trovare solo gli endpoint di un segmento per ogni chiamata, ma non è ovvio per me in questo momento.
Inoltre, le curve di Bezier non sono necessarie, qualsiasi spline che si comporti in modo adeguato farà.
Ho creato un'implementazione di esempio di Matlab.
La funzione Bezier (vettorializzata):
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
La funzione composta di Bezier sopra descritta (volutamente lasciata non vettorizzata per chiarire quanta valutazione è necessaria per ogni chiamata):
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
Lo script che traccia la funzione per un seme casuale (nota che questo è l'unico posto in cui viene chiamata una funzione casuale, le variabili casuali a tutti gli altri codici vengono propagate da questo unico array casuale):
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');
Ecco un esempio di output:
Sembra soddisfare la maggior parte dei tuoi criteri. Tuttavia:
- Ci sono "angoli". Ciò può essere reso possibile utilizzando le curve di Bezier in modo più appropriato.
- "Ovviamente" sembra spline, anche se non puoi davvero indovinare cosa farà dopo un periodo di tempo non banale se non conosci il seme.
- Molto raramente devia troppo verso l'angolo (può essere risolto giocando con la distribuzione del generatore di semi).
- La funzione cubica di Bezier non può raggiungere un'area vicino all'angolo dati questi vincoli.
f'(x)>0
, quindi l'integrazione normalizzata del valore assoluto di qualsiasi funzione di rumore soddisferà tutti i tuoi requisiti. Sfortunatamente non conosco alcun modo semplice per calcolarlo, ma forse qualcun altro lo fa. :)