Pour ce post, y = f (t) où t est le paramètre que vous modifiez (temps / progression) et y est la distance à atteindre. Je vais donc parler en termes de points sur des tracés 2D où l'axe horizontal est le temps / progrès et la verticale est la distance.
Je pense que vous pouvez faire une courbe de Bézier cubique avec le premier point à (0, 1) et le quatrième (dernier) point à (1, 0). Les deux points médians peuvent être placés au hasard (x = rand, y = rand) dans ce rectangle 1 par 1. Je ne peux pas vérifier cela analytiquement, mais juste en jouant avec une applet (ouais, allez-y et riez), il semble que la courbe de Bézier ne diminuera jamais avec une telle contrainte.
Ce sera votre fonction élémentaire b (p1, p2) qui fournit un chemin non décroissant du point p1 au point p2.
Vous pouvez maintenant générer ab (p (1) = (0, 1), p (n) = (1, 0)) et choisir un certain nombre de p (i) le long de cette courbe de telle sorte que 1
Essentiellement, vous générez un chemin "général", puis vous le divisez en segments et régénérez chaque segment.
Puisque vous voulez une fonction mathématique: Supposons que la procédure ci-dessus soit regroupée dans une fonction y = f (t, s) qui vous donne la distance à t pour la fonction de la graine s. Tu auras besoin de:
- 4 nombres aléatoires pour placer les 2 points médians de la spline de Bézier principale (de (0, 1) à (1, 0))
- n-1 nombres pour les limites de chaque segment si vous avez n segments (le premier segment commence toujours à (0, 1) c'est-à-dire t = 0 et le dernier se termine à (1,0) c'est-à-dire t = 1)
- 1 numéro si vous voulez randomiser le nombre de segments
- 4 numéros supplémentaires pour placer les points médians de la spline du segment auquel votre t atterrit
Ainsi, chaque graine doit fournir l'un des éléments suivants:
- 7 + n nombres réels compris entre 0 et 1 (si vous souhaitez contrôler le nombre de segments)
- 7 nombres réels et un entier supérieur à 1 (pour un nombre aléatoire de segments)
J'imagine que vous pouvez accomplir l'un ou l'autre de ces éléments en fournissant simplement un tableau de nombres comme graines de départ. Alternativement, vous pouvez faire quelque chose comme fournir un nombre s comme graine, puis appeler le générateur de nombres aléatoires intégré avec rand (s), rand (s + 1), rand (s + 2) et ainsi de suite (ou initialiser avec s, puis continuez d'appeler rand.NextNumber).
Notez que même si la fonction entière f (t, s) est composée de plusieurs segments, vous n'évaluez qu'un segment pour chaque t. Vous aurez besoin de calculer de façon répétée les limites des segments avec cette méthode, car vous devrez les trier pour se assurer qu'aucun deux segments se chevauchent. Vous pouvez probablement optimiser et vous débarrasser de ce travail supplémentaire et ne trouver que les points de terminaison d'un segment pour chaque appel, mais ce n'est pas évident pour moi en ce moment.
De plus, les courbes de Bézier ne sont pas nécessaires, toute spline ayant un comportement approprié fera l'affaire.
J'ai créé un exemple d'implémentation Matlab.
La fonction de Bézier (vectorisée):
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 fonction composée de Bézier décrite ci-dessus (laissée délibérément non vectorisée pour indiquer clairement combien d'évaluation est nécessaire pour chaque appel):
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
Le script qui trace la fonction pour une graine aléatoire (notez que c'est le seul endroit où une fonction aléatoire est appelée, les variables aléatoires vers tous les autres codes sont propagées à partir de ce seul tableau aléatoire):
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');
Voici un exemple de sortie:
Il semble répondre à la plupart de vos critères. Toutefois:
- Il y a des "coins". Cela peut être possible en utilisant les courbes de Bézier de manière plus appropriée.
- Il ressemble "évidemment" à des splines, bien que vous ne puissiez pas vraiment deviner ce qu'il fera après une période de temps non triviale à moins que vous ne connaissiez la graine.
- Il s'écarte très rarement trop vers le coin (peut être corrigé en jouant avec la distribution du générateur de graines).
- La fonction cubique de Bézier ne peut pas atteindre une zone proche du coin étant donné ces contraintes.
f'(x)>0
, de sorte que l'intégration normalisée de la valeur absolue de toute fonction de bruit satisfera toutes vos exigences. Malheureusement, je ne connais aucun moyen facile de calculer cela, mais peut-être que quelqu'un d'autre le sait. :)