当前位置:首页 > 代码 > 正文

洗牌代码(洗牌百度百科)

admin 发布:2022-12-19 23:38 144


本篇文章给大家谈谈洗牌代码,以及洗牌百度百科对应的知识点,希望对各位有所帮助,不要忘了收藏本站喔。

本文目录一览:

用C++编写一个洗牌发牌的函数,玩家可能有两个、三个和四个

几乎所有的程序员都写过类似于“洗牌”的算法,也就是将一个数组随机打乱后输出,虽然很简单,但是深入研究起来,这个小小的算法也是大有讲究。我在面试程序员的时候,就会经常让他们当场写一个洗牌的函数,从中可以观察到他们对于这个问题的理解和写程序的基本功。

在深入讨论之前,必须先定义出一个基本概念:究竟洗牌算法的本质是什么?也就是说,什么样的洗牌结果是“正确”的?

云风曾经有一篇博文,专门讨论了这个问题,他也给出了一个比较确切的定义,在经过洗牌函数后,如果能够保证每一个数据出现在所有位置的概率是相等的,那么这种算法是符合要求的。在这个前提下,尽量降低时间复杂度和空间复杂度就能得到好的算法。

第一个洗牌算法:

随机抽出一张牌,检查这张牌是否被抽取过,如果已经被抽取过,则重新抽取,直到找到没被抽出过的牌,然后把这张牌放入洗好的队列中,重复该过程,直到所有的牌被抽出。

大概是比较符合大脑对于洗牌的直观思维,这个算法经常出现在我遇到的面试结果中,虽然它符合我们对于洗牌算法的基本要求,但这个算法并不好,首先它的复杂度为O(N2),而且需要额外的内存空间保存已经被抽出的牌的索引。所以当数据量比较大时,会极大降低效率。

第二个算法:

设牌的张数为n,首先准备n个不容易碰撞的随机数,然后进行排序,通过排序可以得到一个打乱次序的序列,按照这个序列将牌打乱。

这也是一个符合要求的算法,但是同样需要额外的存储空间,在复杂度上也会取决于所采用的排序算法,所以仍然不是一个好的算法。

第三个算法:

每次随机抽出两张牌交换,重复交换一定次数次后结束

void shuffle(int* data, int length)

{

for(int i=0; iSWAP_COUNTS; i++)

{

//Rand(min, max)返回[min, max)区间内的随机数

int index1 = Rand(0, length);

int index2 = Rand(0, length);

std::swap(data[index1], data[index2]);

}

}

这又是一个常见的洗牌方法,比较有意思的问题是其中的“交换次数”,我们该如何确定一个合适的交换次数?简单的计算,交换m次后,具体某张牌始终没有被抽到的概率为((n-2)/n)^m,如果我们要求这个概率小于1/1000,那么 m-3*ln(10)/ln(1-2/n),对于52张牌,这个数大约是176次,需要注意的是,这是满足“具体某张牌”始终没有被抽到的概率,如果需要满足“任意一张牌”没被抽到的概率小于1/1000,需要的次数还要大一些,但这个概率计算起来比较复杂,有兴趣的朋友可以试一下。

Update: 这个概率是,推算过程可以参考这里,根据这个概率,需要交换280次才能符合要求

第四个算法:

从第一张牌开始,将每张牌和随机的一张牌进行交换

void shuffle(int* data, int length)

{

for(int i=0; ilength; i++)

{

int index = Rand(0, length);

std::swap(data[i], data[index]);

}

}

很明显,这个算法是符合我们先前的要求的,时间复杂度为O(N),而且也不需要额外的临时空间,似乎我们找到了最优的算法,然而事实并非如此,看下一个算法。

第五个算法:

void shuffle(int* data, int length)

{

for(int i=1; ilength; i++)

{

int index = Rand(0, i);

std::swap(data[i], data[index]);

}

}

一个有意思的情况出现了,这个算法和第三种算法非常相似,从直觉来说,似乎使数据“杂乱”的能力还要弱于第三种,但事实上,这种算法要强于第三种。要想严格的证明这一点并不容易,需要一些数学功底,有兴趣的朋友可以参照一下这篇论文,或者matrix67大牛的博文,也可以这样简单理解一下,对于n张牌的数据,实际排列的可能情况为n! 种,但第四种算法能够产生n^n种排列,远远大于实际的排列情况,而且n^n不能被n!整除,所以经过算法四所定义的牌与牌之间的交换程序,很可能一张牌被换来换去又被换回到原来的位置,所以这个算法不是最优的。而算法五输出的可能组合恰好是n!种,所以这个算法才是完美的。

事情并没有结束,如果真的要找一个最优的算法,还是请出最终的冠军吧!

第六个算法:

void shuffle(int* data, int length)

{

std::random_shuffle(data, data+length);

}

没错,用c++的标准库函数才是最优方案,事实上,std::random_shuffle在实现上也是采取了第四种方法,看来还是那句话,“不要重复制造轮子”

不想写 - -

C语言编程——发牌洗牌模拟,求帮助

实现了2副牌的发牌,和每个人的牌和底牌

#includestdio.h

#includestdlib.h

#includetime.h

#includestring.h

struct CARD //牌

{

char suit[10]; /*花色*/

char face[10]; /*牌面*/

};

enum { posA, posB, posC, posD};//定义好每个人的位置

struct Postion

{

struct CARD getcard[25];//每人获得的牌

};

struct Postion postion[4];//分配四个位置

struct CARD leftCard[8]; //底牌

struct CARD card[54]; //54张牌

char *suit[]={"Spades","Hearts","Clubs","Diamonds"};

char *face[] = {"A","2","3","4","5","6","7","8","9",

"10","jack","Queen","King"};

/* 函数功能:将52张牌的顺序打乱,

函数参数:结构体数组wCard,表示52张牌

函数返回值:无

*/

void Shuffle(struct CARD *wCard)

{

int i,j;

struct CARD temp;

for (i=0; i54; i++)

{

j=rand()%54;

temp=wCard[i];

wCard[i]=wCard[j];

wCard[j]=temp;

}

}

/*函数功能:发牌结果

函数参数:结构体数组wCard,表示有54张牌

函数返回值:无

*/

void Deal(struct CARD *wCard)

{

int i,aidx=0,bidx=0,cidx=0,didx=0;

Shuffle(card);//将牌打乱

/*************发第一副牌,只发50张,分别分给A,B,C,D四个位置 4张留底**************/

// 第一次发完50张后,A,B多一张,所以下面第二次让C,D排在前面,两次发完刚好各40张 */

for (i=0; i50; i++)//发牌数

{

// printf("%10s %5s\n", wCard[i].suit, wCard[i].face);

if(i%4==0)

postion[posA].getcard[aidx++]=wCard[i];

else if(i%4==1)

postion[posB].getcard[bidx++]=wCard[i];

else if(i%4==2)

postion[posC].getcard[cidx++]=wCard[i];

else if(i%4==3)

postion[posD].getcard[didx++]=wCard[i];

}

/**********剩下的四张作为底牌*********/

leftCard[0]=wCard[i++];

leftCard[1]=wCard[i++];

leftCard[2]=wCard[i++];

leftCard[3]=wCard[i++];

Shuffle(card);//再次将牌打乱

/*************发第二副牌,也只发50张,分别分给A,B,C,D四个位置,4张留底,一共8张底**************/

for (i=0; i50; i++)//发牌数

{

// printf("%10s %5s\n", wCard[i].suit, wCard[i].face);

if(i%4==0)

postion[posC].getcard[cidx++]=wCard[i];

else if(i%4==1)

postion[posD].getcard[didx++]=wCard[i];

else if(i%4==2)

postion[posA].getcard[aidx++]=wCard[i];

else if(i%4==3)

postion[posB].getcard[bidx++]=wCard[i];

}

/**********剩下的四张作为底牌,这样就一共为8张底牌*********/

leftCard[4]=wCard[i++];

leftCard[5]=wCard[i++];

leftCard[6]=wCard[i++];

leftCard[7]=wCard[i++];

}

/* 函数功能:将52张牌按黑桃、红桃、草花、方块花色顺序,面值按A~K顺序排列

函数参数:结构体数组wCard,表示不同花色和面值的52张牌

指针数组wFace,指向面值字符串

指针数组wSuit,指向花色字符串

函数返回值:无

*/

void FillCard(struct CARD wCard[],char *wSuit[], char *wFace[])

{

int i;

for (i=0; i52; i++)

{

strcpy(wCard[i].suit, wSuit[i/13]);

strcpy(wCard[i].face, wFace[i%13]);

}

// wCard[53].face="Big"; //大小王

strcpy(wCard[52].suit, "Small");

strcpy(wCard[52].face, "ghost");

strcpy(wCard[53].suit, "Big");

strcpy(wCard[53].face, "ghost");

}

void print(char ch)//输出牌

{

int i;

switch(ch)

{

case 'A': for(i=0; i25; i++)

{

printf("%10s %5s\n", postion[posA].getcard[i].suit, postion[posA].getcard[i].face);

}

break;

case 'B': for(i=0; i25; i++)

{

printf("%10s %5s\n", postion[posB].getcard[i].suit, postion[posB].getcard[i].face);

}

break;

case 'C': for(i=0; i25; i++)

{

printf("%10s %5s\n", postion[posC].getcard[i].suit, postion[posC].getcard[i].face);

}

break;

case 'D': for(i=0; i25; i++)

{

printf("%10s %5s\n", postion[posD].getcard[i].suit, postion[posD].getcard[i].face);

}

break;

}

}

void outputLeftCard()//输出底牌

{

int i;

for(i=0; i8; i++)

printf("%10s %5s\n", leftCard[i].suit, leftCard[i].face);

}

int main()

{

char pos;

srand(time(NULL));

FillCard(card,suit,face);

//Shuffle(card);

Deal(card);

printf("Please choose your position(A、B、C、D):");

scanf("%c", pos);

print(pos);//输出你所在位置的牌

/**********下面输出的是,除了你之外其他人的牌**********/

if(pos !='A')

{

printf("A:\n");

print('A');

}

if(pos !='B')

{

printf("B:\n");

print('B');

}

if(pos !='C')

{

printf("C:\n");

print('C');

}

if(pos !='D')

{

printf("D:\n");

print('D');

}

printf("底牌为:\n");

outputLeftCard();//输出底牌

return 0;

}

C语言 洗牌

下面是正确的代码,没有用链表,通过4个数组来做的,必要的注释我都加了。不理解可以问我。

cout相当于printf,cin相当于scanf。

#include "iostream"

using namespace std;

int main()

{

int num;

cout "请输入特定数n:";

cin num;

int *arry = new int[2*num];

int *temp = new int[2*num];

int *t1 = new int[num];

int *t2 = new int[num];

//初始化数组

for(int j=0;j2*num;j++)

{

arry[j] = temp[j] = j;

}

//以下是循环部分

bool gogo = true;

int count = 0;

while(gogo)

{

//更新奇、偶数组

for(int i=0; i2*num; i++)

{

if(inum)

t1[i] = temp[i];

else

t2[(i-num)] = temp[i];

}

//重组temp数组

for(i=0; i2*num; i++)

{

if(i%2==0)

temp[i] = t2[i/2] ;

else

temp[i] = t1[(i-1)/2];

}

//判断重组后的数组temp是否和原来的数组一样

for(i=0; i2*num; i++)

{

if(arry[i] != temp[i])

{

break;

}

}

//如果完全相同,则此时 i==2n;

if(i==2*num)

gogo = false;

count++;

}

cout count "次后数组恢复到原来的次序。";

return 0;

}

java洗牌算法问题

你指的是Card里的toString方法吧

 public String toString() {

  String aa = suit + " " + num + "  ";

  return aa;

 }

toString这个方法一般在 System.out.print时使用,这个是打印出String,JVM就是默认调用类的toSting方法

注:所有类都有toString方法,默认是当前对象的hashcode,即内存地址

所以在发牌是打印

 public void dealcard()//发牌

    {

        for(int i=0;i52;i++)

        {

            if(i%4==0i!=0){

             System.out.println();  //每发4张牌输出换行

            }

            // 就是这里,默认调用card的toString方法

             System.out.print(card[i]); //依次输出 发的牌

        }

    }

洗牌代码的介绍就聊到这里吧,感谢你花时间阅读本站内容,更多关于洗牌百度百科、洗牌代码的信息别忘了在本站进行查找喔。

版权说明:如非注明,本站文章均为 AH站长 原创,转载请注明出处和附带本文链接;

本文地址:http://ahzz.com.cn/post/30568.html


取消回复欢迎 发表评论:

分享到

温馨提示

下载成功了么?或者链接失效了?

联系我们反馈

立即下载