Para esta publicación, y = f (t) donde t es el parámetro que usted varía (tiempo / progreso) e y es la distancia al objetivo. Así que hablaré en términos de puntos en diagramas 2D donde el eje horizontal es tiempo / progreso y el vertical es distancia.
Creo que puedes hacer una curva de Bezier cúbica con el primer punto en (0, 1) y el cuarto (último) punto en (1, 0). Los dos puntos medios se pueden colocar aleatoriamente (x = rand, y = rand) dentro de este rectángulo 1 por 1. No puedo verificar esto analíticamente, pero solo jugando con un applet (sí, adelante y riéndose) parece que la curva de Bezier nunca disminuirá con tal restricción.
Esta será su función elemental b (p1, p2) que proporciona una ruta no decreciente desde el punto p1 hasta el punto p2.
Ahora puede generar ab (p (1) = (0, 1), p (n) = (1, 0)) y elegir un número de p (i) a lo largo de esta curva de manera que 1
Esencialmente, está generando una ruta "general", y luego la divide en segmentos y regenera cada segmento.
Como desea una función matemática: Suponga que el procedimiento anterior está empaquetado en una función y = f (t, s) que le da la distancia en t para la función de la semilla s. Necesitará:
- 4 números aleatorios para colocar los 2 puntos medios de la spline principal de Bezier (de (0, 1) a (1, 0))
- n-1 números para los límites de cada segmento si tiene n segmentos (el primer segmento siempre comienza en (0, 1), es decir, t = 0 y el último termina en (1,0), es decir, t = 1)
- 1 número si desea aleatorizar el número de segmentos
- 4 números más para colocar los puntos medios de la spline del segmento en el que aterriza
Por lo tanto, cada semilla debe suministrar uno de los siguientes:
- 7 + n números reales entre 0 y 1 (si desea controlar el número de segmentos)
- 7 números reales y un número entero mayor que 1 (para un número aleatorio de segmentos)
Me imagino que puede lograr cualquiera de estos simplemente suministrando una serie de números como la semilla s. Alternativamente, podría hacer algo como suministrar un número s como semilla, y luego llamar al generador de números aleatorios incorporado con rand (s), rand (s + 1), rand (s + 2) y así sucesivamente (o inicializar con sy luego sigue llamando a rand.NextNumber).
Tenga en cuenta que aunque toda la función f (t, s) esté compuesta por muchos segmentos, solo está evaluando un segmento para cada t. Usted tendrá que calcular repetidamente los límites de los segmentos con este método, ya que tendrá que ordenarlos para asegurarse de que no hay dos segmentos se superponen. Probablemente pueda optimizar y deshacerse de este trabajo adicional y solo encontrar los puntos finales de un segmento para cada llamada, pero no es obvio para mí en este momento.
Además, las curvas de Bezier no son necesarias, cualquier spline que se comporte adecuadamente servirá.
Creé una implementación de muestra de Matlab.
La función Bezier (vectorizada):
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 función de Bezier compuesta descrita anteriormente (se dejó deliberadamente sin vectorizar para dejar en claro cuánta evaluación se necesita para cada llamada):
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
El script que traza la función para una semilla aleatoria (tenga en cuenta que este es el único lugar donde se llama una función aleatoria, las variables aleatorias a todos los demás códigos se propagan desde esta matriz aleatoria):
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');
Aquí hay una muestra de salida:
Parece cumplir con la mayoría de sus criterios. Sin embargo:
- Hay "rincones". Esto puede ser manejable usando curvas de Bezier más apropiadamente.
- "Obviamente" parece splines, aunque realmente no puedes adivinar lo que hará después de un período de tiempo no trivial a menos que conozcas la semilla.
- Muy raramente se desvía demasiado hacia la esquina (se puede arreglar jugando con la distribución del generador de semillas).
- La función de Bezier cúbico no puede alcanzar un área cerca de la esquina dadas estas restricciones.
f'(x)>0
, por lo que la integración normalizada del valor absoluto de cualquier función de ruido cumplirá todos sus requisitos. Lamentablemente, no conozco ninguna forma fácil de calcular eso, pero tal vez alguien más sí. :)