c洗牌算法
❶ c语言 1到1000随机排列
声明一个数组,然后用循环将1000个数字顺序写入,再打乱数组元素
原来有人先回答了。。那我加上打乱1000个数组的函数吧 我用c++来着 不过貌似和c差不多
void xipai(int a[],int b)
{
for(int i=0,j,k;i<b;i++)
{
Sleep(10);
srand(clock());
j=rand()%1000;
k=a[i];
a[i]=a[j];
a[j]=k;
}
}
这是我上次写的洗牌算法。。。。。 你可以套用 改几个地方就行
❷ 用C++编写一个洗牌发牌的函数,玩家可能有两个、三个和四个
几乎所有的程序员都写过类似于“洗牌”的算法,也就是将一个数组随机打乱后输出,虽然很简单,但是深入研究起来,这个小小的算法也是大有讲究。我在面试程序员的时候,就会经常让他们当场写一个洗牌的函数,从中可以观察到他们对于这个问题的理解和写程序的基本功。
在深入讨论之前,必须先定义出一个基本概念:究竟洗牌算法的本质是什么?也就是说,什么样的洗牌结果是“正确”的?
云风曾经有一篇博文,专门讨论了这个问题,他也给出了一个比较确切的定义,在经过洗牌函数后,如果能够保证每一个数据出现在所有位置的概率是相等的,那么这种算法是符合要求的。在这个前提下,尽量降低时间复杂度和空间复杂度就能得到好的算法。
第一个洗牌算法:
随机抽出一张牌,检查这张牌是否被抽取过,如果已经被抽取过,则重新抽取,直到找到没被抽出过的牌,然后把这张牌放入洗好的队列中,重复该过程,直到所有的牌被抽出。
大概是比较符合大脑对于洗牌的直观思维,这个算法经常出现在我遇到的面试结果中,虽然它符合我们对于洗牌算法的基本要求,但这个算法并不好,首先它的复杂度为O(N2),而且需要额外的内存空间保存已经被抽出的牌的索引。所以当数据量比较大时,会极大降低效率。
第二个算法:
设牌的张数为n,首先准备n个不容易碰撞的随机数,然后进行排序,通过排序可以得到一个打乱次序的序列,按照这个序列将牌打乱。
这也是一个符合要求的算法,但是同样需要额外的存储空间,在复杂度上也会取决于所采用的排序算法,所以仍然不是一个好的算法。
第三个算法:
每次随机抽出两张牌交换,重复交换一定次数次后结束
void shuffle(int* data, int length)
{
for(int i=0; i<SWAP_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; i<length; i++)
{
int index = Rand(0, length);
std::swap(data[i], data[index]);
}
}
很明显,这个算法是符合我们先前的要求的,时间复杂度为O(N),而且也不需要额外的临时空间,似乎我们找到了最优的算法,然而事实并非如此,看下一个算法。
第五个算法:
void shuffle(int* data, int length)
{
for(int i=1; i<length; 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语言 洗牌算法
/*洗牌程序:用任何语言,随机分配52张扑克牌到52个位置上,每个位置只容许放一张牌
用1-13表示红心A--K
14-26表示黑桃A,2,3-,Q,K
27-39表示方块A,2,3-,Q,K
40-52表示黑桃A,2,3-,Q,K
也就是生成1-52不重复的随机数,放到数组中*/
#include<iomanip.h>
#include<stdlib.h>
#include<time.h>
const int N=52;
static int a[N];
int create(int n)
{
return (1+rand()%52);
}
int main()
{
int i,j;
srand(time(0));
for(i=0;i<N;++i)
{
a[i]=create(N);
for(j=0;j<i;++j)
{
if(a[j]==a[i])
{
a[i]=(a[i]+1)%52;
}
}
cout<<setw(5)<<a[i];
}
cout<<endl;
return 0;
}
❹ VBA洗牌法原理谁能文字阐释一下
我找了此资料,一起共享
下面直接介绍【经典数组洗牌法】的算法原理:
假如你有一个布袋或者抽屉,里面有m=10个不同号码的球,
你要随机抽取,并保证不重复……
那么正确的做法是:
1.每次从布袋中随机抽取一个球;注意到rnd()函数的正确抽取目数应该是=m
2.抽取出来的这一个球要另外放置开;
【如果不另外放置,而只是记下号码后再把球返回布袋,接下来就无法保证这个已经被抽到过的球又被重复抽到。】
而这个,就是1楼代码中没有考虑到而产生的重大bug
3.继续从布袋中随机抽取另一个球;
注意到此时布袋中剩余球的数量少了一个是m-1了,因此rnd()函数的正确抽取目数应该是=m-1
4.抽取出来的这第2个球和已经抽取出来的第1个球放置在一齐,并且按新的序列排放。
5.重复以上随机抽取过程,注意到关键是:
a.每次抽取的母数即剩余球数要递减1个
b.每次抽取出来的新球要分开放置,不能放回布袋!
c.新抽取出来的球,要和前面已经取出的球按新的序列整齐排放。
d.剩下最最重要一点,但是看到这里好多人都可能不会意识到的一个问题:
布袋中剩余球如何放置?
即,假定布袋中球也是像放置在抽屉中那样,有序地排放着的,
那么每次抽走一个球,必然留下一个空格……【这就很麻烦了!】
因为大家知道,实际上数组中用rnd()函数只能是返回一个一定区间内的值及数组位置,
而如果留有空位的话,随机性就无法保证高效……万一抽到空格怎么办?难道重新再抽一次?
如果抽到只剩最后一个求时,则原先的布袋/抽屉中,将留下9个空格,则每次随机函数的计算结果,将有90%的概率仍旧抽到空格……
这就完蛋了。
【经典数组洗牌法】的真正原理是:
1.从m个值中【随机确定一个位置r】(利用Rnd()随机函数计算,具体算法是【以剩余数m为母数】区间进行随机值计算并取整返回位置)
2.把这个位置即要被抽取的元素(球)先取出拿在手中【存入临时变量t】,【腾出一个空位】。
3.把数组的【第1位置】(Lbound)元素拿出来,【放入刚才腾出的r空位】。并随即【腾出】了数组第一位置作为【新的空位】
(也可以以数组的最末位置(UBound)作为开始位置进行处理,具体算法代码就不太一样了)
4.把上述第2步骤取出的、存入了临时变量t的元素(球),准确地放入【新的空位】,即数组第一位置。
这个数组第一位置中的【新元素】,就是已经被有效抽取的第1个不重复值。
然后,继续
1.抽第2个数时,以剩余母数m-1作为随机计算区间而返回一个随机值并计算取整返回第2个不重复的随机位置r
2.把这个位置r即要被抽取的第2个元素(球)先取出拿在手中【存入临时变量t】,【腾出一个空位】。
3.把数组的【第2位置】元素拿出来,【放入刚才腾出的r空位】。并随即【腾出】了数组第2位置作为【新的空位】
(也可以以数组的最末倒数第2位置进行处理,具体算法代码就不太一样了)
4.把上述第2步骤取出的、存入了临时变量t的元素(球),准确地放入【新的空位】,即数组第2位置。
这个数组第2位置中的【新元素】,就是已经被有效抽取的第2个不重复值。
以上述方式反复进行,抽取、置换,存放,直到最后一个,也不会产生重复抽取了。
下面是【经典数组洗牌法】实际代码中最简单的代码例子:
对于一个下标1开始的一维数组,从中随机抽取n个元素返回。
SubGetRnd(arr,n)
Randomize
Fori=1Ton'正序洗牌1ton简化代码
r=Int(Rnd()*(n-i+1))+i
t=arr(r):arr(r)=arr(i):arr(i)=t'下标1开始一维代码
Next
EndSub
'正序洗牌1ton简化代码详解:
SubGetRnd(arr,n)
Randomize'随机种子初始化,保证每次代码运行或打开文件时出现的随机序列是和上次文件保存/运行时不同的序列。
Fori=1Ton'遍历1ton
r=Int(Rnd()*(n-i+1))+i、
'按每次剩余母数(n-i+1)作为随机计算区间,计算Rnd()*(n-i+1)然后用int()函数去整,得到剩余数中的随机位置。
'紧接着,这个随机位置后面【+i】处理,转化成从新的起点i开始的随机位置r。即不再包括已经抽取出的结果,避免重复。
t=arr(r)'把这个随机位置r中的元素取出,存入临时变量t
arr(r)=arr(i)'把【第i个】位置中的元素【放入刚才腾出的r空位】(实际数据操作时并没有腾出,而只是用新的值直接覆盖掉。)
arr(i)=t'把上面刚刚【腾出的i空位】放入刚才存放在临时变量t中的当前最新抽取元素,完成一次抽取过程。
Next'循环抽取、置换、存贮抽取结果
EndSub
❺ 急求 用C程序表示一副扑克牌
对我的回答有什么疑问或要求,可以Hi我。
//一副牌升级,模拟发牌程序,供参考:
//第五家的牌即为底牌
//大小王的ASCII我的VC中显示不出来,所以用“大”“小”代替
//算法:用随机数模拟洗牌。产生两个1~54之间的随机数,然后交换对应的两张牌。
//洗牌模块非原创
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
int main()
{
int i,temp,r;
int pk[54]={501,502,
101,102,103,104,105,106,107,108,109,110,111,112,113,
201,202,203,204,205,206,207,208,209,210,211,212,213,
301,302,303,304,305,306,307,308,309,310,311,312,313,
401,402,403,404,405,406,407,408,409,410,411,412,413};
printf("\n");
srand((unsigned int)time(NULL));
for(i=0;i<53;i++) //洗牌
{
r=rand()%(53-i)+i+1;//加1为了洗最后一张牌
temp=pk[i];
pk[i]=pk[r];
pk[r]=temp;
}
int k=1;
for(i=0;i<54;i++)//输出每家的牌
{
if(i%12==0)printf("\n第%d家的牌:",k++);
switch(pk[i]/100)
{
case 1:printf(" \003");break;//黑桃
case 2:printf(" \006");break;//红桃
case 3:printf(" \005");break;//梅花
case 4:printf(" \004");break;//方块
case 5:
switch(pk[i]%100)
{
case 1:printf(" 大 ");break;//大王
case 2:printf(" 小 ");break;//小王
}
}
switch(pk[i]%100)
{
case 1:if(pk[i]/100!=5)printf("%-2c",'A');break;
case 2:if(pk[i]/100!=5)printf("%-2d",pk[i]%100);break;
case 3:
case 4:
case 5:
case 6:
case 7:
case 8:
case 9:
case 10:printf("%-2d",pk[i]%100);break;
case 11:printf("%-2c",'J');break;
case 12:printf("%-2c",'Q');break;
case 13:printf("%-2c",'K');break;
}
}
printf("\n");
return 0;
}
❻ 兄弟能帮忙解决个c语言问题自动发牌 帮我看下我的程序具体怎么才能实现按先分花色,每种花色再按大小排
你的算法搞得太复杂了!给puke按花色和大小加上一个编号从0~51,每张牌对应一个编号。在程序里直接对编号操作(发牌、洗牌、排序等)就容易多了。
❼ C语言问题.急!
还原需要16次,不是20次,源码和输出结果如下:
/****************************
模拟洗牌-完美洗牌法
****************************/
voidmain()
{
inti,j;
inta[52],b[52];
charpoker[52][4]={
"AH","2H","3H","4H","5H","6H","7H","8H","9H","10H","JH","QH","KH",
"AC","2C","3C","4C","5C","6C","7C","8C","9C","10C","JC","QC","KC",
"AD","2D","3D","4D","5D","6D","7D","8D","9D","10D","JD","QD","KD",
"AS","2S","3S","4S","5S","6S","7S","8S","9S","10S","JS","QS","KS"};
for(i=0;i<52;i++)a[i]=i;
for(j=0;j<52;j++)
{
printf("%s",poker[a[j]]);
if(j%13==12)printf("\n");
}
printf("----\n");
for(i=0;i<16;i++)
{
for(j=0;j<26;j++)
{
b[2*j]=a[j];
}
for(j=26;j<52;j++)
{
b[2*(j-26)+1]=a[j];
}
for(j=0;j<52;j++)
{
printf("%s",poker[b[j]]);
if(j%13==12)printf("\n");
}
printf("----\n");
for(j=0;j<52;j++)a[j]=b[j];
}
getch();
}
输出结果:
AH2H3H4H5H6H7H8H9H10HJHQHKH
AC2C3C4C5C6C7C8C9C10CJCQCKC
AD2D3D4D5D6D7D8D9D10DJDQDKD
AS2S3S4S5S6S7S8S9S10SJSQSKS
----
AHAD2H2D3H3D4H4D5H5D6H6D7H
7D8H8D9H9D10H10DJHJDQHQDKHKD
ACAS2C2S3C3S4C4S5C5S6C6S7C
7S8C8S9C9S10C10SJCJSQCQSKCKS
----
AHACADAS2H2C2D2S3H3C3D3S4H
4C4D4S5H5C5D5S6H6C6D6S7H7C
7D7S8H8C8D8S9H9C9D9S10H10C10D
10SJHJCJDJSQHQCQDQSKHKCKDKS
----
AH7DAC7SAD8HAS8C2H8D2C8S2D
9H2S9C3H9D3C9S3D10H3S10C4H10D
4C10S4DJH4SJC5HJD5CJS5DQH5S
QC6HQD6CQS6DKH6SKC7HKD7CKS
----
AH4C7D10SAC4D7SJHAD4S8HJCAS
5H8CJD2H5C8DJS2C5D8SQH2D5S
9HQC2S6H9CQD3H6C9DQS3C6D9S
KH3D6S10HKC3S7H10CKD4H7C10DKS
----
AH9H4CQC7D2S10S6HAC9C4DQD7S
3HJH6CAD9D4SQS8H3CJC6DAS9S
5HKH8C3DJD6S2H10H5CKC8D3SJS
7H2C10C5DKD8S4HQH7C2D10D5SKS
----
AH5H9HKH4C8CQC3D7DJD2S6S10S
2H6H10HAC5C9CKC4D8DQD3S7SJS
3H7HJH2C6C10CAD5D9DKD4S8SQS
4H8HQH3C7CJC2D6D10DAS5S9SKS
----
AH3H5H7H9HJHKH2C4C6C8C10CQC
AD3D5D7D9DJDKD2S4S6S8S10SQS
2H4H6H8H10HQHAC3C5C7C9CJCKC
2D4D6D8D10DQDAS3S5S7S9SJSKS
----
AH2H3H4H5H6H7H8H9H10HJHQHKH
AC2C3C4C5C6C7C8C9C10CJCQCKC
AD2D3D4D5D6D7D8D9D10DJDQDKD
AS2S3S4S5S6S7S8S9S10SJSQSKS
----
AHAD2H2D3H3D4H4D5H5D6H6D7H
7D8H8D9H9D10H10DJHJDQHQDKHKD
ACAS2C2S3C3S4C4S5C5S6C6S7C
7S8C8S9C9S10C10SJCJSQCQSKCKS
----
AHACADAS2H2C2D2S3H3C3D3S4H
4C4D4S5H5C5D5S6H6C6D6S7H7C
7D7S8H8C8D8S9H9C9D9S10H10C10D
10SJHJCJDJSQHQCQDQSKHKCKDKS
----
AH7DAC7SAD8HAS8C2H8D2C8S2D
9H2S9C3H9D3C9S3D10H3S10C4H10D
4C10S4DJH4SJC5HJD5CJS5DQH5S
QC6HQD6CQS6DKH6SKC7HKD7CKS
----
AH4C7D10SAC4D7SJHAD4S8HJCAS
5H8CJD2H5C8DJS2C5D8SQH2D5S
9HQC2S6H9CQD3H6C9DQS3C6D9S
KH3D6S10HKC3S7H10CKD4H7C10DKS
----
AH9H4CQC7D2S10S6HAC9C4DQD7S
3HJH6CAD9D4SQS8H3CJC6DAS9S
5HKH8C3DJD6S2H10H5CKC8D3SJS
7H2C10C5DKD8S4HQH7C2D10D5SKS
----
AH5H9HKH4C8CQC3D7DJD2S6S10S
2H6H10HAC5C9CKC4D8DQD3S7SJS
3H7HJH2C6C10CAD5D9DKD4S8SQS
4H8HQH3C7CJC2D6D10DAS5S9SKS
----
AH3H5H7H9HJHKH2C4C6C8C10CQC
AD3D5D7D9DJDKD2S4S6S8S10SQS
2H4H6H8H10HQHAC3C5C7C9CJCKC
2D4D6D8D10DQDAS3S5S7S9SJSKS
----
AH2H3H4H5H6H7H8H9H10HJHQHKH
AC2C3C4C5C6C7C8C9C10CJCQCKC
AD2D3D4D5D6D7D8D9D10DJDQDKD
AS2S3S4S5S6S7S8S9S10SJSQSKS
----
❽ 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;j<2*num;j++)
{
arry[j] = temp[j] = j;
}
//以下是循环部分
bool gogo = true;
int count = 0;
while(gogo)
{
//更新奇、偶数组
for(int i=0; i<2*num; i++)
{
if(i<num)
t1[i] = temp[i];
else
t2[(i-num)] = temp[i];
}
//重组temp数组
for(i=0; i<2*num; i++)
{
if(i%2==0)
temp[i] = t2[i/2] ;
else
temp[i] = t1[(i-1)/2];
}
//判断重组后的数组temp是否和原来的数组一样
for(i=0; i<2*num; i++)
{
if(arry[i] != temp[i])
{
break;
}
}
//如果完全相同,则此时 i==2n;
if(i==2*num)
gogo = false;
count++;
}
cout << count << "次后数组恢复到原来的次序。";
return 0;
}
❾ Objective-C 语言实现 Knuth 洗牌算法
没做过这个,但是你可以找c语言的代码来用,完全支持
❿ 如何用C语言产生不重复的0到9之间的随机数
刚调试了下,弄不明白的是,为什么SZ[10]明明不存在却永远是产生的r对应输出的数。。。
。。。。又研究了20分钟,终于弄懂了。。。实际上应该是9更标准一些,虽然10不会溢出。。。rang()%(10-i)第一个从0-9里选,然后选过的消失。。。最后一个为9
第二次从0-8里选,选过的消失。。。最后2个为9,以后永远都选不上第8和第九。无论9出没出现过,以此类推。最终全部为九,其余消失。
如果为10的话,其实也影响不了,因为最后几个数永远都不会取到、
采纳了吧。。。。。。
额,其实最标准的应该把那个地方改成9-i,把没选的数字往前推,但是已经出现的过的就不必推了。。。