単調に減少しないノイズ関数(のファミリ)はありますか?


10

一定の時間にBに到達するように、ポイントAからポイントBに移動するオブジェクトをアニメーション化する関数が欲しいです。オブジェクトは直線に沿って移動するため、必要なのは1次元のみです。

数学的には、次のような連続的なf(x)、x∈[0,1]を探していることを意味します。

  • f(0)= 0
  • f(1)= 1
  • x <y→f(x)≤f(y)
  • 「ほとんど」の点では、f(x + d)-f(x)はdと明確な関係を持ちません。(この関数は一様に増加するわけではなく、そうでなければ予測可能でもありません。これは、微分の度合いが定数ではないと言うことにも相当すると思います。)

理想的には、実際にこれらの機能のファミリーを持ち、いくつかのシード状態を提供する方法が欲しいです。私の現在の使用には、少なくとも4ビットのシード(16の可能な関数)が必要ですが、それ以上のものを提供することはあまり自由ではありません。

累積エラーに関するさまざまな問題を回避するために、この関数いかなる種類の内部状態も必要としない方がいいと思います。つまり、プログラミングの「関数」ではなく、実際の関数にしたいのです。


3
3番目と4番目の要件はとして概算できるf'(x)>0ため、ノイズ関数の絶対値の正規化された積分はすべての要件を満たします。残念ながら、私はそれを簡単に計算する方法を知りませんが、多分誰かがそうします。:)
SkimFlux

関数の垂直勾配を乱すことは、瞬間的な傾斜の仕事ですか?
kaoD 2012年

「累積誤差の様々な問題を回避するために」と言うと、精度が気になると思いました。あなたの多くのコメントに基づいて、あなたは過度に多くの評価のパフォーマンスコストに関心があるようです。私たちが受けるパフォーマンスとメモリの制約を正確に述べる必要があります-累積エラーのない状態で関数を作成しているように見えるため、要件はとにかく役に立ちません(とにかく、どういう意味ですか)。また、4点目が間違っています。簡単な例:e ^ xの導関数は定数ではないので、それを言うことと同等ではありません。
スーパーベスト2012年

回答:


4

この投稿では、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次ベジェ関数は、これらの制約が与えられた場合、コーナー近くの領域に到達できません。

1

(perlinノイズのドット積のように)変換されたコサインの束をブレンドする代わりに、f(x)= x、または2xのようなf(0)= 0で始まるいくつかの単調関数をブレンドできると思いますまたはx ^ 2など。実際、ドメインは0 => 1に制限されているため、cos(90 * x + 270)のように、そのドメイン内の請求書に適合するトリガー関数をブレンドすることもできます。メソッドを1で終了するように正規化するには、f(0)= 0で始まるこれらの単調なメソッドの加重和をf(1)で除算するだけです。このようなものもかなり簡単に反転できるはずです(これは、ステートレスな実際の関数とプログラミング関数についてのビットからあなたが望むものです)。

お役に立てれば。


1

この粗雑な画像を分析ここに画像の説明を入力してください できます。均一なrand関数を利用することで、アニメーションをその場で実行する関数を作成できます。これは正確な数式ではないことを知っていますが、実際にはランダム関数の数式はありません。たとえあったとしても、これを実現するために多くのコーディングを行うことになります。滑らかさの条件を指定していないことを考えると、速度プロファイルは$ C ^ 0 $連続です(ただし、ロボットを扱っていないので、不連続な加速プロファイルを心配する必要はありません)。


「実際にはランダム関数の数式はありません」ランダム関数ではなくノイズ関数が必要です。ノイズ関数は存在することが十分に文書化されています。このような区分的定義は、非効率性(時間スケールが長い場合に問題になるO(pieces)になる)、不純な関数(O(1)で評価されるが以前の位置を維持する必要がある)、または過剰可能な機能を制限します(たとえば、すべての変曲点は一定の間隔にあります)。

申し訳ありませんが、申し訳ありませんが、ノイズ関数も乱数ジェネレーター手順を使用し、形状を生成するためにガイド/キーポイントの個別のセットに依存していると思いました(Perlin Noiseが言及されたのを見ました。統合するのが非常に難しいナンバジェネレータ、したがって分析的な解決策はありません)。ノイズ関数を分析的に統合できますか?これらの1つが候補リンクに
teodron、

例として、Perlinノイズは255の8ビット数のシード状態を取りますが、そこから3次元の無限距離にランダムノイズが生成されます。それらを「ガイドポイント」として説明するのは実際には正確ではありません。数学的には、提供し続けたくない別の256パラメータのようなものです。あなたが言うように、それは本質的に統合可能ではありませんが、それは純粋な関数です。リンク先のページは、Perlinノイズの悪い説明です(彼が説明しているPerlinノイズではありません)。それは可能ですかどうかについては、いくつかのノイズ関数の種類...よく、それが問題だ、そうではありませんか?

1

[0,1]からN個の乱数の増加シーケンスを生成する通常の方法は、任意の範囲のN個の乱数を生成し、それらをすべての合計で除算し、一度に1つずつ合計して、シーケンス。

シーケンス2、2、5、8、6を生成します。
それらの合計は23なので、合計する数値は2 / 23、2 / 23、5 / 23、8 / 23、および6/23です。
最終的なシーケンスは、2 / 23、4 / 23、9 / 23、17 / 23、23 / 23です。

XとYの両方のこれらの値を生成することにより、これを2Dに拡張できます。Nを増やして、必要な任意の粒度を取得できます。


@teodronの同様の答えで、大きな時間スケールでの効率の問題を挙げました。あなたが直面している実際の問題を知らなければ、その懸念が妥当であるかどうかはわかりません。しかし、別のオプションは、小さな N に対して生成し、結果を単純化することです。アプリケーションによっては、これにより実際により良い結果が得られる場合があります。

ここに画像の説明を入力してください
N = 100、平滑化なし

ここに画像の説明を入力してください
N = 15、平滑化あり


あなたが平滑化のために何をしているにせよ、結果は関数でさえないように思われます(x = 0.95あたり)。それがグラフ作成プログラムのアーティファクトなのか、間違いなのかはわかりません。単調性も0.7前後に違反しているようです。とにかく、私は「通常の方法」に精通しています-私は通常の方法がくだらないのではないかと思うので、この質問をしています。パーリンノイズ以前は、結局のところ、値ノイズの巨大なLUTには誰も問題がなく、それは単に「通常の方法」でした。今日、私たちはかなり柔軟効率的な方法を持っています。

3
BlueRajaに同意します。例に関係なく、単調性を損なうことなく平滑化を行うには、よく知られている簡単に実装できる方法があります。たとえば、移動平均や描画スプライン。ただし、@ JoeWreschnigの問題は無関係ではありません。ゲームのルールとメカニクスは、機能して後退しないオブジェクトに依存する可能性があります-質問者が彼が必要とするものを本当に必要としないものと仮定することはめったにありません。
スーパーベスト2012年

1
@BlueRaja:このような区分的アプローチに関する私の基本的な不満は、teodroneへの私の回答に記載されています。それは「最も厳密で数学的に正確な結果」を見つけることではなく、以前は知らなかった数学ツールで新しい可能性を切り開くことです。ここでも、巨大な値のノイズLUTとPerlinノイズの類似性について考えます。サイトのすべての質問に「十分な」回答が必要なわけではありません。中途半端なインテリジェントCSが講義の合間に出くわす可能性があります。時々、独創的でプロフェッショナルなことをするために撮影しましょう。

1
あるいは、変換マトリックスについての90%の初歩的な混乱、この10%が「ゲームのプレイをやめるのを手伝う!」それはすべての専門家が行きたいと思う素晴らしいQ&Aサイトを作るでしょう。

2
@ジョー:それは、ええと、求められていない。あなたはあなたの基準に合う解決策を求めました、私はあなたにそれを与えました。シンプルだからといって、悪くはありません。
BlueRaja-Danny Pflughoeft 2012年

1

この実装は、フラクタルノイズで見つかったオクターブの合計に触発されたもので、少し安いお尻があちこちでシャッフルされています。私はそれがかなり高速であり、約の精度を失うパラメータに格納されているよりも少ないオクターブを要求することによって調整できると信じています1/2^octave

O(log(pieces))時間のみを必要とする区分的な実装と見なすことができます。パラメータ配列は、分割統治のピボット位置と、ピボットに到達したときの移動距離の両方に使用されます。

template<int N> struct Trajectory
{
    Trajectory(int seed = 0)
    {
        /* The behaviour can be tuned by changing 0.2 and 0.6 below. */
        if (seed)
            srand(seed);
        for (int i = 0; i < N; i++)
            m_params[i] = 0.2 + 0.6 * (double)(rand() % 4096) / 4096;
    }

    double Get(double t, int depth = N)
    {
        double min = 0.0, max = 1.0;
        for (int i = 0, dir = 0; i < N && i < depth; i++)
        {
            int j = (dir + 1 + i) % N;
            double mid = min + (max - min) * m_params[j];
            if (t < m_params[i])
            {
                dir += 1;
                t = t / m_params[i];
                max = mid;
            }
            else
            {
                dir ^= i;
                t = (t - m_params[i]) / (1.0 - m_params[i]);
                min = mid;
            }
        }
        t = (3.0 - 2.0 * t) * t * t; // Optional smoothing
        return min + (max - min) * t;
    }

    double m_params[N];
};

浮動小数点除算を事前に計算することで、3倍の情報を格納することを犠牲にして、処理速度を上げることができます。

これは簡単な例です:

5つの異なる軌跡

この例は、次のコードで取得されました。

for (int run = 0; run < 5; run++)
{
    /* Create a new shuffled trajectory */
    Trajectory<12> traj;

    /* Print dots */
    for (double t = 0; t <= 1.0; t += 0.0001)
        printf("%g %g\n", t, traj.Get(t));
}

0

大声で考え、微積分を認めることは私の強みではありません...これはおそらく不可能ですか?明らかなパターンを回避するには、xの変化に対するノイズ関数の平均がゼロに近い必要があり、単調性を保証するには、xのその変化に対するノイズの振幅がxの変化よりも小さい必要があります。結果として、xに比べてx 'の値が低くなります。しかし、これは、dxを0に減少させると、そのような関数はdA(Aは振幅)も0に減少させる必要があることを意味します。

xが1に近づくにつれてノイズの寄与が徐々に減少する関数を定式化することは可能だと想像できますが、xが1に近づくにつれて減速する曲線関数が得られますが、これは私が望むものではありません。


1
私はそのような関数の何百万ものグラフを描くことができます、そしてSkimFluxが言うように、ノイズ関数の統合はあなたがそれを正規化するならば実質的に同等の関数を与えます。したがって、関数は存在します。それは、それらが実現可能なコード化が可能かどうかの問題です。したがって、math.seの代わりにここで尋ねます。

例えば、X 1は相当してい近づくにつれて減速任意の関数は、関数「逆」g(x) = 1 - f(1 - x)xは0を離れるよう代わり加速、

確かに、関数は存在します-teodronのように描画できますが、それらは「ノイズ」関数ですか?ノイズは、ベースラインを基準にした暗黙の振幅を持つ疑似ランダム入力に基づく連続関数を意味します。そして、その振幅が高すぎる場合、ステップ間の差が出力を単調に保つのに十分低いことを保証できません。しかし、ノイズの密度と補間ステップが仕様に合わせて作成される可能性があることは私には思い当たります。これについては、もう少し詳しく考えます。
カイロタン2012年

ノイズとは、単に「予測不可能」であることを意味し、生成方法については何も述べていません(または、技術的には連続性ですが、アニメーションの場合、ほとんど常に一貫したノイズが必要です)。固定端点がこの関数の可能な振幅をいくらか制限していますが、完全ではありません。他のノイズ関数も同様のプロパティを持ちます。たとえば、整数xに対してPerlin(x)= 0です。単調性はそれよりも強い保証ですが、それがそれほど強くて不可能にすることはないと思います。

@JoeWreschnig Perlinノイズ関数が明らかにいくつかの基準に違反していることをご存じでしょう。まず、グリッドノードで0を通過するため、f(x + d)-f(x)は、ある一定の(等間隔の)xに対して、dの定数倍になります。さらに、その巧妙なキャッシングトリックのために、大規模なグリッドでも繰り返されます。クラシックノイズの場合、リファレンス実装ではグリッドタイル(x、y)がタイル(x + 256、y + 256)と同一であると想定されています。これが許容できるかどうか、およびその程度を述べる必要があります。
スーパーベスト2012年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.