Für diesen Beitrag ist y = f (t), wobei t der Parameter ist, den Sie variieren (Zeit / Fortschritt) und y die Entfernung zum Ziel ist. Ich werde also in Bezug auf Punkte auf 2D-Plots sprechen, bei denen die horizontale Achse Zeit / Fortschritt und die vertikale Achse Entfernung ist.
Ich denke, Sie können eine kubische Bezier-Kurve mit dem ersten Punkt bei (0, 1) und dem vierten (letzten) Punkt bei (1, 0) erstellen. Die beiden Mittelpunkte können zufällig (x = Rand, y = Rand) innerhalb dieses 1-mal-1-Rechtecks platziert werden. Ich kann dies nicht analytisch überprüfen, aber wenn ich nur mit einem Applet herumspiele (ja, mach weiter und lache), scheint es, dass die Bezier-Kurve mit einer solchen Einschränkung niemals abnehmen wird.
Dies ist Ihre Elementarfunktion b (p1, p2), die einen nicht abnehmenden Pfad von Punkt p1 zu Punkt p2 bereitstellt.
Jetzt können Sie ab (p (1) = (0, 1), p (n) = (1, 0)) erzeugen und eine Anzahl von p (i) entlang dieser Kurve so auswählen, dass 1
Im Wesentlichen generieren Sie einen "allgemeinen" Pfad, teilen ihn dann in Segmente auf und generieren jedes Segment neu.
Da Sie eine mathematische Funktion wünschen: Angenommen, die obige Prozedur ist in eine Funktion y = f (t, s) gepackt, die Ihnen den Abstand bei t für die Funktion des Samens s gibt. Du wirst brauchen:
- 4 Zufallszahlen zum Platzieren der 2 Mittelpunkte des Haupt-Bezier-Splines (von (0, 1) bis (1, 0))
- n-1 Zahlen für die Grenzen jedes Segments, wenn Sie n Segmente haben (das erste Segment beginnt immer bei (0, 1), dh t = 0, und das letzte endet bei (1,0), dh t = 1)
- 1 Zahl, wenn Sie die Anzahl der Segmente zufällig bestimmen möchten
- 4 weitere Zahlen zum Platzieren der Mittelpunkte des Splines des Segments, auf dem Sie landen
Jeder Samen muss also eines der folgenden liefern:
- 7 + n reelle Zahlen zwischen 0 und 1 (wenn Sie die Anzahl der Segmente steuern möchten)
- 7 reelle Zahlen und eine ganze Zahl größer als 1 (für eine zufällige Anzahl von Segmenten)
Ich kann mir vorstellen, dass Sie beides erreichen können, indem Sie einfach eine Reihe von Zahlen als Startwerte angeben. Alternativ können Sie beispielsweise eine Zahl s als Startwert angeben und dann den integrierten Zufallszahlengenerator mit rand (s), rand (s + 1), rand (s + 2) usw. aufrufen (oder mit initialisieren) s und rufen Sie dann rand.NextNumber weiter auf.
Beachten Sie, dass Sie, obwohl die gesamte Funktion f (t, s) aus vielen Segmenten besteht, nur ein Segment für jedes t auswerten. Sie werden müssen wiederholt die Grenzen der Segmente mit dieser Methode berechnen, weil Sie sie sortieren müssen sicherstellen , dass keine zwei Segmente überlappen. Sie können diese zusätzliche Arbeit wahrscheinlich optimieren und loswerden und nur die Endpunkte eines Segments für jeden Anruf finden, aber es ist mir momentan nicht klar.
Bezier-Kurven sind ebenfalls nicht erforderlich, ein sich entsprechend verhaltender Spline reicht aus.
Ich habe eine Matlab-Beispielimplementierung erstellt.
Die Bezier-Funktion (vektorisiert):
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
Die oben beschriebene zusammengesetzte Bezier-Funktion (absichtlich unentdeckt gelassen, um zu verdeutlichen, wie viel Auswertung für jeden Aufruf erforderlich ist):
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
Das Skript, das die Funktion für einen zufälligen Startwert darstellt (beachten Sie, dass dies der einzige Ort ist, an dem eine zufällige Funktion aufgerufen wird. Die Zufallsvariablen werden von diesem einen zufälligen Array an alle anderen Codes weitergegeben):
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');
Hier ist eine Beispielausgabe:
Es scheint die meisten Ihrer Kriterien zu erfüllen. Jedoch:
- Es gibt "Ecken". Dies kann durch geeignete Verwendung von Bezier-Kurven möglich sein.
- Es sieht "offensichtlich" wie Splines aus, obwohl Sie nicht wirklich erraten können, was es nach einer nicht trivialen Zeit tun wird, wenn Sie den Samen nicht kennen.
- Es weicht sehr selten zu stark in Richtung Ecke ab (kann durch Spielen mit der Verteilung des Saatgutgenerators behoben werden).
- Die kubische Bezier-Funktion kann aufgrund dieser Einschränkungen keinen Bereich in der Nähe der Ecke erreichen.
f'(x)>0
, sodass die normalisierte Integration des Absolutwerts einer Rauschfunktion alle Ihre Anforderungen erfüllt. Leider kenne ich keine einfache Möglichkeit, das zu berechnen, aber vielleicht tut es jemand anderes. :)