参考网址:http://www.soulteary.com/2014/07/05/js-math-random-trick.html

[JS]Math.random()的二三事

看到题目,如果大家平时被问到:如何生成一个怎么样怎么样的整数随机数,估计大家都会不屑,但是当你淡定的回答获取一个范围应该是随机数seeds和区间数值差的乘机与最小数相加然后再怎么怎么的时候…有没有发现你的思维已经固化了呢。

这个知识点应该是玩JS肯定会碰到的之一吧。文末有Markdown,可以直接下载阅读,清爽一点。

先来掉书袋,看看MDN的文档

打开Node,进入终端命令行模式,输入Math.random():

JavaScript
 
1
2
3
>Math.random()
 
0.436846193857491

结果是不是依旧如同往常一样稀松平常的小于1的一个伪随机数跳了出来呢。 这个时候,如果别人问你,还有什么其他方案可以生成随机数么,你会想到神马呢。

逝者如斯夫,不舍昼夜。

如果你继续在终端里输入new Date()-0:

 
 
1
2
3
>newDate()-0
 
1404488829907

我想你可以得到一个自增的数字,对,就是“秒”,如果你说这货哪里随机了,请别着急:

JavaScript
 
1
2
3
>(newDate()-0)%10086
 
8657

这里的取模%的数值可以是大于2且最好小于当前时间的数值,则可以得到你取模数值概率分之一的概率的随机数。

如果你取模的数值是随机数呢,那么产生这个随机数的可见的两个变量都是随机的,那么是不是近似真的“随机数”了呢?

当然,如果使用这招,还要考虑到硬件以及语言执行过程的耗时,因为我们知道计算机执行的时候,有一个时间的精度的范畴,所以需要使用一点点的延时抑制。

扯了一些没用的,你可能着急了,那么请保持好奇心,我们继续说点无聊的事情。

Math.random会提供给我们一个[0,1)之间的随机数,但是如果我们要[1,10]范围随机整数的话,可以使用以下三个函数:

  • Math.round
  • Math.ceil
  • Math.floor

我们先来生成一个随机数:

JavaScript
 
1
2
3
>Math.random()*(10-1)+1
 
8.26644050120376

接着我们来使用这三个Math内建函数:

JavaScript
 
1
2
3
4
5
6
7
8
9
10
11
>Math.round(8.26644050120376)
 
8
 
>Math.ceil(8.26644050120376)
 
9
 
>Math.floor(8.26644050120376)
 
8

把数值换成8.56644050120376后,再来看看:

 
 
1
2
3
4
5
6
7
8
9
10
11
>Math.round(8.56644050120376)
 
9
 
>Math.ceil(8.56644050120376)
 
9
 
>Math.floor(8.56644050120376)
 
8

所以区别一目了然,对于浮点数,round会遵守四舍五入规则,ceil无论如何贪心进位+1,floor无论如何都小心翼翼的自断一臂-1,至于整数,自己试试看咯。

说到这里,接下来可以正常的描述内容了:

问:如何快速生成一段随机文本,比如验证码或者我们访问网站常见的随机数token。

答案很多,我说一个经典的,其实思路很简单,把刚刚生成随机数的方法随便选择一个.toString():

JavaScript
 
1
Math.random().toString(36).substring(7);

//当然也可以写成这样

JavaScript
 
1
Math.random().toString(36).slice(2);

//或者利用时间

JavaScript
 
1
(newDate()-0).toString(36)

随便输出一些,我们可以看到这货输出的字符串长短参次不齐的:

JavaScript
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
mptzulnb3xr
 
87jx7vkuik9
 
761qsolayvi
 
amqx2mx6r
 
ce5uyvkuik9
 
5ioufim5cdi
 
dirp4hiwwmi
 
ioe597ldi
 
ohn9izfr
 
sprsakk2o6r
 
5g3ruo6flxr
 
...
 
//单纯时间来做随机是不是生成的惨不忍睹
 
//而且不做随机延时抑制,重复太明显
 
hx7pom3y
 
hx7pom3z
 
hx7pom40
 
hx7pom41
 
hx7pom42
 
hx7pom43
 
hx7pom44
 
hx7pom45

我们先来看看为什么用toString()可以生成随机数: 首先前面的家伙不管是随机数seeds生成的,还是时间递增的长整型,它们都是Number构造器构造出来的[object Number],而在ecma.js中,Number的这个方法是这样的:

JavaScript
 
1
2
3
4
5
6
7
8
9
/**
 
@param {Number} [radix]
 
@return {string}
 
*/
 
Number.prototype.toString=function(radix){};

作为一个好人,我给你指条明路,MDN的文档

看过之后是不是想到了parseInt函数的第二个参数?我们发现,这个原生函数支持2~36(如果超出36,那么26个字母就不够用了亲)进制的转换,所以如果在生成的时候,随机切换进制,取结果的随机位置,效果会不会更好呢,你可以试一试。

如果你想获得字母多一点且平均一点,那么只有使用36进制了,但是不管怎么躲,都有可能出现一串数字。

 
 
1
xjuk0zxofen1xlxr

有没有好方法来解决这个问题呢,答:

 
 
1
Math.random().toString(36).replace(/[^a-z]+/g,'')
 
 
1
2
3
//或者这样
 
Math.random().toString(36).slice(2).replace(/\d/g,'')

这个时候,你或许会说,文章该就此结束了吧,这个方法看起来很爽很简洁。 不过,你有思考过一个问题么,回顾前文,随机数可以是0,1,…这些整数…

当随机数是这些数值的时候,很抱歉,返回值是原来的数值,即0,1,…,我们得到的最后的结果就会是一个空字符串,而如果是0.5这类某些以5结尾的浮点数的时候,结果依然如此。还有当数值是某些时候,生成的随机数位数会比较短…

解决这个问题,你当然可以重新生成这个随机数,直到它输出一个你心满意足的随机数再放过他,但是,我们刚刚了解到的生成一个大的随机数的方法是依赖时间,小学还是初中学过的用一个比较小的数值除以一个比较大的数值,得到的结果是一个更小的浮点数来解决这个问题呢。

 
 
1
(Math.random()/+newDate()).toString(36).replace(/\d/g,'').slice(1)

这样就得到了一个比较长,且比较公平的随机数。可以用node验证一下:

 
 
1
varc={},r;for(vari=0,j=10000000;i<j;i++){r=(Math.random()/+newDate()).toString(36).replace(/\d/g,'').slice(1);c[r]?c[r]+=1:c[r]=1;}for(variinc){if(c[i]===1){deletec[i];}}console.log(c);

运行结果是没有任何冲突,当然这可能是小概率事件。如果你觉得你点很背,你可以试一试下面这段,手动执行几次,看看,有没有不是空数组这个结果的结果。

 
 
1
for(vari=0,j=100;i<j;i++){(function(){varc={},r;for(vari=0,j=1000;i<j;i++){r=(Math.random()/+newDate()).toString(36).replace(/\d/g,'').slice(1);c[r]?c[r]+=1:c[r]=1;}for(variinc){if(c[i]===1){deletec[i];}}console.log(c);}())}

当然,你也可以不用这两个测试例子上面的那段代码,改用下面这种方式,多输出几次随机字符串,然后拼合在一起。

 
 
1
for(varc='';c.length<5;)c+=Math.random().toString(36).substr(2)

这个把戏估计你看腻了,我们来看下面这个系列的示例,从固定的字典中抽取字符构成随机字符串:

依赖Array的map方法,要注意兼容性,当然,你从MDN那边copy一段hacks也可以无缝兼容,是不是看起来高大上一点:

 
 
1
2
3
4
5
6
7
8
9
Array.apply(0,Array(5)).map(function(){
 
  return(function(charset){
 
    returncharset.charAt(Math.floor(Math.random()*charset.length))
 
  }('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'));
 
}).join('');

不过或许你更容易接受这种多一点:

 
 
1
2
3
4
5
6
7
functionrand(length,current){
 
  current=current?current:'';
 
  returnlength?rand(--length,"0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz".charAt(Math.floor(Math.random()*60))+current):current;
 
}

或者更传统的:

 
 
1
2
3
4
5
6
7
8
9
10
11
functionrand(){
 
  vartext="";
 
  varpossible="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
 
  for(vari=0;i<5;i++)text+=possible.charAt(Math.floor(Math.random()*possible.length));
 
  returntext;
 
}

或许你以为这样把戏就玩完了,too young too simple,之前玩过了Number的原生函数,我们还可以动手脚的是String的原生函数,比如我们知道,字母的ASCII码是[65,97],那么当这个区间是字典,我们随机抽取这个区间是不是会有一些好玩的事情发生呢。

 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
functionrand(x){
 
  vars="";
 
  while(s.length<x&&x>0){
 
    varr=Math.random();
 
    s+=String.fromCharCode(Math.floor(r *26)+(r>0.5?97:65));
 
  }
 
  returns;
 
}

随便输入一个电话号码(推荐输入短一点的数),然后等等看,是不是会出现下面的结果:

 
 
1
JrxKnxrHwJzGCKoKCzrpFxMxDBFqBrpAEGvpvMtCIHIHFCxtMxrHtpEGDyxzxwMBBqDvEBwxprwHqDMCErIzLwuyFApnxpxJoxBAFEoHsAEqvIxBIJvpFBoLnvurHJsvDFwGtFvDsELMLwzowvqBJtCwrGCsCHGvsxCLunGxtrtnyvvwyFqEsstotxnsrqLHAIyCxzLxDqtuzsoFJAGHyxrwxJusJrtpuvIyIILoJrGLKnptqHLBAKwGEpnIwzCtFAnrHIqLHynGwuyupsDpLJFGxBuJBouwCDGKsKFCLKnAzoupsxFqIynKFCoBApyBsJKzJpKwEFCyGywrBoFpvorMzBrBAFowrKxvuJLoKtzpEoDsEsBExyGBMssnADnBwrvJDGunJsMyFGCxIFApEnoLyGxBrHroBsLAICGLDIwvqp

这个话题,好像说的有点麻木了,换个需求吧。有的网站会玩一些随机背景色的小花样。有没有优雅的解决方案呢:

遵守随机数函数的定义,获取颜色数值之间的数值就好,看过了上面的代码,这句,很好懂了伐:

 
 
1
'#'+(0x1000000+(Math.random())*0xffffff).toString(16).substr(1,6)

当然,你可以写一个更加直观的方案:

 
 
1
2
3
4
5
6
7
8
9
10
11
//用十进制数据来代替16进制数据运算,请注意因为右边是开区间,要比上面的0xffffff +1,最后将结果转换回十六进制,然后修剪一下字符串,输出
 
Math.floor(Math.random()*16777216).toString(16);
 
'#000000'.slice(0,-color.length)+color;
 
 
 
//或者更简短的样子如下
 
"#"+("000000"+Math.floor(Math.random()*16777216).toString(16)).substr(-6);

还有一类花样,如下:

 
 
1
2
3
4
5
6
7
functionrandomColor(){
 
  varr=function(){returnMath.floor(Math.random()*256)};
 
  return"rgb("+r()+","+r()+","+r()+")";
 
}

输出如下:

 
 
1
rgb(29,236,191)

时间不早了,最后说一下随机排序数组吧。

关于洗牌算法,网上流传很多,随便选择一种模拟一下就好,比如随便写的全重排:

 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
vari=0,data=[],r;
 
for(;i<10;data[i++]=i);
 
while(--i){
 
  r=Math.round(Math.random()*9+1)-1;
 
  data[i]=data[i]+data[r],data[r]=data[i]-data[r],data[i]=data[i]-data[r];
 
}
 
console.log(data)

或者利用Array.prototype.sort()函数,这里可以不把里面的数值带进来运算。

首先Math.random()会生成一个[0,1)之间的数值,用0.5这个比较公平的数值减去它,概率得到小于0,等于0,大于0三种状况,而Array.prototype.sort()期待的数值恰好是[-1,0,1],是不是很省事。

 
 
1
2
3
4
5
6
7
8
9
vari=0,data=[],r;
 
for(;i<10;data[i++]=i);
 
data.sort(function(){
 
  return.5-Math.random();
 
});

[JS]Math.random()的更多相关文章

  1. 使用js Math.random()函数生成n到m间的随机数字

    何使用js生成n到m间的随机数字,主要目的是为后期的js生成验证码做准备,Math.random()函数返回0和1之间的伪随机数   摘要: 本文讲解如何使用js生成n到m间的随机数字,主要目的是为后 ...

  2. php对应js math.random

    <?php function random($min = 0, $max = 1) {     return $min + mt_rand()/mt_getrandmax()*($max-$mi ...

  3. js生成[n,m]的随机数,js如何生成随机数,javascript随机数Math.random()

    一.预备知识 Math.ceil();  //向上取整. Math.floor();  //向下取整. Math.round();  //四舍五入. Math.random();  //0.0 ~ 1 ...

  4. js 获取随机数 Math.random()

    js 获取随机数 Math.random() // 结果为0-1间的一个随机数(包括0,不包括1) var randomNum1 = Math.random(); //console.log(rand ...

  5. js进阶解决浏览器缓存不能自动更新的问题(在ajax的url上带上一个参数,可以是日期,或者是随机数)(随机数Math.random)(取得日期的毫秒数:new Date().getTime();)

    js进阶解决浏览器缓存不能自动更新的问题(在ajax的url上带上一个参数,可以是日期,或者是随机数)(随机数Math.random)(取得日期的毫秒数:new Date().getTime();) ...

  6. js中Math.random()生成指定范围数值的随机数

    http://www.111cn.net/wy/js-ajax/57062.htm Math.random() 这个方法相信大家都知道,是用来生成随机数的.不过一般的参考手册时却没有说明如何用这个方法 ...

  7. JS实现使用Math.random()函数生成n到m间的随机数字

    Math.random()函数返回0和1之间的伪随机数,可能为0,但总是小于1,[0,1) 生成n-m,包含n但不包含m的整数: 第一步算出 m-n的值,假设等于w 第二步Math.random()w ...

  8. JS中Math.random()的使用和扩展

    Math.random()方法返回大于等于 0 小于 1 的一个随机数.对于某些站点来说,这个方法非常实用,因为可以利用它来随机显示一些名人名言和新闻事件. 在连续整数中取得一个随机数 值 = Mat ...

  9. Chrome V8引擎系列随笔 (1):Math.Random()函数概览

    先让大家来看一幅图,这幅图是V8引擎4.7版本和4.9版本Math.Random()函数的值的分布图,我可以这么理解 .从下图中,也许你会认为这是个二维码?其实这幅图告诉我们一个道理,第二张图的点的分 ...

随机推荐

  1. jQuery实现移动端评测问卷功能

    效果图: 需求: 1.有10道测试题目,单选,选中答案之后,500ms后自动跳转至下一题 2.如果当前题目没有选择答案,将弹窗提示"请选择答案!" 3.点击"上一题&qu ...

  2. 【转】 CSS十问——好奇心+刨根问底=CSSer

    最近有时间,想把酝酿的几篇博客都写出来,今天前端小学生带着10个问题,跟大家分享一下学习CSS的一些体会,我觉得想学好CSS,必须保持一颗好奇心和刨根问底的劲头,而不是复制粘贴,得过且过.本人能力有限 ...

  3. InnoDB锁

    共享锁和排它锁 InnoDB实现了标准的行级锁,包括两种类型:共享锁(S)和排它锁(X) 一个共享锁(S)允许事务持有这种锁来读取一行 一个排它锁(X)允许事务持有这种锁来修改或删除一行 如果事务T1 ...

  4. vb实验7-找出小于18000的最大素数

    vb实验7-找出小于18000的最大素数 vb实验7-找出小于18000的最大素数 ---–写给女朋友的题解 在窗体上画一个文本框,名称为TEXT1,两个命令按钮,C1和 C2,标题分别为" ...

  5. 掌握一门语言Go

    摘要:Go语言的优势不必多说,通过本篇文章,让我们花时间来掌握一门外语,Let's Go! 关键字:Go语言,闭包,基本语法,函数与方法,指针,slice,defer,channel,goroutin ...

  6. 【Splay】bzoj1500(听说此题多码上几遍就能不惧任何平衡树题)

    1500: [NOI2005]维修数列 Time Limit: 10 Sec  Memory Limit: 64 MBSubmit: 15112  Solved: 4996[Submit][Statu ...

  7. Windows程序设计学习笔记(四)自绘控件与贴图的实现

    Windows系统提供大量的控件供我们使用,但是系统提供的控件样式都是统一的,不管什么东西看久了自然会厌烦,为了使界面更加美观,添加一些新的东西我们需要自己绘制控件. 控件在默认情况下并不进行自绘,如 ...

  8. centOS7-配置网络地址

    1.首先需求切换都root账户 # su #输入密码: 2.进入网络配置文件目录查看 ()进入该目录 # /etc/sysconfig/network-scripts ()查看目录下文件 # ll - ...

  9. Python 集合 深浅copy

    一,集合. 集合是无序的,不重复的数据集合,它里面的元素是可哈希的(不可变类型),但是集合本身是不可哈希(所以集合做不了字典的键)的.以下是集合最重要的两点: 去重,把一个列表变成集合,就自动去重了. ...

  10. TLD算法概述--学习理解之(一)

    liuyihai@126.com http://www.cnblogs.com/liuyihai/ TLD(Tracking-Learning-Detection)是英国萨里大学的一个捷克籍博士生Zd ...