在看alg4的过程中时,看到对于洗牌,random shuffle,书上介绍了一种好的算法和一种不好的算法,但没有给出为什么,下面给出我的理解:
(1)每一次交换都与自己位置之后的元素进行交换,代码如下:
public static void shuffle (double[] a){
int n = a.length;
for(int i = 0; i<n; i++)
{int r= i + StdRandom.uniform(n-i);
doule temp=a[i];
a[i]=a[r];
a[r]=temp;}
}
这让我想起了学习概率论时的一个舞会帽子的问题:在一个舞会中,所有人放帽子时打乱了,在他们离开时,每个人都拿了一顶帽子,问:拿对的人数的期望,这里的答案是一人, 这里很容易理解,每个人拿到任何一顶帽子的概率都是相同的。于是当你把一个数列中的每个位置看作是一个人,例如:第一个位置对应于第一个拿帽子的人。而他们本身对应的元素为舞会中的帽子,于是可以发现,每一个位置在洗牌后对应得到任何的元素的概率与他们元素初始位置无关,且都相等。故这样的洗牌方法可以得到一个十分随机的,等可能的序列。
(2)每一次交换都在所有的元素中随机挑选,算法代码如下:
public static void badshuffle (double[] a){
int n = a.length;
for(int i = 0; i<n; i++)
{int r= StdRandom.uniform(n);
doule temp=a[i];
a[i]=a[r];
a[r]=temp;}
}
对于这种bad shuffling, 可以很简单的取个长度为三的数组,按照这个方法算得结果,会发现其概率分布是不均匀的,也就是说可以知道牌的顺序,在看过一定的牌后可以得知下一张牌哪一张的概率大。对于数学上的理解:
对于长度为n的数列,总共有n的n次方种可能(不考虑是否相同),而不同的情况共有n!种可能,这里可以发现,这两个数是互质的,也就是说,最后是不可能出现,每种不同的排列情况概率为1/n!的,为了更直观的理解,我们可以通过模拟实验来观察一下。
实验代码如下
public class ShuffleTest{
public static void shuffle (int[] a){ //有效的打乱方法
int n = a.length;
for(int i = 0; i<n; i++)
{int r= i + StdRandom.uniform(n-i);
int temp=a[i];
a[i]=a[r];
a[r]=temp;}
}
public static void badshuffle(int [] a){ //概率不是均匀分布的方法
int n = a.length;
for(int i = 0; i<n; i++)
{int r= StdRandom.uniform(n);
int temp=a[i];
a[i]=a[r];
a[r]=temp;}
}
public static void main(String[] args){
int n = Integer.parseInt(args[0]);
int m = Integer.parseInt(args[1]);
int[] a=new int[m];
for(int i=0;i<m;i++) a[i]=i;
double[][] target=new double[m][m];
for(int i=0;i<m;i++)
for(int j=0;j<m;j++)
target[i][j]=0;
for(int i=0;i<n;i++){
badshuffle(a);
for(int j=0;j<m;j++)
target[a[j]][j]+=1;}
for(int i=0;i<m;i++)
{for(int j=0;j<m;j++)
StdOut.print(target[i][j]+" ");
StdOut.println();}}
}