洗牌算法
2024-09-18 09:35:20 217人阅读
<style>p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 30.0px "Microsoft YaHei"; color: #3333ff }
p.p2 { margin: 0.0px 0.0px 0.0px 0.0px; font: 15.0px "Microsoft YaHei"; color: #555555 }
p.p3 { margin: 0.0px 0.0px 0.0px 0.0px; font: 15.0px "Microsoft YaHei"; color: #555555; min-height: 20.0px }
p.p4 { margin: 0.0px 0.0px 0.0px 0.0px; font: 15.0px "Microsoft YaHei"; color: #999999 }
p.p5 { margin: 0.0px 0.0px 0.0px 0.0px; font: 9.0px Verdana; color: #0d89cf }
p.p6 { margin: 0.0px 0.0px 0.0px 0.0px; font: 9.0px Verdana; color: #c0c0c0; min-height: 11.0px }
p.p7 { margin: 0.0px 0.0px 0.0px 0.0px; font: 9.0px Verdana; color: #c0c0c0; background-color: #f8f8f8; min-height: 11.0px }
p.p11 { margin: 0.0px 0.0px 0.0px 0.0px; font: 15.0px "Microsoft YaHei"; color: #999999; min-height: 20.0px }
p.p12 { margin: 0.0px 0.0px 0.0px 0.0px; font: 15.0px "Microsoft YaHei"; color: #ff2500 }
p.p13 { margin: 0.0px 0.0px 0.0px 0.0px; font: 15.0px "Microsoft YaHei"; color: #0d89cf }
li.li8 { margin: 0.0px 0.0px 0.0px 0.0px; font: 12.0px "Courier New"; color: #5c5c5c }
li.li9 { margin: 0.0px 0.0px 0.0px 0.0px; font: 12.0px "Courier New"; color: #5c5c5c; background-color: #f8f8f8 }
li.li10 { margin: 0.0px 0.0px 0.0px 0.0px; font: 12.0px "Courier New"; color: #5c5c5c; background-color: #f8f8f8 }
li.li14 { margin: 0.0px 0.0px 0.0px 0.0px; font: 12.0px "Courier New"; color: #5c5c5c }
span.s1 { }
span.s2 { color: #3333ff }
span.s3 { color: #c0c0c0; background-color: #f8f8f8 }
span.s4 { background-color: #f8f8f8 }
span.s5 { }
span.s6 { color: #0433ff }
span.s7 { color: #006699 }
span.s8 { color: #2f8b57 }
span.s9 { }
span.s10 { }
span.s11 { font: 15.0px "Microsoft YaHei"; color: #0d89cf }
span.s12 { color: #006699 }
ol.ol1 { list-style-type: decimal }</style>
1. 问题描述
洗牌算法是常见的随机问题;它可以抽象成:得到一个M以内的所有自然数的随机顺序数组。
常见问题描述:
1.将自然数1 ~ 100随机插入到一个大小为100的数组,无重复元素
2. 1 ~ 52张扑克牌重新洗牌
什么是好的洗牌算法:
洗牌之后,如果能够保证每一个数出现在所有位置上的概率是相等的,那么这种算法是符合要求的;这在个前提下,尽量降低时间和空间复杂度。
2. 算法实现
第一个算法:
随机抽出一张牌,检查这种牌是否被抽取过,如果已经被抽取过,则重新抽取,知道找到没有被抽取的牌;重复该过程,知道所有的牌都被抽取到。
这种算法是比较符合大脑的直观思维,这种算法有两种形式:
1. 每次随机抽取后,将抽取的牌拿出来,则此时剩余的牌为(N-1),这种算法避免了重复抽取,但是每次抽取一张牌后,都有一个删除操作,需要在原始数组中删除随机选中的牌(可使用Hashtable实现)
2. 每次随机抽取后,将抽取的符合要求的牌做好标记,但并不删除;与1相比,省去了删除的操作,但增加了而外的存储标志为的空间,同时导致可每次可能会抽取之前抽过的牌
这种方法的时间/空间复杂度都不好。
第二个算法:
每次随机抽出两张牌交换,交换一定次数后结束:
[cpp] view plain copy
- <span style="color:#999999;">void shuffle(int* array, int len)
- {
- const int suff_time = len;
-
- for (int idx = 0; i < suff_time; i++)
- {
- int i = rand() % len;
- int j = rand() % len;
-
- int temp = array[i];
- array[i] = array[j];
- array[j] = temp;
- }
- }</span>
这是一个常见的洗牌算法; 但是如何确定一个合适的交换次数?
假设交换了m此,则某张牌始终没有被交换的概率为 (n-2)/n * (n-2)/n, ... ...* (n-2)/n = ((n-2)/n)^m;我们希望其概率小于摸个值,求出m的解.假设概率小于1/1000,对于n=52,m大概为176,实际上远远大于数组的长度.
第三个算法:
Fisher–Yates shuffle算法
该算法每次随机选取一个数,然后将该数与数组中最后(或最前)的元素相交换(如果随机选中的是最后/最前的元素,则相当于没有发生交换);然后缩小选取数组的范围,去掉最后的元素,即之前随机抽取出的数。重复上面的过程,直到剩余数组的大小为1,即只有一个元素时结束:
[cpp] view plain copy
- void shuffle(int* array, int len)
- {
- int i = len;
- int j = 0;
- int temp= = 0;
-
- if (i == 0)
- {
- return;
- }
-
- while (--i)
- {
- j = rand() % (i+1);
- temp = array[i];
- array[i] = array[j];
- array[j] = temp;
- }
- }
该算法的数学证明请参照具体的论文或者博文;
该算法复杂度为O(n),且各元素随机概率相等。
洗牌算法
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉:
投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。