随机数
出自FlashWiki
(原文:Numbers)
目录 |
概率基础
单事件概率
当一件事情发生的时候,它的概率等于这一事件可能发生的情况总数除以所有可能发生的事件总数。最简单的例子是抛硬币。它总共只有2种可能性,要么是正面,要么是反面。所以翻到任何一面的概率都是1/2即50%。
比如以一副有52张牌的扑克为例子。
从里面抽出任意一张A的概率:抽到A的情况总数 / 牌的总张数 = 4 / 52 = 0.077 = ~ 8%
从里面抽出任意一张方块的概率: 抽到方块的情况总数 / 牌的总张数 = 13 / 52 = 0.25 = 25%
连续事件概率
当一连串事件发生的时候,它的概率等于这连串事件中的每一个事件的概率相乘得到的乘积。
从一副扑克中连续抽到2张A的概率:
第一张 –> 4 / 52 (这个时候有4 张A在总共 52 张牌中)
第二张 –> 3 / 51 (因为我们已经抽出了一张, 那么还有 3 张A在总共 51 张牌中)
(4 / 52) * (3 / 51) = 0.00452 = 0.45%
从一副扑克中抽到5张同花色的牌的概率:
(52 / 52) * (12 / 51) * (11 / 50) * (10 / 49) * (9 / 48) = 0.00198 = ~0.2%
(注意第一张牌的概率为1,因为任何一张牌都能起到指定花色的作用)
那么抽任意5张并且没有对子的概率是多少呢?
(52 / 52) * (48 / 51) * ( 44 / 50) * (40 / 49) * (36 / 48) = 0.507 = ~ 51%
(对于每抽一张牌的概率计算,我们都要重新计算总共有多少张剩余以及这一次我能抽的牌中符合要求的可能性还剩多少)
概率论的一些资料
均匀分布的随机数
Processing 有一个生成随机数的方法,它可以通过指定一个最小和最大值后在每次执行的时候返回一个这之间的浮点数:
float r = random(0,100); // random floating points between 0 and 100 int n = int(random(4,7)); // converting random values to integers
http://processing.org/reference/random_.html
(请注意,这里生成的随机数其实是伪随机。我们这里不打算讨论如何生成真正的随机数,但是如果你对这感兴趣的话可以访问:http://random.mat.sbg.ac.at/ )
如果我们在一段时间里不停的生成随机数,我们可以发现这些随机数是均匀分布的。
void draw() {
float brightness = random(255);
background(brightness);
}
非均匀分布随机数
有很多种产生非均匀分布随机数的方法。一种简单的做法是把可能性放进数组,再从数组中随机取值,这样你可以人为的控制某个值出现的概率。比如:
int[] stuff = new int[5]; stuff[0] = 1; stuff[1] = 1; stuff[2] = 2; stuff[3] = 3; stuff[4] = 3; int index = int(random(stuff.length)); println(stuff[index]);
这是一种非常不提倡的方法,它效率很低。但是它又是一种很简单的方法,让你有40%的概率得到1,20%的概率得到2,40%的概率得到3。想想当你需要扩展这些代码,比如有非常大量的可能性要存入数组,如果再使用这种方法的话就会非常浪费时间。
另一种做法是使用随机数 (比如,我们有一个0到1的随机数) 当这个随机数在我们指定的区间出现的话才去执行我们的事件。
float prob = 0.10f; //to store a probability of 10%
float r = random(1); //get a random floating point value between 0 and 1
if (r < prob) { //if our random is less than .1
/*INSTIGATE THE EVENT HERE*/
}
在我们的程序里,我们常常需要为一些参数指定一个随机数。(比如:生成一种随机的颜色,设置一个随机的速度或者位置之类的)然后有些时候我们希望产生的随机数并不是均匀分布的,比如:
结果 A -- 10% | 结果 B -- 60% | 结果 C -- 30%
要在程序中实现这个需求,我们可以可以用random方法产生一个0.0到1.0之间的随机数,再设定当随机数在0.0到0.1之间的时候(10%的概率)执行结果A,当随机数在0.1到0.7之间的时候(60%的概率)则执行结果B,当随机数在0.7到1.0之间的时候(30%的概率)则执行结果C。
//probabilities for 3 different cases (these need to add up to 100% since something always occurs here!)
float red_prob = 0.10; // 10% chance of red color occurring
float green_prob = 0.60 + red_prob; // 60% chance of green color occuring
float blue_prob = 0.30 + green_prob; // 30% chance of blue color occuring
float num = random(1); // pick a random number between 0 and 1
if (num < red_prob) {
fill(255,0,0,50);
} else if (num < green_prob) {
fill(0,255,0,50);
} else {
fill(0,0,255,50);
}
ellipse(random(width),random(height),16,16);
正态分布(高斯分布)
生活中有一种随机分布叫正态分布(也叫高斯分布或者钟形曲线)。比如身高,人的身高不可能均匀分布,没有可能出现身高为1cm的人,也没有可能出现身高为10m的人,而围绕一个平均值在其周围分布,而且越靠近这个平均值的概率越高。
正态分布函数有2个参数决定,一个是期望,一个是方差。当我们从正态分布随机函数取随机数的时候,与均匀分布随机数不同,我们得到的值会更靠近之前设定的期望值,而方差决定了其分布的幅度。期望为0,方差为1的正态分布通常被称为标准正态分布。
参考: http://mathworld.wolfram.com/NormalDistribution.html
值得庆幸的是,我们不需要自己去实现那复杂的算法,Processing 已经为我们准备好了一个标准正态分布的随机数生成函数,我们只要对它进行一下加工就可以达到指定期望和方差的目的。
Random generator; generator = new Random(); float num = (float) generator.nextGaussian(); float sd = 20; float mean = 100; num = ( num * sd ) + mean;
试试这代码产生的随机数,可以帮助你更好的理解。
柏林噪声
在我们上面的例子中,不管是均匀分布的随机数还是非均匀分布的随机数,随机数之间是没有任何联系的,就是说上一个数与下一个数的产生是无关的。然而,日常生活中有一种随机并不是这样的,比如波浪、比如风、比如闪电,它看上去是随机的,但是随机数之间却又是连续的。这就要用到柏林噪声(Perlin Noise 发明于1980年 by Ken Perlin)来生成这一连串连续的随机数。
参考资料:
- "Making Noise" by Ken Perlin
- a nice tutorial about Perlin Noise algorithm
- Processing "noise" reference
同样幸运地,我们不需要自己去实现柏林噪声的算法,Processing 已经为我们准备好了,我们只需要简单的调用 noise 函数,就可以为我们生成1维、2维或3维的随机数。
下面让我们试试生成1维的柏林噪声随机数。给noise一个值固定的参数x,函数返回给我们的也是一个固定的0到1之间的浮点数。
for (int i = 0; i < 5; i++) {
float xoff = 0.0;
float noisevalue = noise(xoff);
println(noisevalue);
}
现在试试下面的代码:
float xoff = 0.0;
for (int i = 0; i < 5; i++) {
xoff += 0.01;
float noisevalue = noise(xoff);
println(noisevalue);
}
这里在for循环的每一次计算中,我们改变了参数x的值,让它增加了0.01,你会发现noise返回的随机数也开始不同了。而且你会发现参数中值越小的变化,带来的随机数的变化也越小。
2维柏林噪声的的运行机制和1维是一样的,只不过他的参数有2个,x和y 就像2维平面内的坐标。要理解2维平面内的柏林噪声随机和纯随机的区别其实很容易。如果你的年纪足够大的话,你应该看过老式的那种用天线接受信号的黑白电视机,那“雪花点”就是一种纯随机生成的图象。我们可以用下面的程序简单模拟它:
2D "雪花点" (纯随机):
loadPixels();
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
float bright = random(255);
pixels[x+y*width] = color(bright);
}
}
updatePixels();
下面是使用柏林噪声的方法,这里我们给noise传入了2个参数,xoff和yoff(事实上这只是个名字而已,你可以随便指定你个你想要的名字)
2D "云" (柏林噪声):
loadPixels();
float xoff = 0.0; // Start xoff at 0
// For every x,y coordinate in a 2D space, calculate a noise value and produce a brightness value
for (int x = 0; x < width; x++) {
xoff += increment; // Increment xoff
float yoff = 0.0; // For every xoff, start yoff at 0
for (int y = 0; y < height; y++) {
yoff += increment; // Increment yoff
// Calculate noise and scale by 255
float bright = noise(xoff,yoff)*255;
// Set each pixel onscreen to a grayscale value
pixels[x+y*width] = color(bright);
}
}
updatePixels();
参考阅读
- Computational Beauty of Nature, Introduction, Gary William Flake (you must be logged in through NYU to access the online version.)
- Introduction to Probability, Grinstead, Snell (suggested)
- Mathematics and Physics for Programmers, Chapter 1 — Numbers, Danny Kodicek (suggested)





