今天在群里丢了一把脸,起因是因为我在研究js里的随机数,前提是我不知道Math.random()会返回0,但是不会返回1,结果被人笑话了,这里拿出来,希望大家也注意这个基础的问题。
前几天在公交车上挤车,车上没有美女,于是我就开始想一些js问题,突然我想能不能人工控制js中的随机数的随机概率呢?在某些游戏中,我们故意控制概率来让洗出来的牌更加合理(我们那有种牌叫做保皇,每次地主和狗腿子都到了一个人家里的时候,那个人的牌一般都会特别好,我觉得这是人为设计的)。
说干就干,基本目标就是,给出一个数组,我可以设定其中某个元素被抽中的概率是某个值,之后随机从数组取值的时候会按照这个概率去取,而且可以多个设置。
先上代码吧,这是一个对象:(M和M.Array对象是我框架里的,不是原生对象)
view source
print?
01 var Random=function(arr_da ta){
02 this.array=arr_da ta;
03 this.probs=[];
04 this.refer_array=new Array();
05 }
06 Random.prototype={
07 /**
08 * 设置某个元素被抽中的几率
09 * @param {number} index 要设置的数组元素的索引
10 * @param {number} prob 概率,用0.**的方式表示,也可以设置0.0**,但是会增大计算量,也更精确
11 */
12 setProb:function(index,prob){
13 this.probs.push({index:index,prob:prob})
14 },
15 /**
16 * 在调用setProb之后调用,初始化数据,之后才可以开始取值
17 */
18 init:function(){
19 var count=0;
20 var prob_arr=[];
21 for(var i in this.probs){
22 prob_arr.push(this.probs[i].index)
23 for(var m=0;m<this.probs[i].prob*100;m++){
24 this.refer_array.push(this.probs[i].index);
25 count++;
26 }
27 }
28 for(var i=count;i<100;i++){
29 var m;
30 while(M.Array.has(prob_arr,(m=Math.floor(Math.random()*this.array.length)))){
31 continue;
32 }
33 this.refer_array.push(m)
34 }
35
36 },
37 /**
38 * 随机取一个
39 */
40 getOne:function(){
41 var i=Math.floor(Math.random()*100);
42 return this.array[this.refer_array[i]];
43 }
44 }
45 var rand=new Random([1,2,3,4,5,6,7,8,9,0]);
46 rand.setProb(1, 0.8)
47 rand.setProb(2,0.1)
48 rand.init();
49 var result_arr=[]//取10000次,统计是否2出现了8000次,3是否出现1000次
50 for(var i=10000;i>0;i--){
51 result_arr.push(rand.getOne())
52 }
53 alert(M.Array.count(result_arr,2));//8000左右
54 alert(M.Array.count(result_arr,3));//1000左右
上面的程序里我定义了一个Random对象,然后实例化了一个rand,测试了一下,设置2出现的概率是0.8,3出现的概率是0.1.
之后得出结果,10000次中2出现的次数总是在8000左右,3出现的次数一直在1000左右.说明程序成功;
原理是什么呢?
这个程序的概率只能精确到100分之一,可以修改一下支持1000分之一等更高分辨率的概率.
在程序里我建立了一个100个元素的参考数组,开始设为空,之后我调用setProb的时候把特殊的设置保存在了this.probs里面,
之后我开始根据this.probs里的数据构建refer_arr这个参考数组,首先我读取了第一个设置概率的元素,把它的概率0.8乘以100 之后得到一个数80,然后把这个元素的索引向参考数组里填充80次,同理再把下一个概率0.1乘以100之后得到一个数10,之后向数组填充10个当前元素的索引,然后再随机在剩余的索引中取10次填充到参考数组里,这时候参考数组就有100个元素了,其中80个值为1,10个值为2,其他是随机的.
然后从这个参考数组里随机取一个元素,取到1的概率就是0.8啦,把这个索引在原来数组中的值返回,就是2啦,2出现的概率是0.8.
就是这样,结果经过测试,数据越多越精确.
这个东西就到这里吧,有兴趣的可以自己去探索,我这个方法虽然能出来结果,但是不一定是最好的方法,慎用!
下面说点数组的小事情:
最近在写框架玩,里面涉及到一些对数组的常用操作,开始我乱写,用prototype扩展了Array.后来发现用for遍历数组的时候,遍历里包含了我自定义的扩展方法,汗啊,终于知道为什么前辈们都说不能这样扩展了,大家一定要注意,千万不要再js里随意扩展原生对象,否则会跟别的程序或者库冲突.
之后我把对数组的操作都抽象出来,合到一个单例对象里了,在这里贴出来吧:
view source
print?
01 M.Array=function(){
02 return {
03 /**
04 *判断某个元素是否存在于数组中,是返回true,否返回false
05 *@param {array} arr 要处理的数组
06 *@param {object} item 元素
07 */
08 has:function(arr,item){
09 for(var i in arr){
10 if(arr[i]==item) return true;
11 }
12 return false;
13 },
14 /**
15 *从数组中移除某个元素
16 *@param {array} arr 要处理的数组
17 *@param {object} item 要移除的元素,注意不是索引
18 */
19 remove:function(arr,item){
20 var j=arr.length;
21 for(var i=0;i<j;i++){
22 if(arr[i]==item){
23 arr.splice(i, 1);
24 j--;
25 }
26 }
27 },
28 /**
29 *对数组中的元素都执行一次func,最后返回处理后的结果
30 *
31 */
32 each:function(arr,func){
33 for(var i in arr){
34 arr[i]=func(arr[i]);
35 }
36 return arr;
37 },
38 /**
39 * 将数组打乱,原理是随机交换两个元素的值,可以设定洗牌次数
40 * @param {array} arr 要处理的数组
41 * @param {number} times 洗牌次数,如果不存在就按数组长度计算
42 */
43 random:function(arr,times){
44 var arr_length=arr.length,index_1,index_2;
45 times=times||arr_length;
46
47 for(var i=0;i<times;i++){
48 index_1=Math.floor(Math.random()*arr_length)
49 while((index_2=Math.floor(Math.random()*arr_length))==index_1){
50 continue;
51 }
52 var temp=arr[index_1];
53 arr[index_1]=arr[index_2]
54 arr[index_2]=temp;
55 }
56 return arr;
57 },
58 toString:function(arr){
59 var return_str="";
60 for(var i in arr){
61 return_str+=i+":"+arr[i]+"\n"
62 }
63 return return_str;
64 },
65 getMax:function(arr){
66 var max=0;
67 for(var i in arr){
68 if(arr[i]*1>max) max=arr[i]*1
69 }
70 return max;
71 },
72 getMin:function(arr){
73 var min=0;
74 for(var i in arr){
75 if(arr[i]*1<min) min=arr[i]*1
76 }
77 return min;
78 },
79 /**
80 * 统计数组中某个元素出现的次数
81 */
82 count:function(arr,item){
83 var count=0;
84 for(var i in arr){
85 if(arr[i]==item){
86 count++;
87 }
88 }
89 return count;
90 }
91 }
92 }();
有哪里写的不好,希望大家多提提意见.
下面再说说数组的一些比较好玩的特性:
首先定义一个数组:
view source
print?
1 var arr1=[1,2,3,4,5,6,7,8,9,0]
alert 一下arr1.length结果是10,alert一下M.Array.toString(arr1) 这是刚才的对象里定义的方法会按照key:value的形式输出数组的所有值,结果输出:0:1 1:2 2:3 3:4 4:5 5:6 6:7 7:8 8:9 9:0
下面我们来设置一下 arr1.length=12看看数组会有什么变化:0:1 1:2 2:3 3:4 4:5 5:6 6:7 7:8 8:9 9:0 数组的值没有变,但是alert(arr1.length)竟然是12.其实这时候数组里有两个undefined的值,在遍历中体现不出来,但是在 length中体现出来了.
下面再操作一下:arr1.push(11);猜猜会输出什么?0:1 1:2 2:3 3:4 4:5 5:6 6:7 7:9 8:0 12:11,索引是从12开始的,虽然undefined的元素不能体现在遍历中,但是他们却的确占着一个数组的位置.
下面再设置一下:arr1.length=5;输出:0:1 1:2 2:3 3:4 4:5
是不是很灵活,由此可以用数组来做一些很灵活的操作,不过我就是觉得好玩,还不知道哪里能用到,这种特性在强类型语言里是绝对看不到的,js真是灵活的一比啊.
时间:2010年3月12日0:29:05,写完了,明天的周会就拿这篇文章来分享了.这几天忙,写个博客都静不下心来了,大家凑合看吧,不懂得或者有问题的可以加qq群:46532005交流.谢谢赏光哈,顺便点个推荐就更好了,哈哈
前几天在公交车上挤车,车上没有美女,于是我就开始想一些js问题,突然我想能不能人工控制js中的随机数的随机概率呢?在某些游戏中,我们故意控制概率来让洗出来的牌更加合理(我们那有种牌叫做保皇,每次地主和狗腿子都到了一个人家里的时候,那个人的牌一般都会特别好,我觉得这是人为设计的)。
说干就干,基本目标就是,给出一个数组,我可以设定其中某个元素被抽中的概率是某个值,之后随机从数组取值的时候会按照这个概率去取,而且可以多个设置。
先上代码吧,这是一个对象:(M和M.Array对象是我框架里的,不是原生对象)
view source
print?
01 var Random=function(arr_da
02 this.array=arr_da
03 this.probs=[];
04 this.refer_array=new Array();
05 }
06 Random.prototype={
07 /**
08 * 设置某个元素被抽中的几率
09 * @param {number} index 要设置的数组元素的索引
10 * @param {number} prob 概率,用0.**的方式表示,也可以设置0.0**,但是会增大计算量,也更精确
11 */
12 setProb:function(index,prob){
13 this.probs.push({index:index,prob:prob})
14 },
15 /**
16 * 在调用setProb之后调用,初始化数据,之后才可以开始取值
17 */
18 init:function(){
19 var count=0;
20 var prob_arr=[];
21 for(var i in this.probs){
22 prob_arr.push(this.probs[i].index)
23 for(var m=0;m<this.probs[i].prob*100;m++){
24 this.refer_array.push(this.probs[i].index);
25 count++;
26 }
27 }
28 for(var i=count;i<100;i++){
29 var m;
30 while(M.Array.has(prob_arr,(m=Math.floor(Math.random()*this.array.length)))){
31 continue;
32 }
33 this.refer_array.push(m)
34 }
35
36 },
37 /**
38 * 随机取一个
39 */
40 getOne:function(){
41 var i=Math.floor(Math.random()*100);
42 return this.array[this.refer_array[i]];
43 }
44 }
45 var rand=new Random([1,2,3,4,5,6,7,8,9,0]);
46 rand.setProb(1, 0.8)
47 rand.setProb(2,0.1)
48 rand.init();
49 var result_arr=[]//取10000次,统计是否2出现了8000次,3是否出现1000次
50 for(var i=10000;i>0;i--){
51 result_arr.push(rand.getOne())
52 }
53 alert(M.Array.count(result_arr,2));//8000左右
54 alert(M.Array.count(result_arr,3));//1000左右
上面的程序里我定义了一个Random对象,然后实例化了一个rand,测试了一下,设置2出现的概率是0.8,3出现的概率是0.1.
之后得出结果,10000次中2出现的次数总是在8000左右,3出现的次数一直在1000左右.说明程序成功;
原理是什么呢?
这个程序的概率只能精确到100分之一,可以修改一下支持1000分之一等更高分辨率的概率.
在程序里我建立了一个100个元素的参考数组,开始设为空,之后我调用setProb的时候把特殊的设置保存在了this.probs里面,
之后我开始根据this.probs里的数据构建refer_arr这个参考数组,首先我读取了第一个设置概率的元素,把它的概率0.8乘以100 之后得到一个数80,然后把这个元素的索引向参考数组里填充80次,同理再把下一个概率0.1乘以100之后得到一个数10,之后向数组填充10个当前元素的索引,然后再随机在剩余的索引中取10次填充到参考数组里,这时候参考数组就有100个元素了,其中80个值为1,10个值为2,其他是随机的.
然后从这个参考数组里随机取一个元素,取到1的概率就是0.8啦,把这个索引在原来数组中的值返回,就是2啦,2出现的概率是0.8.
就是这样,结果经过测试,数据越多越精确.
这个东西就到这里吧,有兴趣的可以自己去探索,我这个方法虽然能出来结果,但是不一定是最好的方法,慎用!
下面说点数组的小事情:
最近在写框架玩,里面涉及到一些对数组的常用操作,开始我乱写,用prototype扩展了Array.后来发现用for遍历数组的时候,遍历里包含了我自定义的扩展方法,汗啊,终于知道为什么前辈们都说不能这样扩展了,大家一定要注意,千万不要再js里随意扩展原生对象,否则会跟别的程序或者库冲突.
之后我把对数组的操作都抽象出来,合到一个单例对象里了,在这里贴出来吧:
view source
print?
01 M.Array=function(){
02 return {
03 /**
04 *判断某个元素是否存在于数组中,是返回true,否返回false
05 *@param {array} arr 要处理的数组
06 *@param {object} item 元素
07 */
08 has:function(arr,item){
09 for(var i in arr){
10 if(arr[i]==item) return true;
11 }
12 return false;
13 },
14 /**
15 *从数组中移除某个元素
16 *@param {array} arr 要处理的数组
17 *@param {object} item 要移除的元素,注意不是索引
18 */
19 remove:function(arr,item){
20 var j=arr.length;
21 for(var i=0;i<j;i++){
22 if(arr[i]==item){
23 arr.splice(i, 1);
24 j--;
25 }
26 }
27 },
28 /**
29 *对数组中的元素都执行一次func,最后返回处理后的结果
30 *
31 */
32 each:function(arr,func){
33 for(var i in arr){
34 arr[i]=func(arr[i]);
35 }
36 return arr;
37 },
38 /**
39 * 将数组打乱,原理是随机交换两个元素的值,可以设定洗牌次数
40 * @param {array} arr 要处理的数组
41 * @param {number} times 洗牌次数,如果不存在就按数组长度计算
42 */
43 random:function(arr,times){
44 var arr_length=arr.length,index_1,index_2;
45 times=times||arr_length;
46
47 for(var i=0;i<times;i++){
48 index_1=Math.floor(Math.random()*arr_length)
49 while((index_2=Math.floor(Math.random()*arr_length))==index_1){
50 continue;
51 }
52 var temp=arr[index_1];
53 arr[index_1]=arr[index_2]
54 arr[index_2]=temp;
55 }
56 return arr;
57 },
58 toString:function(arr){
59 var return_str="";
60 for(var i in arr){
61 return_str+=i+":"+arr[i]+"\n"
62 }
63 return return_str;
64 },
65 getMax:function(arr){
66 var max=0;
67 for(var i in arr){
68 if(arr[i]*1>max) max=arr[i]*1
69 }
70 return max;
71 },
72 getMin:function(arr){
73 var min=0;
74 for(var i in arr){
75 if(arr[i]*1<min) min=arr[i]*1
76 }
77 return min;
78 },
79 /**
80 * 统计数组中某个元素出现的次数
81 */
82 count:function(arr,item){
83 var count=0;
84 for(var i in arr){
85 if(arr[i]==item){
86 count++;
87 }
88 }
89 return count;
90 }
91 }
92 }();
有哪里写的不好,希望大家多提提意见.
下面再说说数组的一些比较好玩的特性:
首先定义一个数组:
view source
print?
1 var arr1=[1,2,3,4,5,6,7,8,9,0]
alert 一下arr1.length结果是10,alert一下M.Array.toString(arr1) 这是刚才的对象里定义的方法会按照key:value的形式输出数组的所有值,结果输出:0:1 1:2 2:3 3:4 4:5 5:6 6:7 7:8 8:9 9:0
下面我们来设置一下 arr1.length=12看看数组会有什么变化:0:1 1:2 2:3 3:4 4:5 5:6 6:7 7:8 8:9 9:0 数组的值没有变,但是alert(arr1.length)竟然是12.其实这时候数组里有两个undefined的值,在遍历中体现不出来,但是在 length中体现出来了.
下面再操作一下:arr1.push(11);猜猜会输出什么?0:1 1:2 2:3 3:4 4:5 5:6 6:7 7:9 8:0 12:11,索引是从12开始的,虽然undefined的元素不能体现在遍历中,但是他们却的确占着一个数组的位置.
下面再设置一下:arr1.length=5;输出:0:1 1:2 2:3 3:4 4:5
是不是很灵活,由此可以用数组来做一些很灵活的操作,不过我就是觉得好玩,还不知道哪里能用到,这种特性在强类型语言里是绝对看不到的,js真是灵活的一比啊.
时间:2010年3月12日0:29:05,写完了,明天的周会就拿这篇文章来分享了.这几天忙,写个博客都静不下心来了,大家凑合看吧,不懂得或者有问题的可以加qq群:46532005交流.谢谢赏光哈,顺便点个推荐就更好了,哈哈