Có (họ) chức năng tiếng ồn không giảm đơn điệu không?


10

Tôi muốn một chức năng làm sống động một vật thể di chuyển từ điểm A đến điểm B theo thời gian, sao cho nó đến B vào một thời điểm cố định, nhưng vị trí của nó bất cứ lúc nào cũng bị xáo trộn một cách ngẫu nhiên, nhưng không bao giờ bị lùi lại. Các đối tượng di chuyển dọc theo các đường thẳng, vì vậy tôi chỉ cần một chiều.

Về mặt toán học, điều đó có nghĩa là tôi đang tìm kiếm một số f (x), x ∈ [0,1] liên tục, sao cho:

  • f (0) = 0
  • f (1) = 1
  • x <y → f (x) f (y)
  • Tại "hầu hết" các điểm f (x + d) - f (x) không có mối quan hệ rõ ràng với d. (Hàm này không tăng đồng đều hoặc có thể dự đoán được; tôi nghĩ điều đó cũng tương đương với việc nói không có mức độ phái sinh là một hằng số.)

Lý tưởng nhất, tôi thực sự muốn có một số cách để có một gia đình các chức năng này, cung cấp một số trạng thái hạt giống. Tôi cần ít nhất 4 bit hạt giống (16 chức năng có thể), cho mục đích sử dụng hiện tại của tôi, nhưng vì điều đó không có nhiều khả năng để cung cấp nhiều hơn.

Để tránh các vấn đề khác nhau với lỗi tích lũy, tôi thích chức năng không yêu cầu bất kỳ loại trạng thái nội bộ nào. Đó là, tôi muốn nó là một chức năng thực sự, không phải là một "chức năng" lập trình.


3
Yêu cầu thứ ba và thứ tư của bạn có thể được tính gần đúng f'(x)>0, vì vậy việc tích hợp chuẩn hóa giá trị tuyệt đối của bất kỳ hàm nhiễu nào sẽ đáp ứng tất cả các yêu cầu của bạn. Thật không may, tôi không biết cách dễ dàng nào để tính toán điều đó, nhưng có lẽ người khác cũng vậy. :)
SkimFlux

Sẽ gây nhiễu vuông góc cho chức năng của bạn ngay lập tức độ dốc?
kaoD

Khi bạn nói "Để tránh các vấn đề khác nhau với lỗi tích lũy" tôi nghĩ bạn đã lo lắng về độ chính xác. Dường như, dựa trên nhiều ý kiến ​​của bạn, bạn quan tâm đến chi phí hiệu suất của quá nhiều đánh giá. Bạn nên nêu chính xác những hạn chế về hiệu năng và bộ nhớ mà chúng ta phải tuân theo - dù sao thì yêu cầu này cũng không có ích vì người ta dường như có thể xây dựng các hàm với trạng thái không có lỗi tích lũy (Dù sao thì điều đó có nghĩa là gì?). Ngoài ra, điểm thứ 4 của bạn là sai. Một ví dụ tầm thường: Không có đạo hàm của e ^ x là hằng số, vì vậy nó không tương đương với việc nói điều đó.
Tuyệt vời nhất

Câu trả lời:


4

Đối với bài đăng này, y = f (t) trong đó t là tham số bạn thay đổi (thời gian / tiến độ) và y là khoảng cách đến mục tiêu. Vì vậy, tôi sẽ nói về các điểm trên các ô 2D trong đó trục ngang là thời gian / tiến độ và chiều dọc là khoảng cách.

Tôi nghĩ bạn có thể tạo một đường cong Bezier hình khối với điểm đầu tiên tại (0, 1) và điểm thứ tư (cuối cùng) tại (1, 0). Hai điểm giữa có thể được đặt ngẫu nhiên (x = rand, y = rand) trong hình chữ nhật 1-1 này. Tôi không thể xác minh điều này một cách phân tích, nhưng chỉ từ việc chơi xung quanh với một applet (vâng, hãy tiếp tục và cười) có vẻ như đường cong Bezier sẽ không bao giờ giảm với một ràng buộc như vậy.

Đây sẽ là hàm cơ bản b (p1, p2) của bạn cung cấp đường dẫn không giảm từ điểm p1 đến điểm p2.

Bây giờ bạn có thể tạo ab (p (1) = (0, 1), p (n) = (1, 0)) và chọn một số p (i) dọc theo đường cong này sao cho 1

Về cơ bản, bạn đang tạo một đường dẫn "chung", sau đó chia nó thành các phân đoạn và tạo lại mỗi phân đoạn.

Vì bạn muốn có một hàm toán học: Giả sử quy trình trên được đóng gói thành một hàm y = f (t, s) cung cấp cho bạn khoảng cách tại t cho hàm của hạt giống s. Bạn sẽ cần:

  • 4 số ngẫu nhiên để đặt 2 điểm giữa của spline Bezier chính (từ (0, 1) đến (1, 0))
  • n-1 số cho giới hạn của mỗi phân đoạn nếu bạn có n phân đoạn (phân đoạn đầu tiên luôn bắt đầu tại (0, 1) tức là t = 0 và kết thúc cuối cùng tại (1,0) tức là t = 1)
  • 1 số nếu bạn muốn chọn ngẫu nhiên số lượng phân khúc
  • Thêm 4 số để đặt các điểm giữa của spline của đoạn mà t của bạn hạ cánh tại

Vì vậy, mỗi hạt giống phải cung cấp một trong những điều sau đây:

  • 7 + n số thực từ 0 đến 1 (nếu bạn muốn kiểm soát số lượng phân khúc)
  • 7 số thực và một số nguyên lớn hơn 1 (cho một số phân đoạn ngẫu nhiên)

Tôi tưởng tượng bạn có thể thực hiện một trong hai điều này bằng cách đơn giản là cung cấp một mảng các số làm hạt giống. Ngoài ra, bạn có thể làm một cái gì đó như cung cấp một số s làm hạt giống, sau đó gọi trình tạo số ngẫu nhiên tích hợp với rand (s), rand (s + 1), rand (s + 2), v.v. s và sau đó tiếp tục gọi rand.NextNumber).

Lưu ý rằng mặc dù toàn bộ hàm f (t, s) được tạo thành từ nhiều phân đoạn, bạn chỉ đánh giá một phân đoạn cho mỗi t. Bạn sẽ cần phải liên tục tính toán ranh giới của các phân đoạn bằng phương pháp này, bởi vì bạn sẽ phải sắp xếp chúng để đảm bảo không có hai phân đoạn trùng nhau. Bạn có thể có thể tối ưu hóa và loại bỏ công việc bổ sung này và chỉ tìm thấy các điểm cuối của một phân đoạn cho mỗi cuộc gọi, nhưng điều này đối với tôi không rõ ràng ngay bây giờ.

Ngoài ra, đường cong Bezier là không cần thiết, bất kỳ spline hành xử phù hợp sẽ làm.

Tôi đã tạo một triển khai Matlab mẫu.

Hàm Bezier (vector hóa):

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

Hàm Bezier ghép được mô tả ở trên (cố tình bỏ trống không được ghi chú để làm rõ mức độ cần thiết cho mỗi cuộc gọi):

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

Kịch bản vẽ đồ thị cho hàm ngẫu nhiên (lưu ý rằng đây là nơi duy nhất gọi hàm ngẫu nhiên, các biến ngẫu nhiên cho tất cả các mã khác được truyền từ một mảng ngẫu nhiên này):

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

Đây là một đầu ra mẫu:

nhập mô tả hình ảnh ở đây

Nó dường như đáp ứng hầu hết các tiêu chí của bạn. Tuy nhiên:

  • Có "góc". Điều này có thể được chấp nhận bằng cách sử dụng các đường cong Bezier phù hợp hơn.
  • Nó "rõ ràng" trông giống như splines, mặc dù bạn thực sự không thể đoán nó sẽ làm gì sau một khoảng thời gian không tầm thường trừ khi bạn biết hạt giống.
  • Nó rất hiếm khi lệch quá nhiều về góc (có thể được sửa bằng cách chơi với phân phối của trình tạo hạt giống).
  • Hàm Bezier hình khối không thể đạt đến một khu vực gần góc với các ràng buộc này.

1

Tôi đoán rằng thay vì pha trộn một loạt các cosin biến đổi (như các sản phẩm chấm trong tiếng ồn perlin mang lại cho bạn), bạn có thể pha trộn một số hàm đơn điệu bắt đầu ở f (0) = 0, như f (x) = x, hoặc 2x, hoặc x ^ 2, v.v. Trên thực tế vì tên miền của bạn bị giới hạn ở 0 => 1, bạn cũng có thể pha trộn các hàm lượng giác phù hợp với hóa đơn trong miền đó như cos (90 * x + 270). Để bình thường hóa các phương thức của bạn kết thúc ở 1, bạn chỉ cần chia tổng trọng số của các phương thức đơn điệu này bắt đầu từ f (0) = 0 cho f (1). Một cái gì đó như thế này cũng khá dễ dàng để đảo ngược (mà tôi tập hợp bạn muốn từ bit về các hàm thực không trạng thái so với các hàm lập trình).

Hi vọng điêu nay co ich.


1

Người ta có thể phân tích bức tranh thô này nhập mô tả hình ảnh ở đây Bạn có thể kết thúc với một chức năng thực hiện hoạt hình của mình một cách nhanh chóng, bằng cách sử dụng chức năng rand thống nhất. Tôi biết đây không phải là công thức toán học chính xác, nhưng thực tế không có công thức toán học nào cho một hàm ngẫu nhiên và thậm chí nếu có một công thức, bạn sẽ mã hóa rất nhiều để đạt được điều này. Xem xét bạn không chỉ định bất kỳ điều kiện độ mịn nào, cấu hình tốc độ là $ C ^ 0 $ liên tục (nhưng vì bạn không làm việc với robot, nên không cần phải lo lắng về cấu hình tăng tốc không liên tục).


"Thực sự không có công thức toán học cho một hàm ngẫu nhiên" Tôi muốn một hàm nhiễu, không phải là một hàm ngẫu nhiên. Các chức năng chống ồn được ghi nhận tốt để tồn tại. Các định nghĩa piecewise như thế này cũng có xu hướng tạo ra sự kém hiệu quả (đánh giá trở thành O (mảnh) trở thành vấn đề khi bạn có thang đo thời gian dài), các hàm không tinh khiết (đánh giá trong O (1) nhưng cần giữ vị trí trước đó) hoặc quá mức hạn chế các chức năng có thể (ví dụ: tất cả các điểm uốn đều ở các khoảng cố định).

Hmm, xin lỗi, tôi nghĩ rằng các hàm nhiễu cũng sử dụng thủ tục tạo số ngẫu nhiên và cũng phụ thuộc vào một tập hợp các điểm hướng dẫn / khóa riêng biệt để tạo ra hình dạng (Tôi thấy Perlin Noise đã được đề cập .. một hoạt động thông qua giả ngẫu nhiên bộ tạo số khá khó tích hợp, do đó không có giải pháp phân tích). Có thể tích hợp một chức năng tiếng ồn phân tích? Tôi tự hỏi nếu một trong số này có thể là một liên kết
teodron

Ví dụ, nhiễu Perlin có trạng thái hạt giống là 255 số 8 bit, nhưng từ đó nó tạo ra nhiễu ngẫu nhiên trong khoảng cách vô hạn theo ba chiều; thật không chính xác khi mô tả chúng là "điểm hướng dẫn", về mặt toán học, chúng giống với 256 tham số khác mà bạn không muốn tiếp tục cung cấp. Như bạn nói về cơ bản nó không thể tích hợp được, nhưng nó là một chức năng thuần túy. Trang bạn liên kết đến là một lời giải thích tồi về tiếng ồn Perlin (nó không thực sự là tiếng ồn Perlin mà ông giải thích). Về việc liệu một số loại chức năng tiếng ồn có thể ... tốt, đó là câu hỏi, phải không?

1

Cách thông thường để tạo ra một chuỗi số ngẫu nhiên N tăng dần từ [0,1] là tạo N số ngẫu nhiên trong bất kỳ phạm vi nào, sau đó chia tất cả chúng cho tổng của chúng, sau đó tổng hợp chúng một lần để có được sự nối tiếp.

Tạo chuỗi 2, 2, 5, 8, 6.
Tổng của chúng là 23, vì vậy các số của chúng tôi để tính tổng là 2/23, 2/23, 5/23, 8/23 và 6/23.
Trình tự cuối cùng của chúng tôi là 2/23, 4/23, 9/23, 17/23, 23/23

Điều này có thể được mở rộng thành 2D bằng cách tạo các giá trị này cho cả X và Y. Bạn có thể tăng N để có bất kỳ mức độ chi tiết nào bạn muốn.


Trong câu trả lời tương tự của @ teodron, bạn đã trích dẫn mối quan tâm về hiệu quả với quy mô thời gian lớn. Không biết vấn đề thực sự bạn gặp phải, tôi không thể biết liệu mối quan tâm đó có hợp lệ hay không; nhưng một tùy chọn khác sẽ là tạo ra N nhỏ và đơn giản là làm mịn kết quả. Tùy thuộc vào ứng dụng, điều này thực sự có thể cho kết quả tốt hơn .

nhập mô tả hình ảnh ở đây
N = 100, không làm mịn

nhập mô tả hình ảnh ở đây
N = 15, với làm mịn


Bất cứ điều gì bạn đang làm để làm mịn, dường như nó đã tạo ra kết quả thậm chí không phải là một hàm (khoảng x = 0,95); Tôi không chắc đó là một sự giả tạo của chương trình đồ họa của bạn hay là một lỗi. Tính đơn điệu dường như cũng bị vi phạm khoảng 0,7. Dù sao, tôi quen thuộc với "cách thông thường" - Tôi đang hỏi câu hỏi này bởi vì tôi nghi ngờ cách thông thường là tào lao. Rốt cuộc, Perlin-noise, rốt cuộc, không ai gặp vấn đề với LUT khổng lồ về tiếng ồn giá trị, đó chỉ là "cách thông thường". Ngày nay, chúng ta có một cách linh hoạt hiệu quả hơn đáng kể .

3
Tôi đồng ý với BlueRaja: Có những cách làm mịn nổi tiếng, dễ thực hiện mà không vi phạm tính đơn điệu, bất kể ví dụ nào. Ví dụ, di chuyển trung bình hoặc vẽ splines. Tuy nhiên, mối quan tâm @JoeWreschnig không liên quan. Các quy tắc và cơ chế trò chơi có thể phụ thuộc vào các đối tượng không bao giờ rút lui để hoạt động - hiếm khi nên cho rằng những điều người hỏi không thực sự cần những gì anh ta nói anh ta cần.
Tuyệt vời nhất

1
@BlueRaja: Những phàn nàn cơ bản của tôi về cách tiếp cận từng phần như thế này được mô tả trong phản hồi của tôi với teodrone. Đó không phải là tìm kiếm "kết quả chính xác nhất về mặt toán học và chính xác nhất" - đó là về việc mở ra những khả năng mới với một công cụ toán học mà trước đây chúng ta chưa biết. Một lần nữa, hãy xem xét sự tương đồng giữa tiếng ồn giá trị khổng lồ LUT và tiếng ồn Perlin. Không phải mọi câu hỏi trên trang web đều cần một câu trả lời "đủ tốt", bất kỳ sinh viên CS thông minh nửa chừng nào cũng có thể nổ ra giữa các bài giảng - đôi khi, hãy bắn để làm một cái gì đó nguyên bản và chuyên nghiệp, được chứ?

1
Hoặc chúng ta chỉ có thể tiếp tục để trang web này đắm chìm trong 90% nhầm lẫn cơ bản về ma trận biến đổi, 10% "giúp tôi ngừng chơi trò chơi!" Điều đó sẽ làm cho một trang web Hỏi & Đáp tuyệt vời mà mọi chuyên gia sẽ thích đến.

2
@Joe: Đó là, erm, chưa được khám phá. Bạn yêu cầu một giải pháp phù hợp với tiêu chí của bạn, tôi đã đưa cho bạn một giải pháp. Chỉ vì nó đơn giản không làm cho nó xấu.
BlueRaja - Daniel Pflughoeft

1

Tôi đề nghị thực hiện này lấy cảm hứng từ việc tổng hợp các quãng tám được tìm thấy trong tiếng ồn fractal, với một chút mông rẻ tiền xáo trộn ở đây và đó. Tôi tin rằng nó khá nhanh và có thể được điều chỉnh bằng cách yêu cầu số quãng tám ít hơn so với được lưu trữ trong các tham số với độ chính xác khoảng 1/2^octave.

Bạn có thể xem nó như là một triển khai từng phần chỉ yêu cầu thời gian O (log (miếng)) . Mảng tham số được sử dụng cho cả vị trí trục chia và chinh phục và cho quãng đường di chuyển khi đạt đến trục.

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

Nó có thể được thực hiện nhanh hơn bằng cách tính toán trước các phân chia dấu phẩy động, với chi phí lưu trữ gấp ba lần thông tin.

Đây là một ví dụ nhanh:

năm quỹ đạo khác nhau

Ví dụ thu được với đoạn mã sau:

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

Suy nghĩ thành tiếng, và thừa nhận tính toán không phải là điểm mạnh của tôi ... điều này có lẽ là không thể? Để tránh bất kỳ mô hình rõ ràng nào, trung bình của hàm nhiễu trên bất kỳ thay đổi nào trong x phải gần bằng 0 và để đảm bảo tính đơn điệu, biên độ của nhiễu đối với thay đổi trong x phải nhỏ hơn thay đổi trong x, vì bất kỳ biên độ lớn hơn nào cũng có thể dẫn đến giá trị thấp hơn tại x 'so với x. Nhưng điều đó có nghĩa là khi bạn giảm dx về 0, thì hàm đó cũng phải giảm dA (trong đó A là biên độ) về 0, nghĩa là bạn không nhận được sự đóng góp nào từ bất kỳ chức năng nhiễu tuân thủ nào.

Tôi có thể tưởng tượng rằng có thể hình thành một hàm làm giảm dần sự đóng góp tiếng ồn khi x tiến đến 1, nhưng điều đó sẽ cung cấp cho bạn một hàm cong giảm tốc khi x tiếp cận 1, đó không phải là điều tôi nghĩ bạn muốn.


1
Tôi có thể vẽ hàng triệu biểu đồ của các chức năng như vậy và như SkimFlux nói rằng việc tích hợp chức năng nhiễu mang lại chức năng tương đương thực tế nếu bạn bình thường hóa nó. Vì vậy, các chức năng tồn tại , đó chỉ là vấn đề liệu chúng có thể được mã hóa khả thi hay không . Do đó hỏi ở đây thay vì math.se.

Ví dụ: bất kỳ chức năng nào giảm tốc khi x tiếp cận 1 đều có chức năng "đảo ngược" tương đương g(x) = 1 - f(1 - x), thay vào đó tăng tốc khi x khởi hành 0.

Chắc chắn, các chức năng tồn tại - bạn có thể vẽ một chức năng như teodron đã làm - nhưng chúng có phải là chức năng 'nhiễu' không? Tiếng ồn ngụ ý một hàm liên tục dựa trên đầu vào giả ngẫu nhiên với biên độ ngầm định so với đường cơ sở. Và nếu biên độ đó quá cao thì bạn không thể đảm bảo sự khác biệt giữa các bước đủ thấp để giữ mức đầu ra đơn điệu. Nhưng điều đó xảy ra với tôi rằng mật độ của tiếng ồn và bước nội suy có thể được tạo ra để đáp ứng các thông số kỹ thuật của bạn, điều mà tôi sẽ suy nghĩ thêm một chút.
Kylotan

Tiếng ồn chỉ có nghĩa là "không thể đoán trước", nó không nói gì về các phương thức tạo (hoặc thậm chí, về mặt kỹ thuật, tính liên tục, mặc dù đối với hoạt hình bạn hầu như luôn muốn có tiếng ồn kết hợp). Đúng là các điểm cuối cố định hạn chế phần nào biên độ có thể của hàm này, nhưng không hoàn toàn. Các hàm nhiễu khác có các thuộc tính tương tự, ví dụ Perlin (x) = 0 cho bất kỳ số nguyên x nào. Tính đơn điệu là một sự đảm bảo mạnh mẽ hơn thế, nhưng tôi không nghĩ nó mạnh đến mức nó không thể làm được.

@JoeWreschnig Tôi chắc chắn rằng bạn biết rằng chức năng nhiễu Perlin ngang nhiên vi phạm một số tiêu chí của bạn. Đầu tiên, nó đi qua 0 tại các nút lưới nên f (x + d) -f (x) là bội số không đổi của d đối với một số nhất định (cách đều đặn) x. Ngoài ra, do thủ thuật bộ nhớ đệm thông minh đó, nó sẽ lặp lại cho các lưới lớn. Đối với nhiễu cổ điển, tôi nghĩ rằng việc triển khai tham chiếu được cho là có ô lưới (x, y) giống hệt với ô (x + 256, y + 256). Bạn nên nói nếu điều này được chấp nhận, và ở mức độ nào.
Tuyệt vời nhất
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.