随机洗牌算法Knuth Shuffle和错排公式

Knuth随机洗牌算法:譬如现在有54张牌,如何洗牌才能保证随机性。可以这么考虑,从最末尾一张牌开始洗,对于每一张牌,编号在该牌前面的牌中任意一张选一张和当前牌进行交换,直至洗到第一张牌为止。参考代码如下:

void knuth() {
        for (int i = 54; i > 1; i--) {
            int id = rand() % (i - 1) + 1;
            swap(a[i], a[id]);
        }
    }

由上述方法可知,每一张牌经过洗牌之后一定不会出现在原来位置,那么一共会有多少情况呢,这其实就是错排的定义,n个数的错排数有如下递推公式:

f(n)=(n-1)(f(n-1)+f(n-2))

公式的推导:首先让我们假设已知n-1个数和n-2个数的错排数,这时又在原来基础上加了一个数字,那么如果这n个数要构成错排,新加入的数字一定不能出现在自己的位置上,所以它只能选择其余的n-1个位置,不妨设选择了第k个位置,那么原本在第k个位置上的数又会跑到哪里去呢,这里有两种情况,原本的第k个数跑到第n个数的位置上去了,这时这两个数只是相互交换了位置,其余的n-2个数怎么排列完全不受影响,故此时有f(n-2)种情况;再考虑原本第k个数不在第n个数的位置上,那么除去第n个数,其余的n-1个数任然构成错排,错排数为f(n-1)。至此就可得递推式f(n)=(n-1)(f(n-1)+f(n-2));

原文地址:https://www.cnblogs.com/ZefengYao/p/9490061.html

时间: 2024-10-08 01:47:13

随机洗牌算法Knuth Shuffle和错排公式的相关文章

随机洗牌算法

随机洗牌算法: 时间和空间复杂度都为O(n). 1 #include <bits/stdc++.h> 2 3 using namespace std; 4 5 const int maxn = 54; 6 7 void random_shuffle(vector<int> &a) 8 { 9 srand(int(time(0))); 10 int aSize = a.size(); 11 for(int i = 1; i != aSize; ++i) { 12 int j

随机洗牌算法---我们一起谈谈

看到这个问题是在知乎上, 一个回答: 实现一下 #include <iostream> #include <vector> #include <string> #include <algorithm> using namespace std; const int RAND_MAXNUM = 100; int bigRand() { return RAND_MAXNUM*rand() + rand(); } int randIn(int l, int u) {

随机打乱数组顺序之随机洗牌算法

var shuffleArray = function(array) { var currentIndex = array.length; var temporary; var toIndex; while (currentIndex) { toIndex = Math.floor(Math.random() * currentIndex--); temporary = array[currentIndex]; array[currentIndex] = array[toIndex]; arra

洗牌算法浅试

Python有自带的洗牌算法函数shuffle(). 自己也通过学习也琢磨了一下它的实现,然后给出一个时间复杂度O(n),空间复杂度O(1)的例子: 1 import random 2 3 def shuffle1(lst) : 4 l = len(lst) 5 if l <= 1 : return lst 6 7 i = 0 8 while l > 1 : 9 j = int(random.random() * l) 10 t = lst[i] 11 lst[i] = lst[i+j] 12

随机算法之30万员工抽取10万员工(洗牌算法)

算法方面不是我的强项,所以遇到这个问题,记录一下解决方法: 最开始的时候,做法是从random中来随机选取一个,然后再判断已有列表中是否存在, 然后以此重复,看到这里,会算法的同学肯定会说这明显不行:就好比多少人中生日为一天的概率大于50%: 所以然后查找了一些资料,看到了洗牌算法:包括多种变种吧. 这里说一下我参考的解决办法: 比如有30万个元素的列表,那么第一次随机选择一个:记录位置是x: 然后把元素的最后一个位置元素存入位置x,然后把最后位置的元素删除: (这里在交换的时候,需要判断这个元

Javascript 洗牌算法,打乱数组,随机获取元素

//利用洗牌算法Array.prototype.shuffle=function(){ var i,t,m=this.length; while(m){ i=Math.floor(Math.random()*m--); t=this[m]; this[m]=this[i]; this[i]=t; } return this;} var arr=[1,2,3,4,5];console.log(arr.shuffle());console.log(arr.slice(0,2));

【每日算法】洗牌算法

洗牌算法 给定一个n个数的序列,设计一个算法将其随机打乱,保证每个数出现在任意一个位置的概率相同(也就是说在n!个的排列中,每一个排列出现的概率相同). 朴素的做法: 假设输入为数组num[length]. 随机选一个数,放到num[0]中,再随机选数,如果该数已经选过,重新选,直到该数未选过时放入num[1]中,以此类推,直到所有的数都选出来,很明显,这种选法一共有n!中可能,每种可能出现的概率都相同. 但是该做法效率不高,因为选过的数再选将耗费大量时间. 改进的洗牌算法: 基于以上算法的缺陷

算法之洗牌算法

洗牌算法是我们常见的随机问题,在玩游戏.随机排序时经常会碰到,一个最常用的地方就是组卷.它可以抽象成这样:M以内的所有自然数的随机顺序数组. package com.math; import java.util.Random; /** * @author summer * */ public class Shuffle { static final int[] a = new int[54]; static Random rnd = new Random(); static{ for(int i

转:浅谈洗牌算法(面试题)

很多人都有耳闻过洗牌算法,时常会在面试中碰到,我们下面来定义一下这个问题. 所谓洗牌算法,就是给你一个1到n的序列,让你随机打乱,保证每个数出现在任意一个位置的概率相同,也就是说在n!个的排列中,每一个排列出现的概率相同. 最朴素的做法 对于这个问题我们从最朴素的解法谈起.每次随机选出一个没有被选过的数放到一个队列中,如果随机出来的数已经被选过,那么继续随机直到遇到一个没有被选过的数放入到队列中:重复这样子操作直到所有的数都被选择出来. 我们看看这样子作为什么是对的.首先选第一个数的时候有n个数