#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int  d[6];
int  i,n,a,b,t;
int  c,j;
void  main() {
     srand ( time (NULL));
     printf ( "shuffle 0..n-1 demo\n" );
     for  (n=1;n<=5;n++) { /* 测试1~5个元素 */
         printf ( "_____n=%d_____\n" ,n);
         j=1;
         for  (c=1;c<=n;c++) j=j*c; /* j为n! */
         j*=n*2;
         for  (c=1;c<=j;c++) { /* 测试n*2*n!次 */
             for  (i=0;i<n;i++) d[i]=i; /* 填写0~n-1 */
             for  (i=n;i>0;i--) { /* 打乱0~n-1 */
                 a=i-1;b= rand ()%i;
                 if  (a!=b) {t=d[a];d[a]=d[b];d[b]=t;}
             }
             printf ( "%04d:" ,c);
             for  (i=0;i<n;i++)  printf ( "%d" ,d[i]);
             printf ( "\n" );
         }
     }
     printf ( "shuffle 1..n demo\n" );
     for  (n=1;n<=5;n++) { /* 测试1~5个元素 */
         printf ( "_____n=%d_____\n" ,n);
         j=1;
         for  (c=1;c<=n;c++) j=j*c; /* j为n! */
         j*=n*2;
         for  (c=1;c<=j;c++) { /* 测试n*2*n!次 */
             for  (i=1;i<=n;i++) d[i]=i; /* 填写1~n */
             for  (i=n;i>1;i--) { /* 打乱1~n */
                 a=i;b= rand ()%i+1;
                 if  (a!=b) {t=d[a];d[a]=d[b];d[b]=t;}
             }
             printf ( "%04d:" ,c);
             for  (i=1;i<=n;i++)  printf ( "%d" ,d[i]);
             printf ( "\n" );
         }
     }
}
 
 
 洗牌的算法有很多,这里主要介绍下几种主要的算法。

方法一:每次找一个随机的位置,然后将这54个数放到找的位置中。

步骤:1.用一个整型数组记录各个位置是否已经放置了数,如果放置了则不为0,否则为0。所以在算法开始的时候,初始化此数组每个元素的值都为0.

2.每次产生一个0-53之间的数,看这个位置是否放置了数,如果已经放置了,则继续采用同样的方法找一个随机的位置进行判断,如果这个位置还未放置,则设置此位置。

3.反复执行步骤2,直到所有的位置都放好了数。

代码实现如下:

 1 void shuffle(int *dest,int N)
 2 {
 3     int pos,card;
 4     for (card=1;card<=N;card++)
 5     {
 6         do
 7         {
 8             pos=rand()%(N-1);
 9         } while (dest[pos]!=0);
10
11         dest[pos]=card;
12     }
13 }
14
15 void main()
16 {
17     int dest[54]={0};
18     shuffle(dest,54);
19     for (int i=0;i<54;i++)
20     {
21         cout<<dest[i]<<" ";
22     }
23 }

此方法有个缺点就是很耗时间,因为随着空位置越来越少,寻找会越来越困难。

方法二:就是先对数组进行初始化,然后随机交换数组中任意两个元素。交换的次数越多就越随机。此方法也很简单,而且时间复杂度也比较低,计算量也不大。

代码实现如下:

 1 void shuffle ( int a[], int n )
 2 {
 3     int tmp = 0, p1, p2;
 4     int cnt = rand() % 1023;
 5     while (cnt--)
 6     {
 7         p1 = rand() % n;
 8         p2 = rand() % n;
 9         tmp = a[p1];
10         a[p1] = a[p2];
11         a[p2] = tmp;
12     }
13 }

方法三:它的基本思想是初始化一个vector,顺序加入所有牌,即1-54个数,然后从这个vector中随机抽取一个到另一个vector中,将这个过程执行54次即可完成。

代码的实现如下:

 1 void vectorShuffle(vector<int> &unshuffle,vector<int> &shuffled)
 2 {
 3     unsigned int temp,len=unshuffle.size();
 4     while(len)
 5     {
 6         temp=rand()%(len--);
 7         shuffled.push_back(unshuffle[temp]);
 8         unshuffle.erase(unshuffle.begin()+temp);
 9     }
10 }
11
12
13 void main()
14 {
15     vector<int> uncard,carded;
16     for (int i=0;i<54;i++)
17     {
18         uncard.push_back(i+1);
19     }
20     vectorShuffle(uncard,carded);
21     for (int j=0;j<54;j++)
22     {
23         cout<<carded[j]<<" ";
24     }
25 }

PS:STL中也有一个封装好了的洗牌算法-random_shuffle。使用方法:如果定义有 vector<int> datas,那么直接调用该函数。例如:random_shuffle(datas.begin(),datas.end()); 还可以定义起始迭代器和末尾迭代器来对序列中的某一部分进行洗牌,并且所耗费的时间也较少。

[code=html]

本程序实现了一般扑克牌游戏的洗牌、发牌和理牌过程,程序模拟了对四个玩家的发牌,且能让各自玩家按大小顺序整理自己的手牌。牌的大小顺序按斗地主的规则(但无大小王)。
   本程序定义了一个牌的结构体,其成员变量是两个字符指针。一个指向点数的字符数组,另一个指向花色的字符数组。考虑到牌的整理需要比较两张牌的大小,而字符不太好比较,从而构造了一个辅助函数Value来实现将字符转换为整数。程序得最后还对桌牌和手牌进行了检测,看是否有重复的牌。

算法说明:

发牌算法

Void Deal(CARD *Deck,CARD (*player)[13]){

for (i = 0;i < 4; i++){

// 遍历四个玩家

for (j = 0;j < 13; j++){

// 洗牌第i个玩家的第j张牌是手牌中的第4*j+i张(轮流发牌)

player[i][j] = Deck[4*j+i];

}

}

}

排序算法

void  InsertSort ( CARD *R) { // 对某一玩家R作直接插入排序,有13张牌
     for  ( i=1; i<n; ++i ) {
  temp = R[i]; // 复制为监视哨
  for  ( j=i-1; j>=0; --j )

if(Value(R[j].face)<Value(temp.face))break;// 如果R[j]的牌比temp的小则调出循环,找到比temp小的牌          else if (Value(R[j].face) == Value(temp.face)  // 若face 相同 则比较suit

? Value(R[j].suit) < Value(temp.suit) ? 1 : 0 : 0) break;

for  (k = j-1; k > j;k--)
       R[j+1] = R[j]; // 记录后移
        R[k+1] = temp; // 插入到正确位置
  }
  } // InsertSort
         测试算法

测试桌牌和手牌是否有重复的牌。其思想是从第一张开始与后面的牌一次比较,直到找到一张与待比较的牌相同的或者倒数第二张与最后一张牌比较后终止。

算法描述:

For(I = 0; I < 13; I++ ){

Temp = R[I]; // 将待检测的牌放入temp中

If( Locat(Temp,R)){

Print :ERROR!

break; // 如果Temp在R[I+1…13]中则返回1

}

// 若没有重复的牌不提示任何信息
         变量说明:

一.  张牌(Card)都用一个 strcut card 结构体类型来表示具体定义如下 struct card{

char *face;  // 指针 face 指向不同纸牌的点数

char *suit;  // 指针 suit 指向不同纸牌的花色

} CARD;

再定义两个字符数组分别存储牌的点数和花色:

char *face[] = {"3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K","A", "2"};
char *suit[] = {"/004", "/005", "/003", "/006"};
定义两个CARD型数组 deck[52],player[4][13],deck用与存储桌牌,player用于存储玩家手牌。

[/code]

[cpp] view plain copy print ?
  1. // 扑克牌发牌游戏 -- 第一次上机作业
  2. /* 整个程序由三部分组成
  3. 1.扑克牌初始化
  4. 2.洗牌
  5. 3.发牌
  6. 4.玩家各自将手牌排序(按黑红紫坊从小到大的排序,2点最大)
  7. */
  8. #include <stdio.h>
  9. #include <stdlib.h>
  10. #include <time.h>
  11. #include <iostream>
  12. typedef struct card{
  13. // 每张牌(Card)都用一个 strcut card 结构体类型来表示
  14. char *face;  // 指针 face 指向不同纸牌的点数
  15. char *suit;  // 指针 suit 指向不同纸牌的花色
  16. } CARD;
  17. CARD deck[52], player[4][13];
  18. char *face[] = {"3", "4", "5", "6", "8", "8", "9", "10", "J", "Q", "K","A", "2"};
  19. char *suit[] = {"/004", "/005", "/003", "/006"};
  20. // 比较时用的牌值
  21. void InitDeck (CARD * , char *[] , char *[]);
  22. void Shuffle (CARD *);
  23. void Deal (CARD * , CARD (*)[13]);
  24. void Sort (CARD (*)[13]);
  25. int  Value (char *);
  26. void Disply(void );
  27. int Locat(CARD *, int , CARD , int (*)(CARD, CARD));
  28. int Equl(CARD, CARD);
  29. void Testcard(CARD *, CARD (*)[13]);
  30. int main(){
  31. srand(time(NULL));
  32. InitDeck (deck, face, suit);  // 填新牌
  33. Shuffle (deck);  // 洗牌
  34. Deal (deck, player); // 发牌
  35. Sort (player);  // 对玩家的手牌进行排序
  36. Disply();
  37. Testcard(deck, player);
  38. return 0;
  39. }
  40. void InitDeck (CARD *pDeck, char *pface[], char *psuit[]){
  41. /* wDeck 结构体指针,指向待初始化桌牌 deck[52]
  42. wface 字符数组指针,指向点数
  43. wsuit 字符数组指针,指向花色
  44. 该函数用于初始化桌牌 deck[52]
  45. */
  46. int i;
  47. for (i = 0; i <= 51; i++){
  48. pDeck[i].face = pface[i%13];  // 点数初始化
  49. pDeck[i].suit = psuit[i/13];  // 花色初始化
  50. }
  51. }
  52. void Shuffle (CARD *pDeck){
  53. // 用产生的随机数 j 依次与前面的牌交换
  54. int i, j;
  55. CARD temp;
  56. for (i = 0; i <= 51; i++){
  57. // 洗牌
  58. j = rand()%52;  // 产生0到51之间的随机数
  59. temp = pDeck[i];  // 交换纸牌
  60. pDeck[i] = pDeck[j];
  61. pDeck[j] = temp;
  62. }
  63. }
  64. void Deal (CARD *pDeck , CARD (*pplayer)[13]) {
  65. int i, j;  // i为玩家编号,j为手牌编号
  66. for (i = 0;i < 4; i++){
  67. // 遍历四个玩家
  68. for (j = 0;j < 13; j++){
  69. // 洗牌第i个玩家的第j张牌是手牌中的第4*j+i张(轮流发牌)
  70. pplayer[i][j] = pDeck[4*j+i];
  71. }
  72. }
  73. }
  74. void Sort (CARD (*pplayer)[13]){
  75. // 运用插入排序法分别对四个玩家的手牌排序
  76. int i, j, p, q;  // i为玩家编号,j为手牌编号,p, q 为工作指针
  77. CARD temp;
  78. for (i = 0;i < 4; i++){
  79. // 外层循环,对四个玩家遍历
  80. for (j = 1;j < 13; j++){
  81. // 内层循环,开始排序
  82. // 先比较face 再比较suit
  83. temp = pplayer[i][j];  // 待插入的牌
  84. for (p = j-1; p >= 0; p--){
  85. // 从后往前找出第一个小于temp的牌的序号
  86. if (Value(pplayer[i][p].face) < Value(temp.face))  // 找到比temp小的牌
  87. break;
  88. else if (Value(pplayer[i][p].face) == Value(temp.face)  // 若face 相同 则比较suit
  89. ? Value(pplayer[i][p].suit) < Value(temp.suit) ? 1 : 0 : 0) break;
  90. }
  91. for (q = j-1; q > p; q--){  // 插入操作
  92. pplayer[i][q+1] = pplayer[i][q];
  93. }
  94. pplayer[i][q+1] = temp;
  95. }
  96. }
  97. }
  98. int Value(char *str){
  99. // 字符映射函数
  100. int i = 0;
  101. while(i < 4 && *str - *suit[i++]) ;
  102. if (i == 4) {
  103. i = 0;
  104. while(i < 13 && *str - *face[i++]) ;
  105. }
  106. return i;
  107. }
  108. void Disply(){
  109. using namespace std;
  110. int i, j;
  111. cout << "player1" << "player2" << "player3" << "player4" << endl;
  112. for (j = 0; j < 13; j++){
  113. // j为第 j 行
  114. for (i = 0; i < 4; i++){
  115. // i为第 i 位玩家
  116. cout << player[i][j].suit << player[i][j].face << "/t";
  117. if (i == 3) cout << endl;
  118. }
  119. }
  120. }
  121. void Testcard(CARD *pdeck, CARD (*pplayer)[13]){
  122. using namespace std;
  123. int i, j;
  124. CARD temp;
  125. for (i = 0; i < 51; i++){
  126. temp = pdeck[i];
  127. if (Locat(&deck[i+1],52,temp,Equl)){
  128. cout << "error! the deckcard are same"<< endl;
  129. break;
  130. }
  131. }  // 测试桌牌是否重复
  132. for (i = 0; i < 4; i++){
  133. for (j = 0; j < 13; j++){
  134. temp = pplayer[i][j];
  135. if(Locat(&pplayer[i][j+1],13,temp,Equl)){
  136. cout << "error! the player's card are same" << endl;
  137. break;
  138. }
  139. }  // 测试手牌是否重复
  140. }
  141. }
  142. int Locat(CARD *pdecks, int len, CARD deck, int (*compare)(CARD , CARD )){
  143. // 若牌堆中有满足 compare() 的牌则返回1,没有则返回0
  144. // len牌堆的长度 有13和52
  145. CARD *p;
  146. int i = 0;  // 第一张牌
  147. p = pdecks;  // 第一张牌的地址
  148. while (i < len && compare(*p++ , deck))
  149. ++i;
  150. if(i == len)
  151. return 0;
  152. else
  153. return 1;
  154. }
  155. int Equl(CARD card1, CARD card2){
  156. // 比较card1 和 card2 是否相同,相同则返回0
  157. // 不同返回1
  158. int fval, sval;
  159. // fval 为点数字符差,sval 为花色字符差
  160. fval = *card1.face - *card2.face;
  161. sval = *card1.suit - *card2.suit;
  162. if(fval || sval)
  163. return 1;  // 有一个不同就不相同
  164. else
  165. return 0;
  166. }

洗牌、发牌算法 (打乱扑克牌顺序)相关推荐

  1. C语言 扑克牌洗牌发牌统计同花顺个数程序

    目录 一.2个算法关键点 关键点1:洗牌算法 关键点2:查找同花顺算法 二.运行结果 三.完整代码 题目: 一张扑克牌可用结构类型描述,一副扑克牌的52张牌则是一个结构数组. 1.试编写洗牌函数和供4 ...

  2. 扑克牌洗牌发牌java代码_java实战(一)之Java模仿斗地主洗牌发牌小游戏

    斗地主是全国范围内的一种桌面游戏,尽管全国各种类型,但大同小异.本节我们先来实现一下斗地主中的简单洗牌.发牌和看牌功能. 按照斗地主的规则,完成洗牌发牌的动作.具体规则为使用 54 张牌打乱顺序,3 ...

  3. Java项目:模拟扑克牌洗牌发牌排序

    用JAVA实现简单的扑克牌洗牌发牌并排序,首先写代码之前需要明确需要分哪些步骤: 创建一个HashMap集合对象 创建一个ArrayList集合对象 生成一副扑克牌 将扑克牌按照键值关系添加到Hash ...

  4. 用JAVA实现简单的扑克牌洗牌发牌并排序

    用JAVA实现简单的扑克牌洗牌发牌并排序,首先写代码之前需要明确需要分哪些步骤: 创建一个HashMap集合对象 创建一个ArrayList集合对象 生成一副扑克牌 将扑克牌按照键值关系添加到Hash ...

  5. 纸牌游戏洗牌发牌排序算法设计

    纸牌游戏洗牌发牌排序算法设计 本文提供纸牌游戏设计制作的基础部分,即洗牌,发牌,牌张排序排列显示的算法. 以及游戏开始时间使用时间的显示.我是用简单的C语言编译器MySpringC在安卓手机上编写的. ...

  6. java中Map集合、模拟斗地主洗牌发牌、JDK9对集合添加的优化

    1.1 Map集合概述 Map集合概述 Map==>映射(一个对应一个) Map是一个接口,只要实现了该接口的类都是双列集合. 双列集合每次存储元素时都需要存储两个元素,一个元素称为键,一个元素 ...

  7. java实现斗地主洗牌发牌功能

    设计思路 初始化54张牌 洗牌 发牌 给玩家手牌排序 展示玩家手牌及3张底牌 实现思路 首先是54张牌要选择一种数据结构存放,这里选择List和Map集合都可以,我这里选择的是Map 要区分4种花色, ...

  8. 模拟斗地主洗牌发牌-JAVA

    1.1案例介绍 按照斗地主的规则,完成洗牌发牌的动作. 具体规则: 1.组装54张扑克牌 2.将54张牌顺序打乱 3.三个玩家参与游戏,三人交替摸牌,,每人17张牌,最后三张留作底牌. 4.查看三人各 ...

  9. 【案例 6-4】斗地主洗牌发牌

    [案例介绍] 1.任务描述 斗地主的扑克牌游戏,相信许多人都会玩,本例要求编写一个斗地主的洗牌发牌程序,要求按照斗地主的规则完成洗牌发牌的过程.一副扑克总共有 54 张牌,牌面由花色和数字组成(包括 ...

最新文章

  1. Linux图形分区编辑器 GParted Live 1.0 Beta 发布
  2. P3649-[APIO2014]回文串【PAM】
  3. [渝粤教育] 西南科技大学 程序设计语言VB 在线考试复习资料(1)
  4. 【C++深度剖析教程25】继承中的构造与析构
  5. python升级命令debian_debian python 2.7.11 升级
  6. Ionic JPush极光推送 插件实例
  7. SpringBoot 中 get/post 请求处理方式,以及requestboy为Json时的处理
  8. MyBatis(二)------使用JDBC编程问题总结
  9. Java实现 LeetCode 75 颜色分类
  10. C#UDP广域网,局域网通信-原理分析
  11. c语言编程求百位和个位的差,对任意一个键盘输入的3位整数,求出它的个位、十位和百位。 一道c语言题目?...
  12. 致我们失去但美好回忆的青春
  13. aect17定义_AECT-05定义与94定义的区别?
  14. 高清屏智能手表PSRAM存储芯片APS6404L-SQR-ZR
  15. 适合理工直男的钟平老师逻辑英语学习笔记
  16. 【Modbus 】Modbus 协议
  17. 穷人跟懒人 富人跟勤快人
  18. 端口复用|端口重映射
  19. 手机抓包+注入黑科技HttpCanary——最强大的Android抓包注入工具
  20. 手机端,h5,获取位置,经度纬度

热门文章

  1. Android 仿今日头条、网易新闻的频道管理
  2. android manifest相关属性
  3. 如何更换安装鸿蒙系统,华为鸿蒙系统2.0如何进行安装?鸿蒙系统2.0安装方法详细介绍...
  4. 红魔8pro pro+机型NX729J解锁bl 获取root教程
  5. 保险私有云 IaaS 资源池选型与演进之路 | SmartX 客户实践
  6. 淘宝店铺商品接口 item_search_shop数据获取、拼多多关键词、1688原数据接口、淘宝商品详情sku信息等各大电商平台接口采集调用展示
  7. Visionpro从小白到大佬,第一章了解工具名称和用途
  8. 详解DNS服务、DNS解析、DNS劫持和污染
  9. 中国能源建设合并葛洲坝又有最新进展,葛洲坝即将终止上市
  10. 元宇宙被这个圈子带火了一波,是我没想到的