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?
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?
Câu trả lời:
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).
Có rất nhiều phương pháp:
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.
Đâ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 */
}
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 )
Tôi sẽ sử dụng Box-Muller. Hai điều về điều này:
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!
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.
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.
Đâ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))
}
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.
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?
Để 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)
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.
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)
Để 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)
Đâ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ả:
Rõ ràng là nó thực sự không hiệu quả so với randn tích hợp Matlab .
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)]
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
function distRandom(){
do{
x=random(DISTRIBUTION_DOMAIN);
}while(random(DISTRIBUTION_RANGE)>=distributionFunction(x));
return x;
}