Monoton olarak azalmayan gürültü işlevleri (ailesi) var mı?


10

Zaman içinde A noktasından B noktasına hareket eden bir nesneyi, belirli bir zamanda B'ye ulaşacak şekilde canlandırmak için bir işlev istiyorum, ancak herhangi bir zamanda konumu sürekli olarak rastgele bozulur, ancak asla geriye gitmez. Nesneler düz çizgiler boyunca hareket eder, bu yüzden sadece bir boyuta ihtiyacım var.

Matematiksel olarak, bazı sürekli f (x), x ∈ [0,1] arıyorum, öyle ki:

  • f (0) = 0
  • f (1) = 1
  • x <y → f (x) ≤ f (y)
  • "En" noktalarında f (x + d) - f (x) 'nin d ile bariz bir ilişkisi yoktur. (İşlev eşit olarak artmıyor veya başka türlü öngörülebilir değil; bence bu da hiçbir türev derecesinin sabit olmadığını söylemeye eşdeğer.)

İdeal olarak, aslında bu işlevlerden bir aileye sahip olmak ve bazı tohum durumu sağlamak için bir yol istiyorum. Şu anki kullanımım için en az 4 bit tohuma (16 olası fonksiyon) ihtiyacım var, ama bu daha fazlasını sağlamaktan çekinmeyin.

Birikim hataları ile çeşitli sorunları önlemek için, ben fonksiyonunu tercih ediyorum değil iç devletin her türlü gerektirmez. Yani, bir programlama "işlevi" değil, gerçek bir işlev olmasını istiyorum.


3
Üçüncü ve dördüncü gereksinimleriniz yaklaşık olarak tahmin edilebilir f'(x)>0, böylece herhangi bir gürültü işlevinin mutlak değerinin normalleştirilmiş entegrasyonu tüm gereksinimlerinizi karşılayacaktır. Maalesef bunu hesaplamanın kolay bir yolunu bilmiyorum, ama belki başka biri yapıyor. :)
SkimFlux

Anlık eğim işlevinizin dikeyini bozar mıydı?
kaoD

"Biriktirme hatalarıyla ilgili çeşitli sorunları önlemek için" derken hassasiyet konusunda endişelendiğinizi düşündüm. Birçok yorumunuza dayanarak, çok fazla değerlendirmenin performans maliyetinden endişe duyuyorsunuz. Tam olarak hangi performans ve bellek kısıtlamalarına tabi olduğumuzu belirtmelisiniz - gereksinim zaten yararsızdır, çünkü görünüşte birikim hataları olmayan durumla işlevler oluşturabilir (Bu ne anlama geliyor?). Ayrıca 4. noktanız yanlış. Önemsiz bir örnek: e ^ x'in türevi sabit değildir, bu yüzden bunu söylemekle eşdeğer değildir.
Superbest

Yanıtlar:


4

Bu yazı için, y = f (t) burada t, değiştirdiğiniz parametredir (zaman / ilerleme) ve y, hedefe olan mesafedir. Bu yüzden, yatay eksenin zaman / ilerleme ve dikey mesafenin mesafe olduğu 2D çizimlerdeki noktalar açısından konuşacağım.

Sanırım ilk noktası (0, 1) ve dördüncü (son) noktası (1, 0) olan bir kübik Bezier eğrisi oluşturabilirsiniz. İki orta nokta bu 1'e 1 dikdörtgen içine rastgele yerleştirilebilir (x = rand, y = rand). Bunu analitik olarak doğrulayamıyorum, ama sadece bir uygulama ile oynamaktan (evet, devam edin ve gülün) Bezier eğrisinin böyle bir kısıtlamayla asla azalmayacağı anlaşılıyor.

Bu, p1 noktasından p2 noktasına azalmayan bir yol sağlayan temel işleviniz b (p1, p2) olacaktır.

Şimdi ab (p (1) = (0, 1), p (n) = (1, 0)) üretebilir ve bu eğri boyunca bir dizi p (i) 'yi seçebilirsiniz, böylece 1

Esasen, bir "genel" yol üretiyorsunuz ve sonra bunu parçalara ayırıyorsunuz ve her segmenti yeniden üretiyorsunuz.

Matematiksel bir işlev istediğiniz için: Yukarıdaki yordamın tohum = işlevi için t'deki mesafeyi veren y = f (t, s) bir işlevde paketlendiğini varsayalım. İhtiyacın olacak:

  • Ana Bezier spline'ın 2 orta noktasını yerleştirmek için 4 rastgele sayı ((0, 1) ila (1, 0) arasında)
  • n segmentiniz varsa her segmentin sınırları için n-1 sayıları (ilk segment her zaman (0, 1) ile başlar, yani t = 0 ve son uçlar (1,0) ile t = 1'de başlar)
  • Segment sayısını rasgele seçmek istiyorsanız 1 sayı
  • Arazinizin bulunduğu segmentin spline orta noktalarını yerleştirmek için 4 numara daha

Bu nedenle, her tohum aşağıdakilerden birini sağlamalıdır:

  • 0 ile 1 arasında 7 + n gerçek sayı (segment sayısını kontrol etmek istiyorsanız)
  • 7 gerçek sayı ve 1'den büyük bir tam sayı (rastgele sayıda segment için)

Bunların ikisini de tohum olarak bir dizi sayı sağlayarak başarabileceğinizi düşünüyorum. Alternatif olarak, tohum olarak bir sayı s sağlayın ve sonra rand (s), rand (s + 1), rand (s + 2) vb. İle yerleşik rasgele sayı üretecini çağırabilirsiniz (veya s ve sonra rand.NextNumber aramaya devam edin).

F (t, s) işlevinin tamamı birçok bölümden oluşmasına rağmen, her bir t için yalnızca bir bölüm değerlendirdiğinizi unutmayın. Sen edecek Emin hiçbir iki segment üst üste olmak için bunları sıralamak zorunda kalacak, çünkü tekrar tekrar bu yöntemle segmentlerin sınırlarını hesaplamak gerekir. Muhtemelen bu ekstra çalışmayı optimize edebilir ve kurtulabilirsiniz ve her arama için sadece bir segmentin uç noktalarını bulabilirsiniz, ancak şu anda benim için açık değil.

Ayrıca, Bezier eğrileri gerekli değildir, herhangi uygun bir spline olacaktır.

Örnek bir Matlab uygulaması oluşturdum.

Bezier işlevi (vectorized):

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

Yukarıda açıklanan bileşik Bezier işlevi (her çağrı için ne kadar değerlendirmenin gerekli olduğunu netleştirmek için kasıtlı olarak vektörünü bırakmadan bırakılır):

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

Rastgele bir tohum için işlevi çizen komut dosyası (rastgele bir işlevin çağrıldığı tek yer olduğuna dikkat edin, diğer tüm koda rastgele değişkenler bu rastgele diziden yayılır):

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');

İşte bir örnek çıktı:

resim açıklamasını buraya girin

Ölçütlerinizin çoğunu karşılıyor gibi görünüyor. Ancak:

  • "Köşeler" var. Bu Bezier eğrileri daha uygun bir şekilde kullanılarak yapılabilir.
  • "Açıkça" spline benziyor, ancak tohum bilmediğiniz sürece önemsiz bir süre sonra ne yapacağını gerçekten tahmin edemezsiniz.
  • Çok nadiren köşeye çok fazla sapar (tohum jeneratörünün dağılımı ile oynayarak sabitlenebilir).
  • Kübik Bezier işlevi, bu kısıtlamalar göz önüne alındığında köşeye yakın bir alana ulaşamaz.

1

Sanırım bir sürü dönüştürülmüş kosinüsleri karıştırmak yerine (perlin gürültüsünde nokta ürünler gibi, f (0) = 0 ile başlayan, f (x) = x veya 2x gibi birkaç monotonik işlevi karıştırabilirsiniz. veya x ^ 2, vb. Yöntemlerinizi 1'de bitecek şekilde normalleştirmek için, bu monotonik yöntemlerin ağırlıklı toplamını f (0) = 0'dan f (1) ile bölebilirsiniz. Bunun gibi bir şeyi de tersine çevirmek oldukça kolay olmalıdır (ki bu sizi programlama işlevlerine karşı vatansız gerçek işlevler hakkında biraz toplamak istiyorum).

Bu yardımcı olur umarım.


1

Bu ham resmi analiz edebilirsiniz Tek resim açıklamasını buraya girin bir rand fonksiyonunu kullanarak animasyonunuzu anında gerçekleştiren bir fonksiyonla sonuçlanabilirsiniz. Bunun tam bir matematiksel formül olmadığını biliyorum, ama aslında rastgele bir işlev için matematiksel bir formül yok ve bir tane olsa bile, bunu başarmak için çok kod yazardınız. Herhangi bir pürüzsüzlük koşulu belirtmediğiniz düşünüldüğünde, hız profili $ C ^ 0 $ süreklidir (ancak robotlarla uğraşmadığınız için süreksiz ivme profilleri hakkında endişelenmenize gerek yoktur).


"Rastgele bir işlev için aslında matematiksel bir formül yoktur" Rastgele bir işlev değil, bir gürültü işlevi istiyorum. Gürültü işlevlerinin varlığı iyi belgelenmiştir. Bunun gibi parçalı tanımlar aynı zamanda verimsizlik (değerlendirme, uzun zaman ölçekleriniz olduğunda sorun haline gelen O (parçalar) olur), saf olmayan fonksiyonlar (O (1) olarak değerlendirme yapar, ancak önceki pozisyonda kalması gerekir) ya da olası işlevleri kısıtlayın (örn. tüm bükülme noktaları sabit aralıklardadır).

Hmm, üzgünüm, gürültü fonksiyonlarının da rastgele bir sayı üreteci prosedürü kullandığını ve aynı zamanda bir şekil vermek için ayrı bir kılavuz / anahtar nokta setine bağlı olduğunu düşündüm (Perlin Gürültüsünden bahsedildiğini gördüm .. biri sahte rasgele çalışıyor) entegrasyonu oldukça zor olan sayı üreteçleri, dolayısıyla analitik çözüm yok). Bir gürültü fonksiyonu analitik olarak entegre edilebilir mi? Bunlardan birinin aday bağlantı
teodron

Örnek olarak, Perlin gürültüsü 255 8 bitlik bir tohumluk halini alır, ancak bundan üç boyutta sonsuz mesafede rastgele gürültü üretir; bunları "kılavuz noktaları" olarak tanımlamak gerçekten doğru değil, matematiksel olarak daha fazla sağlamak istemediğiniz 256 parametreye benziyorlar. Dediğiniz gibi, aslında entegre edilemez, ama saf bir işlevdir. Bağlantı verdiğiniz sayfa Perlin gürültüsünün kötü bir açıklamasıdır (açıkladığı Perlin gürültüsü değildir). Bunun için mümkün olup olmadığını gelince bazı soru iyi gürültü fonksiyonunun ... tür, değil mi?

1

[0,1] 'den artan bir N rasgele sayı dizisi üretmenin olağan yolu, herhangi bir aralıkta N rasgele sayı üretmektir, daha sonra hepsini toplam toplamlarına böler, sonra bunları elde etmek için birer birer toplar. sıra.

2, 2, 5, 8, 6 dizisini oluşturun.
Toplamları 23'tür, bu nedenle toplam sayılarımız 2/23, 2/23, 5/23, 8/23 ve 6/23'tür.
Son dizimiz 2/23, 4/23, 9/23, 17/23, 23/23

Hem X hem de Y için bu değerleri üreterek 2D'ye genişletilebilir. İstediğiniz ayrıntı düzeyini elde etmek için N'yi artırabilirsiniz.


@ Teodron'un benzer cevabında, büyük zaman ölçekleriyle verimlilik endişelerini dile getirdiniz. Karşılaştığınız asıl sorunu bilmeden, bu kaygının geçerli olup olmadığını söyleyemem; ancak başka bir seçenek, küçük N için üretilmesi ve sonucu düzeltmesi olacaktır. Uygulamaya bağlı olarak, bu aslında daha iyi sonuçlar verebilir .

resim açıklamasını buraya girin
N = 100, düzleştirme yok

resim açıklamasını buraya girin
N = 15, düzleştirmeli


Düzgünleştirmek için ne yaparsanız yapın, sonucu bir işlev bile yapmamış gibi görünüyor (yaklaşık x = 0.95); Bunun grafik programınızın bir yapaylığı mı yoksa bir hata mı olduğundan emin değilim. Tekdüzeliğin de 0.7 civarında ihlal edildiği görülmektedir. Her neyse, "olağan yol" hakkında bilgi sahibiyim - Bu soruyu soruyorum çünkü olağan yolun boktan olduğundan şüpheleniyorum. Sonuçta Perlin-gürültü, sonuçta, hiç kimse değer gürültü dev AÜSS ile ilgili bir sorun vardı, sadece "olağan yolu" oldu. Bugün, çok daha esnek ve verimli bir yolumuz var .

3
BlueRaja ile aynı fikirdeyim: Örneğe bakılmaksızın, tekdüzeliği ihlal etmeden düzgün bilinen, uygulanması kolay bilinen yollar vardır. Örneğin, hareketli ortalama veya spline çizme. Ancak, @JoeWreschnig endişe ilgili değildir. Oyun kuralları ve mekaniği asla çalışmaya geri dönmeyen nesnelere bağlı olabilir - askerin ihtiyaç duyduğu şeylere gerçekten ihtiyaç duymadığını varsaymak nadiren iyi bir fikirdir.
Superbest

1
@BlueRaja: Bunun gibi parçalı yaklaşımlarla ilgili temel şikayetlerim teodron'a cevabımda açıklanıyor. "En katı ve matematiksel olarak kesin sonuç" bulmakla ilgili değil - daha önce bizim bilmediğimiz bir matematiksel araçla yeni olasılıklar açmakla ilgili. Yine, dev değer gürültü LUT'ları ve Perlin gürültüsü arasındaki benzerliği düşünün. Sitedeki her soru, yarının akıllı CS undergrad dersleri arasında patlayabilir bir kelepçe "yeterince iyi" bir cevap gerekmez - bazen, orijinal ve profesyonel bir şey yapmak için ateş edelim, tamam mı?

1
Ya da sadece bu sitenin dönüşüm matrisleri hakkında% 90 temel karışıklık içinde yürümesine izin vermeye devam edebiliriz,% 10 "oyun oynamayı durdurmama yardım et!" Bu, her profesyonelin gelmeyi seveceği harika bir Soru-Cevap sitesi yapacak.

2
@ Joe: Bu, çağrılmadı. Kriterlerinize uygun bir çözüm istediniz, size bir tane verdim. Sadece basit olması onu kötü yapmaz.
BlueRaja - Danny Pflughoeft

1

Bu uygulamayı fraktal gürültüde bulunan oktavların toplamından esinlenerek, burada ve orada ucuz kıç kırarak biraz tavsiye ederim. Oldukça hızlı olduğuna ve parametrelerde saklanandan daha az oktav isteyerek ayarlanabileceğine inanıyorum.1/2^octave .

Bunu sadece O (log (adet)) zamanı gerektiren parçalı bir uygulama olarak görebilirsiniz . Parametre dizisi hem böl ve fethet pivot konumu hem de pivota ulaşırken kat edilen mesafe için kullanılır.

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];
};

Kayan nokta bölümlerini önceden hesaplayarak, üç kat daha fazla bilgi depolama pahasına daha hızlı yapılabilir.

Bu kısa bir örnek:

beş farklı yörünge

Örnek aşağıdaki kodla elde edilmiştir:

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

Yüksek sesle düşünmek ve hesabı kabul etmek benim güçlü yanım değil ... bu belki de mümkün değil mi? Herhangi bir belirgin paterni önlemek için, x'deki herhangi bir değişiklik üzerindeki gürültü fonksiyonunun ortalaması sıfıra yakın olmalı ve monotonluğu garanti etmek için, x'deki bu değişiklik üzerindeki gürültünün genliği x'deki değişiklikten daha küçük olmalıdır, çünkü daha büyük bir genlik x 'de x'e göre daha düşük bir değerle sonuçlanır. Ancak bu, dx'i 0'a düşürdüğünüzde, böyle bir fonksiyonun da dA'yı (A'nın genliği olduğu yerde) sıfıra indirmesi gerektiği anlamına gelir, yani herhangi bir uyumlu gürültü fonksiyonundan katkıda bulunmazsınız.

X'in 1 yaklaştıkça gürültü katkısını kademeli olarak azaltan bir fonksiyon formüle etmenin mümkün olduğunu hayal edebiliyorum, ancak bu x'e 1 yaklaştıkça yavaşlayan kavisli bir fonksiyon verecek, ki bu benim istediğim gibi değil.


1
Bu tür işlevlerin milyonlarca grafiğini çizebilirim ve SkimFlux'un söylediği gibi, bir gürültü işlevinin entegrasyonu, normalize ederseniz pratik olarak eşdeğer bir işlev verir. Yani fonksiyonlar var , bu sadece mümkün kodlanıp kodlanamayacakları meselesi . Bu yüzden burada math.se yerine soruyorum.

Örneğin, x yaklaşımı 1 olarak yavaşlayan herhangi bir işlev eşdeğer "tersine çevrilmiş" bir işleve sahiptir g(x) = 1 - f(1 - x), bunun yerine x 0'dan

Elbette, fonksiyonlar var - teodron'un yaptığı gibi bir tane çizebilirsiniz - ama bunlar 'gürültü' fonksiyonları mı? Gürültü, taban çizgisine göre örtük genliğe sahip sahte rasgele girdiye dayanan sürekli bir işlev anlamına gelir. Bu genlik çok yüksekse, adımlar arasındaki farkın çıktıyı monotonik tutacak kadar düşük olduğunu garanti edemezsiniz. Ama bana, gürültünün yoğunluğunun ve enterpolasyon adımının, spesifikasyonlarınızı karşılamak için tasarlanabileceği, biraz daha düşüneceğim.
Kylotan

Gürültü sadece "öngörülemez" anlamına gelir, üretim yöntemleri hakkında hiçbir şey söylemez (hatta teknik olarak süreklilik, ancak animasyon için neredeyse her zaman tutarlı gürültü istersiniz). Sabit uç noktaların bu işlevin olası genliğini bir miktar kısıtladığı ancak tamamen değil olduğu doğrudur. Diğer gürültü işlevleri benzer özelliklere sahiptir, örneğin herhangi bir x tamsayı için Perlin (x) = 0. Tekdüzeliklik bundan daha güçlü bir garantidir, ancak bunun çok daha güçlü olduğunu düşünmüyorum.

@JoeWreschnig Perlin gürültü işlevinin bazı kriterlerinizi açıkça ihlal ettiğinin farkında olduğunuzdan eminim. İlk olarak ızgara düğümlerinde 0'dan geçer, bu nedenle f (x + d) -f (x), bazı (düzenli aralıklı) x için d'nin sabit bir katıdır. Ayrıca, bu akıllı önbellekleme hilesi nedeniyle, büyük ızgaralar için tekrarlanacaktır. Klasik gürültü için, referans uygulamasının ızgara döşemesinin (x, y) döşemeye (x + 256, y + 256) aynı olması gerektiğini düşünüyorum. Bunun kabul edilebilir olup olmadığını ve ne ölçüde olduğunu belirtmelisiniz.
Superbest
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.