ExcelHome技术论坛

 找回密码
 免费注册

QQ登录

只需一步,快速开始

快捷登录

搜索
EH技术汇-专业的职场技能充电站 妙哉!函数段子手趣味讲函数 Excel服务器-会Excel,做管理系统 Excel Home精品图文教程库
HR薪酬管理数字化实战 Excel 2021函数公式学习大典 Excel数据透视表实战秘技 打造核心竞争力的职场宝典
300集Office 2010微视频教程 数据工作者的案头书 免费直播课集锦 ExcelHome出品 - VBA代码宝免费下载
用ChatGPT与VBA一键搞定Excel WPS表格从入门到精通 Excel VBA经典代码实践指南
楼主: 香川群子

[分享] 小花鹿请进。关于随机不重复抽取 算法之 【数组经典洗牌法】原理

  [复制链接]

TA的精华主题

TA的得分主题

发表于 2012-9-10 15:27 | 显示全部楼层
本帖已被收录到知识树中,索引项:其他结构和算法
本帖最后由 hamtyb 于 2012-9-10 15:27 编辑

这个找了好久了.谢谢LZ分享经验了!!

TA的精华主题

TA的得分主题

发表于 2012-9-10 17:27 | 显示全部楼层

TA的精华主题

TA的得分主题

发表于 2012-9-23 08:25 | 显示全部楼层
正在学习随机不重复的方法,先收藏。

TA的精华主题

TA的得分主题

发表于 2012-9-30 08:29 | 显示全部楼层
又学习了一遍,能够看懂了那个最简单的代码了,真的很不错。这种随机不重复抽取的结果是一个数组,在实际运用中需要自己再加工。

TA的精华主题

TA的得分主题

发表于 2013-1-21 15:03 | 显示全部楼层
[广告] VBA代码宝 - VBA编程加强工具 · VBA代码随查随用  · 内置多项VBA编程加强工具       ★ 免费下载 ★      ★使用手册
香川群子 发表于 2012-6-20 21:06
占楼。下面是【经典数组洗牌法】实际代码功能扩展的代码例子:

一维数组arr,从其中第a个元素开始,到第 ...

香川,我喜欢看你的帖子~
对于本贴9楼中的问题:
一维数组arr,从其中第a个元素开始,到第b个元素为止的区间内,任意抽取n个不重复值返回。
其中,n<=b-a+1  即n可以正好和a-b中元素个数相等,或小于a-b区间中的元素个数。


你给出的洗牌算法是:
  1. Sub GetRnd(arr, a, b, n)
  2.     Randomize
  3.     l = LBound(arr)
  4.     For i = l To n + l - 1 '正序洗牌
  5.         r = Int(Rnd() * (b - a + 1 - (i - l))) + a + (i - l)
  6.         t = arr(r, 1)
  7.         arr(r, 1) = arr(i + a - l, 1)
  8.         arr(i, 1) = t '下标1开始一维代码
  9.     Next
  10. End Sub
复制代码
这里我有点不理解的是,arr(r)为什么要用 arr(i + a - l)填充?
像如下两种写法应该也是满足要求的,你是不是有别的什么用意?
  1. Sub GetRnd(arr, a, b, n)
  2.     Randomize
  3.     s = LBound(arr)
  4.     For i = s To n + s - 1 '正序洗牌
  5.         r = Int(Rnd() * (b - a + 1 - (i - s))) + a + (i - s)
  6.         t = arr(r, 1)
  7.         arr(r, 1) = arr(i, 1)
  8.         arr(i, 1) = t '下标1开始一维代码
  9.     Next
  10. End Sub
复制代码
  1. Sub GetRnd(arr, a, b, n)
  2.     Randomize
  3.     s = LBound(arr)
  4.     For i = 1 To n '正序洗牌
  5.         r = Int(Rnd() * (b - a - i + 1)) + a + i
  6.         t = arr(r + s - 1, 1)
  7.         arr(r + s - 1, 1) = arr(i + s - 1, 1)
  8.         arr(i + s - 1, 1) = t '下标1开始一维代码
  9.     Next
  10. End Sub
复制代码

TA的精华主题

TA的得分主题

 楼主| 发表于 2013-1-21 23:33 | 显示全部楼层
[广告] Excel易用宝 - 提升Excel的操作效率 · Excel / WPS表格插件       ★免费下载 ★       ★ 使用帮助
vbaplus 发表于 2013-1-21 15:03
香川,我喜欢看你的帖子~
对于本贴9楼中的问题:
一维数组arr,从其中第a个元素开始,到第b个元素为止的 ...

关于第一个问题:

你例子中,用arr(i, 1)来代替我代码中的arr(i + a - l, 1) 【你自己的习惯是arr(i + a - s, 1)】

那么,如果a=l或者说a=s的时候,当然两个式子的结果是等价的。

但如果a不是从l或s开始,而是l+2或l+5开始呢?

显然两者就不同了。

………

要点:
我的代码,起始位置可以任意指定。

举例:数组元素1-10,我可以:
  指定a=1、b=10、n=10,即从1-10中随机抽取10个并返回全部10个元素。
或指定a=1、b=10、n=6,即从1-10中随机抽取6个后就结束,后面剩余4个元素不管了。

或指定a=3、b=10、n=4,即从3-10中随机抽取4个后就结束,
那么抽取到的4个数肯定不会含1、2,而且一共还剩余6个元素不用管了。


或指定a=3、b=8、n=4,即从3-8中随机抽取4个后就结束,
那么抽取到的4个数肯定既不会含1、2,也不会含9、10,而且一共还剩余6个元素不用管了。


就是这个意思。

而你改写的代码,就做不到了。



TA的精华主题

TA的得分主题

 楼主| 发表于 2013-1-21 23:47 | 显示全部楼层
vbaplus 发表于 2013-1-21 15:03
香川,我喜欢看你的帖子~
对于本贴9楼中的问题:
一维数组arr,从其中第a个元素开始,到第b个元素为止的 ...

而你的第2段改写代码,错误更大,已经不能正常运行了。

指针完全错误。

TA的精华主题

TA的得分主题

发表于 2013-1-22 20:42 | 显示全部楼层
[广告] Excel易用宝 - 提升Excel的操作效率 · Excel / WPS表格插件       ★免费下载 ★       ★ 使用帮助
香川群子 发表于 2013-1-21 23:47
而你的第2段改写代码,错误更大,已经不能正常运行了。

指针完全错误。

恩,谢谢,香川,我的两段改写应该都是错在没有理解arr(r, 1) = arr(i + a - l, 1)造成的。我的改写错在:我用从arr最开始的位置按顺利取值去填充arr(r,1),而这个随机取值位置还有被再次取值的可能,如果被再次取到的话,那就出现arr最初位置的数据,不符合在a~b位置取值的条件。而你用arr(i + a - l, 1)去填充arr(r, 1) ,也就是在 a~b区间上从第一个位置开始取值去填充arr(r,1)随机取值位,而不是像我那样从arr的最初位置去取值填充,这样做就避免了我刚才犯的错误,你处理得很完美。

经过这次理解,我再重新改写了一下,你看还有没有错误?另外我把你的l改为s是因为l和1有视觉混淆。
  1. Sub GetRnd(arr, a, b, n)
  2.     Randomize
  3.     s = LBound(arr)
  4.     For i = 1 To n '正序洗牌
  5.         r = Int(Rnd() * (b - a - i + 1)) + a + i
  6.         t = arr(r + s - 1, 1)
  7.         arr(r + s - 1, 1) = arr(i + a - 1, 1)
  8.         arr(i + s - 1, 1) = t '下标1开始一维代码
  9.     Next
复制代码

TA的精华主题

TA的得分主题

 楼主| 发表于 2013-1-22 22:41 | 显示全部楼层
vbaplus 发表于 2013-1-22 20:42
恩,谢谢,香川,我的两段改写应该都是错在没有理解arr(r, 1) = arr(i + a - l, 1)造成的。我的改写错在: ...

呵呵,而我就是故意用小写l作为参数,让粗心的人以为是1……

只有真正理解代码的人才不会搞错。

TA的精华主题

TA的得分主题

 楼主| 发表于 2013-1-22 22:44 | 显示全部楼层
[广告] VBA代码宝 - VBA编程加强工具 · VBA代码随查随用  · 内置多项VBA编程加强工具       ★ 免费下载 ★      ★使用手册
vbaplus 发表于 2013-1-22 20:42
恩,谢谢,香川,我的两段改写应该都是错在没有理解arr(r, 1) = arr(i + a - l, 1)造成的。我的改写错在: ...

理论上,如果每次a都比起始位置大,那么交换只要两次就可以了,不用回填到 i + a - l 位置了。

不过,因为实际上偏偏a=起始位置的情形是最容易出现的,所以通用代码就只能牺牲一点速度了。
您需要登录后才可以回帖 登录 | 免费注册

本版积分规则

手机版|关于我们|联系我们|ExcelHome

GMT+8, 2024-5-20 01:26 , Processed in 0.045873 second(s), 7 queries , Gzip On, MemCache On.

Powered by Discuz! X3.4

© 1999-2023 Wooffice Inc.

沪公网安备 31011702000001号 沪ICP备11019229号-2

本论坛言论纯属发表者个人意见,任何违反国家相关法律的言论,本站将协助国家相关部门追究发言者责任!     本站特聘法律顾问:李志群律师

快速回复 返回顶部 返回列表