ฉันจะแปลงการแจกแจงแบบสม่ำเสมอได้อย่างไร (เนื่องจากเครื่องกำเนิดตัวเลขสุ่มส่วนใหญ่สร้างขึ้นเช่นระหว่าง 0.0 ถึง 1.0) เป็นการแจกแจงปกติ จะเกิดอะไรขึ้นถ้าฉันต้องการค่าเฉลี่ยและส่วนเบี่ยงเบนมาตรฐานที่ฉันเลือก?
ฉันจะแปลงการแจกแจงแบบสม่ำเสมอได้อย่างไร (เนื่องจากเครื่องกำเนิดตัวเลขสุ่มส่วนใหญ่สร้างขึ้นเช่นระหว่าง 0.0 ถึง 1.0) เป็นการแจกแจงปกติ จะเกิดอะไรขึ้นถ้าฉันต้องการค่าเฉลี่ยและส่วนเบี่ยงเบนมาตรฐานที่ฉันเลือก?
คำตอบ:
อัลกอริทึม Zigguratมีประสิทธิภาพสวยสำหรับนี้แม้ว่าBox-Muller เปลี่ยนเป็นเรื่องง่ายที่จะใช้จากรอยขีดข่วน (และไม่บ้าช้า)
มีหลายวิธี:
การเปลี่ยนการกระจายของฟังก์ชันไปเป็นฟังก์ชันอื่นเกี่ยวข้องกับการใช้ฟังก์ชันผกผันที่คุณต้องการ
กล่าวอีกนัยหนึ่งคือถ้าคุณมุ่งเป้าไปที่ฟังก์ชันความน่าจะเป็นเฉพาะ p (x) คุณจะได้รับการแจกแจงโดยการรวมทับ -> d (x) = ปริพันธ์ (p (x)) และใช้ผกผัน: Inv (d (x)) . ตอนนี้ใช้ฟังก์ชันความน่าจะเป็นแบบสุ่ม (ซึ่งมีการแจกแจงแบบสม่ำเสมอ) และส่งค่าผลลัพธ์ผ่านฟังก์ชัน Inv (d (x)) คุณควรจะได้รับค่าสุ่มที่มีการกระจายตามฟังก์ชันที่คุณเลือก
นี่คือวิธีการคำนวณแบบทั่วไปโดยใช้วิธีนี้คุณสามารถเลือกฟังก์ชันความน่าจะเป็นหรือการแจกแจงใด ๆ ที่คุณมีได้ตราบเท่าที่มีการประมาณแบบผกผันหรือผกผันที่ดี
หวังว่านี่จะช่วยได้และขอบคุณสำหรับข้อสังเกตเล็ก ๆ เกี่ยวกับการใช้การแจกแจงไม่ใช่ความน่าจะเป็น
นี่คือการใช้งานจาวาสคริปต์โดยใช้รูปแบบเชิงขั้วของการแปลง 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 */
}
ใช้ทฤษฎีบทขีด จำกัด กลางวิกิพีเดียรายการ รายการ Mathworldเพื่อประโยชน์ของคุณ
สร้าง n ของจำนวนที่กระจายสม่ำเสมอรวมกันลบ n * 0.5 และคุณได้ผลลัพธ์ของการแจกแจงปกติโดยประมาณที่มีค่าเฉลี่ยเท่ากับ 0 และความแปรปรวนเท่ากับ(1/12) * (1/sqrt(N))
(ดูวิกิพีเดียเกี่ยวกับการแจกแจงแบบสม่ำเสมอสำหรับตัวสุดท้ายนั้น)
n = 10 ให้บางอย่างเร็วครึ่งหนึ่ง หากคุณต้องการบางสิ่งที่ดีกว่าครึ่งหนึ่งสำหรับโซลูชัน Tylers (ตามที่ระบุไว้ในรายการวิกิพีเดียเกี่ยวกับการแจกแจงปกติ )
ฉันจะใช้ Box-Muller สองสิ่งเกี่ยวกับสิ่งนี้:
โดยที่ R1, R2 เป็นตัวเลขเครื่องแบบสุ่ม:
การกระจายตามปกติโดยมี SD เป็น 1: sqrt (-2 * log (R1)) * cos (2 * pi * R2)
นี่คือสิ่งที่แน่นอน ... ไม่จำเป็นต้องทำลูปช้าทั้งหมด!
ดูเหมือนจะเหลือเชื่อที่ฉันสามารถเพิ่มบางอย่างลงในสิ่งนี้ได้หลังจากแปดปี แต่สำหรับกรณีของ Java ฉันต้องการชี้ให้ผู้อ่านใช้เมธอด Random.nextGaussian ()ซึ่งสร้างการแจกแจงแบบเกาส์ที่มีค่าเฉลี่ย 0.0 และค่าเบี่ยงเบนมาตรฐาน 1.0 สำหรับคุณ
การบวกและ / หรือการคูณอย่างง่ายจะเปลี่ยนค่าเฉลี่ยและส่วนเบี่ยงเบนมาตรฐานตามความต้องการของคุณ
สุ่มโมดูลไลบรารี Python มาตรฐานมีสิ่งที่คุณต้องการ:
normalvariate (mu, sigma)
การแจกแจงปกติ mu คือค่าเฉลี่ยและซิกม่าคือส่วนเบี่ยงเบนมาตรฐาน
สำหรับอัลกอริทึมเองให้ดูที่ฟังก์ชันใน random.py ในไลบรารี Python
นี่คือการใช้งาน JavaScript ของฉันในอัลกอริทึม P ( วิธีเชิงขั้วสำหรับการเบี่ยงเบนปกติ ) จากส่วน 3.4.1 ของหนังสือของ Donald Knuth The Art of Computer Programming :
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))
}
ฉันควรลองสิ่งนี้ใน EXCEL: =norminv(rand();0;1)
Excel: สิ่งนี้จะผลิตผลของตัวเลขสุ่มซึ่งปกติควรจะกระจายด้วยค่าเฉลี่ยศูนย์และความแปรปรวนรวมกัน สามารถระบุ "0" ด้วยค่าใดก็ได้เพื่อให้ตัวเลขเป็นค่าเฉลี่ยที่ต้องการและเมื่อเปลี่ยน "1" คุณจะได้ค่าความแปรปรวนเท่ากับกำลังสองของข้อมูลที่คุณป้อน
ตัวอย่างเช่น: =norminv(rand();50;3)
จะให้ผลกับตัวเลขที่กระจายตามปกติโดยมี MEAN = 50 VARIANCE = 9
ถามฉันจะแปลงการแจกแจงแบบสม่ำเสมอ (เนื่องจากเครื่องกำเนิดตัวเลขสุ่มส่วนใหญ่สร้างขึ้นเช่นระหว่าง 0.0 ถึง 1.0) เป็นการแจกแจงแบบปกติได้อย่างไร
สำหรับการใช้งานซอฟต์แวร์ฉันรู้จักชื่อเครื่องกำเนิดไฟฟ้าแบบสุ่มสองชื่อซึ่งให้ลำดับสุ่มหลอกที่เหมือนกันใน [0,1] (Mersenne Twister, Linear Congruate Generator) เรียกมันว่า U (x)
มีพื้นที่ทางคณิตศาสตร์ซึ่งเรียกว่าทฤษฎีความน่าจะเป็น สิ่งแรก: หากคุณต้องการสร้างโมเดล rv ด้วยการแจกแจงแบบรวม F คุณสามารถลองประเมิน F ^ -1 (U (x)) ใน pr ทฤษฎีได้รับการพิสูจน์แล้วว่า rv ดังกล่าวจะมีการแจกแจงแบบรวม F
ขั้นตอนที่ 2 สามารถใช้ได้ในการสร้าง rv ~ F โดยไม่ต้องใช้วิธีการนับใด ๆ เมื่อ F ^ -1 สามารถรับการวิเคราะห์ได้โดยไม่มีปัญหา (เช่น exp.distribution)
ในการจำลองการแจกแจงแบบปกติคุณสามารถคำนวณ y1 * cos (y2) โดยที่ y1 ~ มีความสม่ำเสมอใน [0,2pi] และ y2 คือการแจกแจงแบบรีรี
ถาม: ถ้าฉันต้องการค่าเฉลี่ยและส่วนเบี่ยงเบนมาตรฐานที่ฉันเลือก?
คุณสามารถคำนวณ sigma * N (0,1) + m
แสดงได้ว่าการขยับและการปรับขนาดดังกล่าวนำไปสู่ N (m, ซิกม่า)
นี่คือการนำ Matlab ไปใช้โดยใช้รูปแบบเชิงขั้วของการแปลงBox-Muller :
ฟังก์ชัน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
และการเรียกใช้histfit(randn_box_muller(10000000),100);
นี่คือผลลัพธ์:
เห็นได้ชัดว่ามันจะไม่มีประสิทธิภาพจริงๆเมื่อเทียบกับ Matlab ในตัวrandn
ฉันมีรหัสต่อไปนี้ซึ่งอาจช่วยได้:
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)]
นอกจากนี้ยังง่ายกว่าที่จะใช้ฟังก์ชัน rnorm () ที่ใช้งานได้เนื่องจากเร็วกว่าการเขียนตัวสร้างตัวเลขสุ่มสำหรับการแจกแจงปกติ ดูรหัสต่อไปนี้เป็นข้อพิสูจน์
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;
}