在我们的实际开发工作中,经常都会使用到数组随机选取的功能需求,比如彩票随机一注,随机五注,机动车号牌随机选一个等等。

接下来的内容,来给大家分享一下,我们在开发过程中,常用到的4种数组随机选取实现方案,这些实现方案都是在实战中应用的,非常值得大家学习和收藏,我们一起来看看都有哪些方法吧!

1 使用Math.random()

这种方式是使用Array.sort()和Math.random()结合的方法,Math.random()返回的是一个0-1之间(不包括1)的伪随机数,注意这不是真正的随机数。

var letter = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'];
function shuffle1(arr) {return arr.sort(() => 0.5 - Math.random())
}
console.time("shuffle1");
letter = shuffle1(letter);
console.timeEnd("shuffle1");
console.log(letter);

这种方式并不是真正的随机,来看下面的例子。对这个10个字母数组排序1000次,假设这个排序是随机的话,字母a在排序后的数组中每个位置出现的位置应该是1000/10=100,或者说接近100次。看下面的测试代码:

let n = 1000;
let count = (new Array(10)).fill(0);
for (let i = 0; i < n; i++) {let letter = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'];letter.sort(() => Math.random() - 0.5);count[letter.indexOf('a')]++
}
console.log(count); 

结果如下:

可以看出元素a的位置在0到9出现的次数并不是接近100的。

原因有两点:

  1. Math.random()方法产生的伪随机数并不是在0到1之间均匀分布,不能提供像密码一样安全的随机数字。
  2. Array.prototype.sort(compareFunction)方法中的compareFunction(a, b)回调必须总是对相同的输入返回相同的比较结果,否则排序的结果将是不确定的。

这里sort(() => 0.5 - Math.random())没有输入,跟谈不上返回相同的结果,所以这个方法返回的结果不是真正的数组中的随机元素。

2 随机值排序

既然(a, b) => Math.random() - 0.5 的问题是不能保证针对同一组 a、b 每次返回的值相同,那么我们不妨将数组元素改造一下,比如将元素'a'改造为{ value: 'a', range: Math.random() },数组变成[{ value: 'a', range: 0.10497314648454847 }, { value: 'b', range: 0.6497386423992171 }, ...],比较的时候用这个range值进行比较,这样就满足了Array.sort()的比较条件。

代码如下:

let letter = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'];
function shuffle2(arr) {let new_arr = arr.map(i => ({value: i, range: Math.random()}));new_arr.sort((a, b) => a.r - b.r);arr.splice(0, arr.length, ...new_arr.map(i => i.value));
}
console.time("shuffle2");
letter = shuffle2(letter);
console.timeEnd("shuffle2");
console.log(shuffle2); 

输出结果如下:

我们再使用上面的方式测试一下,看看元素a元素是不是随机分布的。

let n = 1000, count = (new Array(10)).fill(0);
for (let i = 0; i < n; i++) {let letter = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'];letter = shuffle2(letter)count[letter.indexOf('a')]++
}
console.log(count); 

结果如下:

从这里可以看出,元素a在位置0到9出现的次数是接近100的,也就是说元素a是随机分布的,其他的元素也是,这时再从这个新数组中截取前几个元素就是想要的数组了。

3 洗牌算法

上面的sort算法,虽然满足了随机性的需求,但是性能上并不是很好,很明显为了达到随机目的把简单数组变成了对象数组,最后又从排序后的数组中获取这个随机数组,明显走了一些弯路。

洗牌算法可以解决随机性问题,洗牌算法的步骤如下:

  1. 数组arr,有n个元素,存放从1到n的数值;
  2. 生成一个从0到n-1的随机数x;
  3. 输出arr下标为x的元素,即第一个随机数;
  4. 将arr的尾元素和下标为x的元素值互换;
  5. 同步骤2,生成一个从0到n-2的随机数x;
  6. 输出arr下标为x的数组,第二个随机数;
  7. 将arr倒数第二个元素和下标为x的元素互换;
  8. 重复执行直至输出m个数为止;

洗牌算法是真的随机的吗,换言之洗牌算法真的可以随机得到n个元素中m个吗?下面拿一个只有5个元素的数组来说明。

数组有5个元素,如下图。

从5个元素随机抽出一个元素和最后一个换位,假设抽到3,概率是1/5,如下图。注意其他任意4个元素未被抽到的概率是4/5。最终3出现在最后一位的概率是1/5。

将抽到的3和最后一位的5互换位置,最后一位3就确定了,如下图:

再从前面不确定的4个元素随机抽一个,这里注意要先考虑这4个元素在第一次未被抽到的概率是4/5,再考虑本次抽到的概率是1/4,然后乘一下得到1/5。注意其他任意3个未被抽到的概率是3/4。5出现在倒数第二位的概率是4/5*1/4=1/5如下图:

现在最后2个元素确定了,从剩下的3个元素中任意抽取一个,概率是1/3,身下任意2个未被抽到的概率是2/3,但是要考虑上一次未被抽到的概率是3/4,以及上上一次未被抽到的概率是4/5,于是最终1出现在倒数第三位的概率是1/3*3/4*4/5=1/5。

现在倒数3个元素已经确定,剩下的2个元素中任意取一个,概率是1/2,但是要考虑上一次未被抽到的概率是2/3,上上一次未被抽到的概率是3/4,上上上一次未被抽到的概率是4/5,最终4出现在倒数第4位的概率是1/2*2/3*3/4*4/5=1/5。

最后还剩下一个2,它出现在倒数第5位的概率肯定也是1/5。不嫌啰嗦的话可以继续看下去。

现在倒数4个元素已经确定,剩下1个元素中任意取一个,概率是1,但要考虑上一次未被抽中的概率是1/2,上上一次未被抽中的概率是2/3,上上上一次未被抽中的概率是3/4,上上上上一次未被抽中的概率是4/5,于是2出现在倒数5位置的概率是1*1/2*2/3*3/4*4/5=1/5。

有了算法,下面给出洗牌算法的代码:

let letter = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'];
function shuffle3(arr) {let i = arr.length, t, j;while (i) {j = Math.floor(Math.random() * (i--)); //t = arr[i];arr[i] = arr[j];arr[j] = t;}
}
console.time("shuffle3");
shuffle3(letter);
console.timeEnd("shuffle3");
console.log(letter) 

运行结果如下:

还有最后一个问题,我们来验证一下,还是和上面的方法一样,随机排序1000次,看看字母a出现在0-9个位置的概率是多少,理论上应该是1000/10=100。

来看下面的代码:

let n = 1000;
let count = (new Array(10)).fill(0);
function shuffle3(arr) {let i = arr.length, t, j;while (i) {j = Math.floor(Math.random() * (i--)); //t = arr[i];arr[i] = arr[j];arr[j] = t;}
}
for (let i = 0; i < n; i++) {let letter = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'];shuffle3(letter);count[letter.indexOf('a')]++
}
console.log(count); 

结果如下:

可以看到基本上都是接近100的,可以说明洗牌算法是随机的。

4 机选彩票

最近开发做了一个模拟彩票游戏的功能,彩票有机选,其实就是随机选取,下面以双色球为例来看看实现的效果是什么样的。

双色球前区从1到33个小球,后区从1到16个小球,一注彩票中前区至少选6个,后区至少选1个。

这里使用洗牌算法实现,如下图:

JavaScript4种数组随机选取实战源码相关推荐

  1. Spring容器中获取Bean实例的七种方式(附实战源码)

    目录 写作说明 一:写作原因 二:源码出处 实现方式 一:使用BeanFactory直接获取(不推荐) 二:在初始化时保存ApplicationContext对象 三:继承自抽象类Applicatio ...

  2. java的数组与Arrays类源码详解

    java的数组与Arrays类源码详解 java.util.Arrays 类是 JDK 提供的一个工具类,用来处理数组的各种方法,而且每个方法基本上都是静态方法,能直接通过类名Arrays调用. 类的 ...

  3. 【实战+源码】基于RGB-D(深度视觉)的具有机械臂抓取功能的自主规划移动服务机器人的设计与实现(一)——准备工作

    目录 一,实物or仿真 1,实物或仿真的利弊 2,从哪些角度去考虑是选择实物还是仿真 二,环境准备 1,首推ROS 2,其他环境 三,理论学习 四,实物搭建 一,实物or仿真 我想这个问题是在开发之前 ...

  4. 【Android校招】六家大厂一起发难,android物联网开发从入门到实战源码

    2.kotlin相比于Java好处是什么?kotlin对空指针异常是如何处理的?let.with.apply的适用场景? 3.Java异常机制.Error能捕获到吗? 4.我项目中账号密码是保存在本地 ...

  5. HTML在线颜色代码选取器源码

    简介: HTML在线颜色代码选取器源码 前端设计工具HTML在线颜色代码选取器源码,一共19种颜色不同灰度选取,鼠标点击即可实现复制颜色代码,可本地使用也可上传在线当小工具使用! 网盘下载地址: ht ...

  6. 写给小白的机器学习之决策树算法详解(附实战源码)

    这里是实战源码,里面算法参数解释和数据可视化详解 GitHub慢的话看码云 具体ppt也已上传至csdn和GitHub 可以做分类树和回归树 现在是一个多分类任务 PPT讲解 强壮性是对若有缺失值等其 ...

  7. C++实现array right rotation数组右旋转(附完整源码)

    C++实现array right rotation数组右旋转算法 C++实现array right rotation数组右旋转算法完整源码(定义,实现,main函数测试) C++实现array rig ...

  8. php+api抖音随机播放视频源码

    简介: php+api抖音随机播放视频源码,前端纯静态html+css,数据采用php+API接口调用第三方数据,不需要配置环境,上传即用. 源码体积小,视频采集自网络,无视频资源,内置多条播放线路. ...

  9. 线程的3种实现方式并深入源码简单分析实现原理

    前言 本文介绍下线程的3种实现方式并深入源码简单的阐述下原理 三种实现方式 Thread Runnable Callable&Future 深入源码简单刨析 Thread Thread类实现了 ...

最新文章

  1. 利用文件锁控制程序的执行
  2. python缩进说法_【多选题】关于Python程序中与“缩进”有关的说法中,以下选项中错误的是()。...
  3. android textview获取背景颜色,android – 从textview获取背景颜色而不使用ColorDrawable(API 11)...
  4. android多个网络请求如何依次执行,Android 并发和串行网络请求
  5. 系统学习深度学习(四十三)--GAN简单了解
  6. jquery 删除数组元素
  7. wps表格在拟合曲线找点_用excel寻找拟合曲线上的某一点的使用方法
  8. 大数据技术综合分析!数据采集与预处理
  9. c语言 iostream,C语言 我应该在哪里使用iostream类?
  10. pdf拆分成一页一页
  11. 冰点还原精灵有什么作用
  12. 转行成为数据分析师的经验分享
  13. 1284. Minimum Number of Flips to Convert Binary Matrix to Zero Matrix
  14. c 语言编程字谜,字谜游戏(a)C语言
  15. 【Linux 内核】实时调度类 ④ ( 实时运行队列 rt_rq 源码分析 | 实时运行队列 rt_rq 结构体字段分析 | active、rt_nr_running、curr、next 字段 )
  16. 在线教育的优势强调实时直播、实时互动
  17. Unity 预定义标签
  18. [CF936B]Sleepy Game
  19. JavaScript 基础(002_Event Bubbling)
  20. M洞察|“MOBA”还是“MMO”?2023上半年热门手游大盘点来了,拯救你的游戏荒

热门文章

  1. 《ICASSP 2021 DEEP NOISE SUPPRESSION CHALLENGE: DECOUPLING MAGNITUDE ANDPHASE OPTIMIZATION WITH A TW》
  2. PDF 原理及 Python 调用
  3. c语言整形符号位_C语言整型数据(整数)
  4. IM软件私有化方案和云方案有什么区别
  5. 华为AI盘古大模型研究框架
  6. java小游戏之扑克牌
  7. 如何申请163邮箱账号,用好电子邮箱办公效率提升N+
  8. 给图片加图片边框 图片边框要求为PNG格式
  9. java 设计模式之三-模版模式
  10. 曲面细分着色器与几何着色器