Chuyển đổi Phân phối Đồng nhất thành Phân phối Thông thường


106

Làm cách nào để tôi có thể chuyển đổi phân phối đồng nhất (như hầu hết các trình tạo số ngẫu nhiên tạo ra, ví dụ: từ 0,0 đến 1,0) thành phân phối chuẩn? Điều gì sẽ xảy ra nếu tôi muốn có giá trị trung bình và độ lệch chuẩn cho lựa chọn của mình?


3
Bạn có đặc tả ngôn ngữ hay đây chỉ là một câu hỏi thuật toán chung chung?
Bill the Lizard,

3
Câu hỏi thuật toán chung. Tôi không quan tâm ngôn ngữ nào. Nhưng tôi muốn câu trả lời không dựa vào chức năng cụ thể mà chỉ ngôn ngữ đó cung cấp.
Terhorst

Câu trả lời:


47

Các thuật toán Ziggurat là khá hiệu quả cho điều này, mặc dù Box-Muller chuyển đổi dễ dàng hơn để thực hiện từ đầu (và không điên chậm).


7
Các cảnh báo thông thường về bộ tạo đồng dư tuyến tính áp dụng cho cả hai phương pháp này, vì vậy hãy sử dụng bộ tạo cơ bản phù hợp. Chúc mừng.
dmckee --- cựu điều hành kitten

3
Chẳng hạn như Mersenee Twister, hoặc bạn có đề xuất nào khác?
Gregg Lind

47

Có rất nhiều phương pháp:

  • Đừng không sử dụng Box Muller. Đặc biệt nếu bạn vẽ nhiều số gaussian. Box Muller đưa ra kết quả được kẹp giữa -6 và 6 (giả sử độ chính xác gấp đôi. Mọi thứ trở nên tồi tệ hơn với phao.). Và nó thực sự kém hiệu quả hơn các phương pháp có sẵn khác.
  • Ziggurat ổn, nhưng cần tra cứu bảng (và một số điều chỉnh dành riêng cho nền tảng do vấn đề kích thước bộ nhớ cache)
  • Tỷ lệ đồng phục là yêu thích của tôi, chỉ có một số phép cộng / phép nhân và bản ghi 1/5 của thời gian (ví dụ: nhìn ở đó ).
  • Đảo ngược CDF hiệu quả (và bị bỏ qua, tại sao?), Bạn có thể triển khai nhanh chóng nếu bạn tìm kiếm trên google. Nó là bắt buộc đối với số Quasi-Random.

2
Bạn có chắc chắn về việc kẹp [-6,6] không? Đây là một điểm khá quan trọng nếu đúng (và đáng được lưu ý trên trang wikipedia).
redcalx

1
@locster: đây là những gì một giáo viên của tôi đã nói với tôi (anh ấy đã nghiên cứu máy phát điện như vậy, và tôi tin tưởng lời anh ấy). Tôi có thể tìm cho bạn một tài liệu tham khảo.
Alexandre C.

7
@locster: thuộc tính không mong muốn này cũng được chia sẻ bởi phương pháp CDF nghịch đảo. Xem cimat.mx/~src/prope08/randomgauss.pdf . Điều này có thể được giảm bớt bằng cách sử dụng một RNG đồng nhất có xác suất khác 0 để mang lại một số dấu phẩy động rất gần với số không. Hầu hết RNG thì không, vì chúng tạo ra một số nguyên (thường là 64 bit) sau đó được ánh xạ tới [0,1]. Điều này làm cho những phương pháp đó không phù hợp để lấy mẫu đuôi của các biến gaussian (hãy nghĩ đến việc định giá các tùy chọn đình công thấp / cao trong tài chính tính toán).
Alexandre C.

6
@AlexandreC. Chỉ cần rõ ràng về hai điểm, sử dụng các số 64 bit, các phần đuôi đi ra 8,57 hoặc 9,41 (giá trị thấp hơn tương ứng với chuyển đổi thành [0,1) trước khi lấy nhật ký). Ngay cả khi bị kẹp vào [-6, 6] cơ hội nằm ngoài phạm vi này là khoảng 1,98e-9, đủ tốt cho hầu hết mọi người ngay cả trong khoa học. Đối với con số 8,57 và 9,41, con số này trở thành 1,04e-17 và 4,97e-21. Những con số này quá nhỏ nên sự khác biệt giữa lấy mẫu Box Muller và lấy mẫu gaussian thực sự về giới hạn nói trên hầu như chỉ mang tính học thuật. Nếu bạn cần tốt hơn, chỉ cần thêm lên bốn trong số họ và chia cho 2.
CrazyCasta

6
Tôi nghĩ rằng đề xuất không sử dụng biến đổi Box Muller gây hiểu lầm cho một tỷ lệ lớn người dùng. Thật tuyệt khi biết về hạn chế, nhưng như CrazyCasta đã chỉ ra, đối với hầu hết các ứng dụng không phụ thuộc nhiều vào các yếu tố ngoại lai, bạn có thể không cần phải lo lắng về điều này. Ví dụ: nếu bạn đã từng phụ thuộc vào việc lấy mẫu từ một thông thường bằng cách sử dụng numpy, bạn đã phụ thuộc vào biến đổi Box Muller (dạng tọa độ cực) github.com/numpy/numpy/blob/… .
Andreas Grivas

30

Thay đổi phân phối của bất kỳ chức năng nào sang chức năng khác liên quan đến việc sử dụng nghịch đảo của chức năng bạn muốn.

Nói cách khác, nếu bạn nhắm đến một hàm xác suất cụ thể p (x), bạn sẽ nhận được phân phối bằng cách tích phân trên nó -> d (x) = tích phân (p (x)) và sử dụng nghịch đảo của nó: Inv (d (x)) . Bây giờ sử dụng hàm xác suất ngẫu nhiên (có phân phối đồng đều) và ép giá trị kết quả thông qua hàm Inv (d (x)). Bạn sẽ nhận được các giá trị ngẫu nhiên được đúc với phân phối theo chức năng bạn đã chọn.

Đây là cách tiếp cận toán học chung - bằng cách sử dụng nó, bây giờ bạn có thể chọn bất kỳ hàm xác suất hoặc phân phối nào bạn có miễn là nó có xấp xỉ nghịch đảo hoặc xấp xỉ nghịch đảo tốt.

Hy vọng điều này sẽ giúp và cảm ơn vì nhận xét nhỏ về việc sử dụng phân phối chứ không phải xác suất.


4
+1 Đây là một phương pháp bị bỏ qua để tạo các biến gaussian hoạt động rất tốt. Trong trường hợp này, CDF nghịch đảo có thể được tính toán hiệu quả bằng phương pháp Newton (đạo hàm là e ^ {- t ^ 2}), một giá trị gần đúng ban đầu rất dễ nhận được dưới dạng phân số hữu tỉ, vì vậy bạn cần 3-4 đánh giá erf và exp. Nó là bắt buộc nếu bạn sử dụng số bán ngẫu nhiên, một trường hợp mà bạn phải sử dụng chính xác một số thống nhất để có được một số gaussian.
Alexandre C.

9
Lưu ý rằng bạn cần đảo ngược hàm phân phối tích lũy, không phải hàm phân phối xác suất. Alexandre ngụ ý này, nhưng tôi nghĩ nhắc đến nó một cách rõ ràng hơn có thể không làm tổn thương - kể từ khi câu trả lời dường như cho thấy PDF
ltjax

Bạn có thể sử dụng PDF nếu bạn chuẩn bị chọn ngẫu nhiên một hướng liên quan đến giá trị trung bình; tôi hiểu điều đó đúng không?
Mark McKenna

2
Điều này được gọi Inverse đổi lấy mẫu
dashesy

1
Đây là câu hỏi liên quan trong SE với một câu trả lời tổng quát hơn với lời giải thích tốt đẹp.
ngắn gọn

23

Đây là một triển khai javascript bằng cách sử dụng dạng cực của phép biến đổi Box-Muller.

/*
 * Returns member of set with a given mean and standard deviation
 * mean: mean
 * standard deviation: std_dev 
 */
function createMemberInNormalDistribution(mean,std_dev){
    return mean + (gaussRandom()*std_dev);
}

/*
 * Returns random number in normal distribution centering on 0.
 * ~95% of numbers returned should fall between -2 and 2
 * ie within two standard deviations
 */
function gaussRandom() {
    var u = 2*Math.random()-1;
    var v = 2*Math.random()-1;
    var r = u*u + v*v;
    /*if outside interval [0,1] start over*/
    if(r == 0 || r >= 1) return gaussRandom();

    var c = Math.sqrt(-2*Math.log(r)/r);
    return u*c;

    /* todo: optimize this algorithm by caching (v*c) 
     * and returning next time gaussRandom() is called.
     * left out for simplicity */
}

5

Sử dụng định lý giới hạn trung tâm Mục nhập mathworld wikipedia để có lợi cho bạn.

Tạo n trong số các số được phân phối đồng đều, tính tổng chúng, trừ đi n * 0,5 và bạn có đầu ra của phân phối chuẩn gần đúng với giá trị trung bình bằng 0 và phương sai bằng (1/12) * (1/sqrt(N))(xem wikipedia về phân phối đồng đều cho số cuối cùng đó)

n = 10 cung cấp cho bạn một cái gì đó nhanh một nửa. Nếu bạn muốn một cái gì đó tốt hơn một nửa, hãy tìm giải pháp tylers (như đã lưu ý trong mục nhập wikipedia trên các bản phân phối bình thường )


1
Điều này sẽ không cung cấp một điểm chuẩn đặc biệt gần ("đuôi" hoặc điểm cuối sẽ không gần với phân phối chuẩn thực sự). Box-Muller tốt hơn, như những người khác đã đề xuất.
Peter K.

1
Box Muller có đuôi sai quá (nó sẽ trả về một số từ -6 và 6 ở độ chính xác kép)
Alexandre C.

n = 12 (tổng 12 số ngẫu nhiên trong phạm vi từ 0 đến 1 và trừ đi 6) cho kết quả là stddev = 1 và mean = 0. Sau đó, điều này có thể được sử dụng để tạo bất kỳ phân phối chuẩn nào. Chỉ cần nhân kết quả với stddev mong muốn và cộng giá trị trung bình.
JerryM

3

Tôi sẽ sử dụng Box-Muller. Hai điều về điều này:

  1. Bạn kết thúc với hai giá trị mỗi lần lặp
    Thông thường, bạn lưu vào bộ nhớ cache một giá trị và trả lại giá trị kia. Trong lần gọi mẫu tiếp theo, bạn trả về giá trị đã lưu trong bộ nhớ cache.
  2. Box-Muller đưa ra điểm Z
    Sau đó, bạn phải chia tỷ lệ điểm Z theo độ lệch chuẩn và thêm giá trị trung bình để nhận được giá trị đầy đủ trong phân phối chuẩn.

Làm thế nào để bạn thang điểm Z?
Terhorst

3
quy mô = trung bình + stdDev * zScore // cung cấp cho bạn bình thường (nghĩa là, stdDev ^ 2)
yoyoyoyosef

2

Trong đó R1, R2 là các số đồng nhất ngẫu nhiên:

PHÂN PHỐI BÌNH THƯỜNG, với SD là 1: sqrt (-2 * log (R1)) * cos (2 * pi * R2)

Đây là chính xác ... không cần phải làm tất cả những vòng lặp chậm!


Trước khi ai đó sửa lỗi cho tôi ... đây là giá trị gần đúng tôi đã nghĩ ra: (1,5- (R1 + R2 + R3)) * 1,88. Tôi thích nó quá.
Erik Aronesty 14/10/11

2

Có vẻ như không thể tin được rằng tôi có thể thêm một cái gì đó vào điều này sau tám năm, nhưng đối với trường hợp của Java, tôi muốn hướng người đọc đến phương thức Random.nextGaussian () , tạo ra một phân phối Gaussian với trung bình 0,0 và độ lệch chuẩn 1,0 cho bạn.

Một phép cộng và / hoặc phép nhân đơn giản sẽ thay đổi giá trị trung bình và độ lệch chuẩn theo nhu cầu của bạn.


1

Mô-đun thư viện Python tiêu chuẩn ngẫu nhiên có những gì bạn muốn:

normalvariate (mu, sigma)
Phân phối chuẩn. mu là giá trị trung bình và sigma là độ lệch chuẩn.

Đối với chính thuật toán, hãy xem hàm trong random.py trong thư viện Python.

Các entry thủ công là đây


2
Thật không may, thư viện của python sử dụng Kinderman, AJ và Monahan, JF, "Máy tính tạo ra các biến ngẫu nhiên bằng cách sử dụng tỷ lệ độ lệch đồng nhất", ACM Trans Math Software, 3, (1977), pp257-260. Điều này sử dụng hai biến ngẫu nhiên đồng nhất để tạo ra giá trị bình thường, thay vì một biến duy nhất, vì vậy không rõ ràng cách sử dụng nó làm ánh xạ mà OP muốn.
Ian

1

Đây là cách triển khai JavaScript của tôi đối với Thuật toán P ( Phương pháp cực cho độ lệch chuẩn ) từ Phần 3.4.1 của cuốn sách Nghệ thuật lập trình máy tính của Donald Knuth :

function normal_random(mean,stddev)
{
    var V1
    var V2
    var S
    do{
        var U1 = Math.random() // return uniform distributed in [0,1[
        var U2 = Math.random()
        V1 = 2*U1-1
        V2 = 2*U2-1
        S = V1*V1+V2*V2
    }while(S >= 1)
    if(S===0) return 0
    return mean+stddev*(V1*Math.sqrt(-2*Math.log(S)/S))
}

0

Tôi điều bạn nên thử điều này trong EXCEL: =norminv(rand();0;1). Điều này sẽ tạo ra các số ngẫu nhiên được phân phối bình thường với phương sai trung bình bằng 0 và hợp nhất. "0" có thể được cung cấp với bất kỳ giá trị nào, để các số sẽ có giá trị trung bình mong muốn và bằng cách thay đổi "1", bạn sẽ nhận được phương sai bằng bình phương đầu vào của bạn.

Ví dụ: =norminv(rand();50;3)sẽ nhường cho các số được phân phối chuẩn với MEAN = 50 VARIANCE = 9.


0

Q Làm cách nào để chuyển đổi phân phối đồng nhất (như hầu hết các trình tạo số ngẫu nhiên tạo ra, ví dụ từ 0,0 đến 1,0) thành phân phối chuẩn?

  1. Để triển khai phần mềm, tôi biết một số tên bộ tạo ngẫu nhiên cung cấp cho bạn một chuỗi ngẫu nhiên đồng nhất giả trong [0,1] (Mersenne Twister, Linear Congruate Generator). Hãy gọi nó là U (x)

  2. Nó tồn tại một lĩnh vực toán học được gọi là lý thuyết khả dĩ. Điều đầu tiên: Nếu bạn muốn lập mô hình rv với phân phối tích phân F thì bạn có thể thử chỉ đánh giá F ^ -1 (U (x)). Trong pr.theory, người ta đã chứng minh rằng rv như vậy sẽ có phân phối tích phân F.

  3. Bước 2 có thể được sử dụng để tạo rv ~ F mà không cần sử dụng bất kỳ phương pháp đếm nào khi F ^ -1 có thể được tính toán phân tích mà không có vấn đề gì. (ví dụ: exp.distribution)

  4. Để lập mô hình phân phối chuẩn, bạn có thể phân tích y1 * cos (y2), trong đó y1 ~ đồng nhất trong [0,2pi]. và y2 là phân phối liên tục.

Q: Điều gì xảy ra nếu tôi muốn có một giá trị trung bình và độ lệch chuẩn cho lựa chọn của mình?

Bạn có thể tính sigma * N (0,1) + m.

Nó có thể được chỉ ra rằng sự dịch chuyển và tỷ lệ như vậy dẫn đến N (m, sigma)


0

Đây là một triển khai Matlab bằng cách sử dụng dạng cực của phép biến đổi Box-Muller :

Chức năng randn_box_muller.m:

function [values] = randn_box_muller(n, mean, std_dev)
    if nargin == 1
       mean = 0;
       std_dev = 1;
    end

    r = gaussRandomN(n);
    values = r.*std_dev - mean;
end

function [values] = gaussRandomN(n)
    [u, v, r] = gaussRandomNValid(n);

    c = sqrt(-2*log(r)./r);
    values = u.*c;
end

function [u, v, r] = gaussRandomNValid(n)
    r = zeros(n, 1);
    u = zeros(n, 1);
    v = zeros(n, 1);

    filter = r==0 | r>=1;

    % if outside interval [0,1] start over
    while n ~= 0
        u(filter) = 2*rand(n, 1)-1;
        v(filter) = 2*rand(n, 1)-1;
        r(filter) = u(filter).*u(filter) + v(filter).*v(filter);

        filter = r==0 | r>=1;
        n = size(r(filter),1);
    end
end

Và gọi histfit(randn_box_muller(10000000),100);đây là kết quả: Box-Muller Matlab Histfit

Rõ ràng là nó thực sự không hiệu quả so với randn tích hợp Matlab .


0

Tôi có mã sau đây có thể có ích:

set.seed(123)
n <- 1000
u <- runif(n) #creates U
x <- -log(u)
y <- runif(n, max=u*sqrt((2*exp(1))/pi)) #create Y
z <- ifelse (y < dnorm(x)/2, -x, NA)
z <- ifelse ((y > dnorm(x)/2) & (y < dnorm(x)), x, z)
z <- z[!is.na(z)]

0

Việc sử dụng hàm đã thực hiện rnorm () cũng dễ dàng hơn vì nó nhanh hơn so với việc viết một bộ tạo số ngẫu nhiên cho phân phối chuẩn. Xem đoạn mã sau để chứng minh

n <- length(z)
t0 <- Sys.time()
z <- rnorm(n)
t1 <- Sys.time()
t1-t0

-2
function distRandom(){
  do{
    x=random(DISTRIBUTION_DOMAIN);
  }while(random(DISTRIBUTION_RANGE)>=distributionFunction(x));
  return x;
}

Tuy nhiên, không được đảm bảo để trở lại, phải không? ;-)
Peter K.

5
Các con số ngẫu nhiên là quá quan trọng để được phó mặc cho cơ hội.
Drew Noakes

Không trả lời câu hỏi - phân phối chuẩn có miền vô hạn.
Matt
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.