ExcelHome技术论坛

 找回密码
 免费注册

QQ登录

只需一步,快速开始

快捷登录

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

[原创] 生成不重复随机数的一段代码

  [复制链接]

TA的精华主题

TA的得分主题

发表于 2011-1-23 17:28 | 显示全部楼层
[广告] Excel易用宝 - 提升Excel的操作效率 · Excel / WPS表格插件       ★免费下载 ★       ★ 使用帮助
原帖由 灰袍法师 于 2011-1-23 16:10 发表


看完,这个跳蚤算法其实是一个简化的洗牌算法,而且你们在该链接也讨论过,对极大的数据范围无效

我也建议你看一下《计算机编程的艺术》第二部,伪随机数生成算法章节,洗牌算法。

洗牌算法可以避免要求数 ...

的确,适用的才是最好的。。。。

TA的精华主题

TA的得分主题

发表于 2011-1-23 20:51 | 显示全部楼层
[广告] VBA代码宝 - VBA编程加强工具 · VBA代码随查随用  · 内置多项VBA编程加强工具       ★ 免费下载 ★      ★使用手册
非常强大,大批量生成密码时能用到,谢谢。

TA的精华主题

TA的得分主题

 楼主| 发表于 2011-1-23 23:57 | 显示全部楼层
灰袍法师真是高人。小生佩服。

我发现一个问题,就是我发现我按照灰袍法师的方法改过后的算法,从1到1000万中选65000个数据,只需要0.59秒,但是如果从1到1亿里面选取65000个数的时候,却需要131秒。虽然只多了10倍,时间却差了很多。这是为什么呢

TA的精华主题

TA的得分主题

发表于 2011-1-24 02:14 | 显示全部楼层
[广告] VBA代码宝 - VBA编程加强工具 · VBA代码随查随用  · 内置多项VBA编程加强工具       ★ 免费下载 ★      ★使用手册
哦,这个是字典对象的一个奇怪的缺点,数值型的key,大小超过一千万的话,查询速度暴降几十倍。原因未明

Zamyi筒子可能是第一个发现这个问题
http://club.excelhome.net/viewth ... ;highlight=%2Bzamyi

解决办法是:用cstr(key) 来强制字符串类型访问字典,或者自己写一个哈希查找来代替字典对象
(巨牛逼,哈希表就是VBA字典的老祖宗,在千万记录的规模下,速度比VBA字典快几百倍,不过实现也很麻烦就是了)
http://club.excelhome.net/viewth ... e%3D1&frombbs=1

VBA字典另一个怪毛病是:key的数量超过10万,速度也会暴降10倍以上,如果是一个500万key的字典,那么死机。。。。。。
解决办法是:把500万key分别放在50个字典,每个最多10万key,每次查询最多分别查询50次。。。。。。
或者,还是用自己设计的哈希表

回到本题,我发现如果把本帖问题看作两部分

1 抽样,从m个可能的值选取n个,有个抽样算法可以做到 等可能性 抽取n个值,而且不需要检查重复,我再研究研究。

2 洗牌,把这n个值随机排列,也就是lsftest筒子在上面提到的算法,速度快那是一定的,O(n)而已
(应该叫洗牌算法,1975年某牛人首先提出来的,小仙妹即使是自行发现此算法,也不能把它命名为跳蚤算法了。。。。。。)

那么就可以完全不做任何检测重复的操作了,这就避开了VBA字典对象的怪毛病,也不用自己设计哈希表了。

TA的精华主题

TA的得分主题

发表于 2011-1-24 09:34 | 显示全部楼层
[广告] VBA代码宝 - VBA编程加强工具 · VBA代码随查随用  · 内置多项VBA编程加强工具       ★ 免费下载 ★      ★使用手册

TA的精华主题

TA的得分主题

发表于 2011-1-24 09:58 | 显示全部楼层
[广告] Excel易用宝 - 提升Excel的操作效率 · Excel / WPS表格插件       ★免费下载 ★       ★ 使用帮助
以上各位的方法都不算快,我的原理是:从a(min,max)取出一个,把最后一个放出取出的那个,a变成a(min,max-1)个,一直到取得n个为止,总共二个循环,一个max-min+1次,一个n次。对于极端,100W取999999个,也不会效率降低,当然对于100W取百几个不会很快。

TA的精华主题

TA的得分主题

发表于 2011-1-24 11:39 | 显示全部楼层
原帖由 Zamyi 于 2011-1-24 09:58 发表
以上各位的方法都不算快,我的原理是:从a(min,max)取出一个,把最后一个放出取出的那个,a变成a(min,max-1)个,一直到取得n个为止,总共二个循环,一个max-min+1次,一个n次。对于极端,100W取999999个,也不会效率 ...

看不出快在哪里。。。而且会把原数组的数据破坏。。
最大的问题在于,当max》》》》》》》min的时候,光这个a(min,max)就杯具了。。
法无定法,还是具体问题具体分析好。

[ 本帖最后由 lsftest 于 2011-1-24 11:47 编辑 ]

TA的精华主题

TA的得分主题

发表于 2011-1-24 11:55 | 显示全部楼层
[广告] Excel易用宝 - 提升Excel的操作效率 · Excel / WPS表格插件       ★免费下载 ★       ★ 使用帮助
'还是跳蚤算法(same as Zamyi)好。字典多了就慢了,以下代码在原贴上仅供参考。

Option Explicit

Sub DictionaryRnd()
    Dim i As Long, dic
    Dim min As Long, max As Long, areaNum As Long
    Dim t
   
    t = Timer
    max = range("d2").Value
    min = range("d1").Value

    Set dic = CreateObject("scripting.dictionary")
    areaNum = max - min
    Do Until dic.Count = range("d3").Value
        i = Int(Rnd * areaNum) + min
        dic(i) = dic(i)
    Loop
   
    [d4] = Timer - t
    range("A:A").Clear
    range("a1").Resize(range("d3").Value) = Application.Transpose(dic.keys)
    Set dic = Nothing
End Sub

Sub ArrayRnd()
    Dim arr, brr, i, j
    Dim min As Long, max As Long
    Dim tmpRnd As Long, temp As Long
    Dim t
   
    t = Timer
    max = range("d2").Value
    min = range("d1").Value
   
    ReDim arr(max - min)
    For i = min To max
        arr(i - min) = i
    Next i
   
    Randomize Timer
    ReDim brr(1 To  range("d3").Value,1 To 1)
    j = UBound(arr)
    For i = 1 To range("d3").Value
        tmpRnd = Int(Rnd * j)
        brr(i, 1) = arr(tmpRnd)
        temp = arr(tmpRnd)
        arr(tmpRnd) = arr(j)
        arr(j) = temp
        j = j - 1
    Next i
   
    [d4] = Timer - t
    range("A:A").Clear
    range("a1").Resize(range("d3").Value) = brr
    Set arr = Nothing
    Set brr = Nothing
End Sub

[ 本帖最后由 yes2boy 于 2011-1-24 11:57 编辑 ]

TA的精华主题

TA的得分主题

发表于 2011-1-24 14:42 | 显示全部楼层
Public Function ZRand(Min, Max, n)
Dim a(), b(), i, Cou As Long
ReDim a(1 To Max - Min + 1)
ReDim b(n - 1)
Cou = Max - Min + 1
For i = 0 To n - 1
  t = Int(Cou * Rnd()) + 1
  If a(t) = "" Then b(i) = t Else b(i) = a(t)
  If a(Cou) = "" Then a(t) = Cou Else a(t) = a(Cou)
  Cou = Cou - 1
Next
ZRand = b
End Function

TA的精华主题

TA的得分主题

 楼主| 发表于 2011-1-24 16:06 | 显示全部楼层
[广告] VBA代码宝 - VBA编程加强工具 · VBA代码随查随用  · 内置多项VBA编程加强工具       ★ 免费下载 ★      ★使用手册
ZAMYI老师的代码如果max等于1亿就内存溢出了。为什么呢?
您需要登录后才可以回帖 登录 | 免费注册

本版积分规则

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

GMT+8, 2025-1-16 21:44 , Processed in 0.044595 second(s), 6 queries , Gzip On, MemCache On.

Powered by Discuz! X3.4

© 1999-2023 Wooffice Inc.

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

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

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