随机洗牌算法

来源:互联网 发布:手机网络角色游戏 编辑:程序博客网 时间:2024/05/18 09:58
算法一:就是随机一个位置放入队列中的一个书,位置上已经有数则重新随机一个位置,直到队列空

算法(Java):


public HashSet getRamdomNumber(int total,int number){ 


//在0到total-1中随机生成number个不重复的随机数


    HashSet h=new HashSet();

    while(h.size()<number){

        Random r=new Random();

        int n=r.nextInt(total);

        //n++;


        h.add((Integer)n);   //不用考虑重复的情况
    }

    return h;

}


    第二种方法是从网上看到的,思路是:将数组的元素进行随机的交换.这个算法我不太好说,因为它的效果和效率都依赖于交换的次数N.次数越多则乱序的程度越高但是相应的效率也会降低.个人不太推荐这个算法,因为N的次数不好确定.


算法:


void shuffle ( int a[], int n )      //洗牌算法

{


    int tmp = 0,p1,p2;

    int cnt = rand() % 1023;

    while (cnt--)   //随机交换两个位置的数,共交换cnt次


    {

           p1 = rand() % n;

           p2 = rand() % n;

           tmp = a[p1];

           a[p1] = a[p2];

           a[p2] = tmp;

    }


}


    后来在看Core Java 2的时候看到了一个计算彩票的问题,实际上就是洗牌算法.思路是这样的:从数组中随机取出一个放到一个新的集合中,然后重复这个工作.这样最后原数组为空而新的集合则为乱序.这个算法比第一种算法的效率高多了,而且计算次数是固定的.当时就以为找到了最佳算法.后来蒙朋友提醒,对这个算法进行了改进.其实没有必要开辟一个新的集合的,用原来的存储空间就行.具体思路是:从数组的数中随机取出一个和最后一个元素交换,再从前面N-1个数中随机取一个和倒数第二个交换…这样可以达到和前面算法相同的效果而且存储空间也得到了节省,很不错的算法了.


算法:


void shuffle()

{

       int *prev=new int[54];

       for(int i=0;i<54;i++)      //数组的初始化,表示54张牌

      {

             prev[i]=i;

      }

       srand(time(0));

       int times=53;

       while(times!=0)

       {

              int one=rand()%times;

              swap(prev[one],prev[times]);       //交换元素

              times--;

       }    

       //这个时候prev数组处于乱序状态,用完后回收空间

       delete prev;

}

这是我摘自 matrix67大牛的博客的一段总结:

1. for i:=1 to n do swap(a[i], a[random(1,n)]);
2. for i:=1 to n do swap(a[i], a[random(i,n)]);


    如果不仔细思考的话,绝大多数人会认为第一个算法才是真正随机的,因为它的操作“更对称”,保证了概率均等。但静下心来仔细思考,你会发现第二种算法才是真正满足随机性的。为了证明这一点,只需要注意到算法的本质是“随机确定a[1]的值,然后递归地对后n-1位进行操作”,用数学归纳法即可轻易说明算法的正确性。而事实上,这段程序一共将会产生n*(n-1)*(n-2)*…*1种等可能的情况,它们正好与1至n的n!种排列一一对应。
     有人会问,那第一种算法为什么就错了呢?看它的样子多么对称美观啊……且慢,我还没说第一种算法是错的哦!虽然第一种算法将产生比第二种算法更多的可能性,会导致一些重复的数列,但完全有可能每种数列重复了相同的次数,概率仍然是均等的。事实上,更有可能发生的是,这两种算法都是正确的,不过相比之下呢第一种算法显得更加对称美观一些。为此,我们需要说明,第一种算法产生的所有情况均等地分成了n!个等价的结果。显然,这个算法将会产生n^n种情况,而我们的排列一共有n!个,因此n^n必须能够被n!整除才行(否则就不能均等地分布了)。但是,n!里含有所有不超过n的质数,而n^n里却只有n的那几个质因子。这表明要想n^n能被n!整除,n的质因子中必须含有所有不超过n的质数。这个结论看上去相当荒唐,反例遍地都是,并且直觉上告诉我们对于所有大于2的n这都是不成立的。为了证明这一点,只需要注意到2是质数,并且根据Bertrand-Chebyshev定理,在n/2和n之间一定还有一个质数。这两个质数的乘积已经大于n了。搞了半天,第一种看似对称而美观的算法居然是错的!



0 0
原创粉丝点击