この投稿では、y = f(t)です。ここで、tは変化するパラメーター(時間/進行状況)で、yはターゲットまでの距離です。したがって、横軸が時間/進行で、縦軸が距離である2Dプロット上の点で説明します。
最初の点が(0、1)で、4番目の(最後の)点が(1、0)の3次ベジェ曲線を作成できると思います。2つの中間点は、この1行1列の長方形内にランダムに配置できます(x = rand、y = rand)。これを分析的に検証することはできませんが、アプレットをいじってみただけで(そうですね、笑ってください)、そのような制約があってもベジェ曲線は決して減少しないようです。
これは、ポイントp1からポイントp2への減少しないパスを提供する基本関数b(p1、p2)です。
これで、ab(p(1)=(0、1)、p(n)=(1、0))を生成し、この曲線に沿って1(
基本的に、「一般的な」パスを1つ生成し、それをセグメントに分割して、各セグメントを再生成します。
数学関数が必要な場合:上記の手順が1つの関数y = f(t、s)にパッケージ化され、シードsの関数のtでの距離を与えると仮定します。必要になるだろう:
- メインのベジェスプラインの2つの中間点を配置するための4つの乱数((0、1)から(1、0)まで)
- n個のセグメントがある場合、各セグメントの境界のn-1の数値(最初のセグメントは常に(0、1)で始まります(つまり、t = 0、最後は(1,0)で終わります)、つまりt = 1)
- セグメントの数をランダム化する場合は1つの数値
- Tが着地するセグメントのスプラインの中間点を配置するための4つの数値
したがって、各シードは次のいずれかを提供する必要があります。
- 0と1の間の7 + n実数(セグメントの数を制御する場合)
- 7つの実数と1より大きい1つの整数(ランダムな数のセグメントの場合)
シードとして数値の配列を指定するだけで、これらのいずれかを達成できると思います。または、1つの数値sをシードとして指定し、組み込みの乱数ジェネレータをrand(s)、rand(s + 1)、rand(s + 2)などで呼び出す(または、 sし、rand.NextNumberを呼び出し続けます。
関数f(t、s)全体が多数のセグメントで構成されている場合でも、評価するのはtごとに1つのセグメントのみです。あなたはなりますが、必ずノー二つのセグメントがオーバーラップさせるためにそれらをソートする必要がありますので、繰り返しこの方法では、セグメントの境界を計算する必要があります。おそらく、この余分な作業を最適化して取り除くことができ、呼び出しごとに1つのセグメントのエンドポイントのみを見つけることができますが、今のところ、私にはわかりません。
また、ベジェ曲線は必要ありません。適切に動作するスプラインがあれば十分です。
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
ランダムシードの関数をプロットするスクリプト(これはランダム関数が呼び出される唯一の場所であり、他のすべてのコードへのランダム変数はこの1つのランダム配列から伝達されることに注意してください):
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');
次に出力例を示します。
ほとんどの基準を満たしているようです。しかしながら:
- 「コーナー」があります。これは、ベジエ曲線をより適切に使用することにより、順応性があります。
- それは「明らかに」スプラインのように見えますが、シードを知らない限り、重要な期間が経過した後の動作を実際に推測することはできません。
- 非常にまれにコーナーに偏りすぎます(シードジェネレーターのディストリビューションを試すことで修正できます)。
- 3次ベジェ関数は、これらの制約が与えられた場合、コーナー近くの領域に到達できません。
f'(x)>0
ため、ノイズ関数の絶対値の正規化された積分はすべての要件を満たします。残念ながら、私はそれを簡単に計算する方法を知りませんが、多分誰かがそうします。:)