洗牌、发牌算法 (打乱扑克牌顺序)

2023-05-16

 
#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.       
  102.     while(i < 4 && *str - *suit[i++]) ;  
  103.     if (i == 4) {  
  104.     i = 0;  
  105.     while(i < 13 && *str - *face[i++]) ;  
  106.     }  
  107.     return i;  
  108. }  
  109. void Disply(){  
  110.     using namespace std;  
  111.     int i, j;  
  112.       
  113.     cout << "player1" << "player2" << "player3" << "player4" << endl;  
  114.     for (j = 0; j < 13; j++){  
  115.         // j为第 j 行   
  116.         for (i = 0; i < 4; i++){  
  117.             // i为第 i 位玩家   
  118.             cout << player[i][j].suit << player[i][j].face << "/t";  
  119.             if (i == 3) cout << endl;  
  120.         }  
  121.     }  
  122. }  
  123. void Testcard(CARD *pdeck, CARD (*pplayer)[13]){  
  124.     using namespace std;  
  125.     int i, j;  
  126.     CARD temp;  
  127.    
  128.     for (i = 0; i < 51; i++){  
  129.         temp = pdeck[i];  
  130.         if (Locat(&deck[i+1],52,temp,Equl)){  
  131.             cout << "error! the deckcard are same"<< endl;  
  132.             break;  
  133.         }  
  134.     }  // 测试桌牌是否重复   
  135.     for (i = 0; i < 4; i++){  
  136.         for (j = 0; j < 13; j++){  
  137.             temp = pplayer[i][j];  
  138.             if(Locat(&pplayer[i][j+1],13,temp,Equl)){  
  139.                 cout << "error! the player's card are same" << endl;  
  140.                 break;  
  141.             }  
  142.         }  // 测试手牌是否重复   
  143.     }  
  144. }  
  145. int Locat(CARD *pdecks, int len, CARD deck, int (*compare)(CARD , CARD )){  
  146.     // 若牌堆中有满足 compare() 的牌则返回1,没有则返回0   
  147.     // len牌堆的长度 有13和52   
  148.     CARD *p;  
  149.     int i = 0;  // 第一张牌  
  150.     p = pdecks;  // 第一张牌的地址   
  151.     while (i < len && compare(*p++ , deck))   
  152.         ++i;  
  153.     if(i == len)  
  154.         return 0;  
  155.     else  
  156.         return 1;  
  157. }  
  158. int Equl(CARD card1, CARD card2){  
  159.     // 比较card1 和 card2 是否相同,相同则返回0  
  160.     // 不同返回1   
  161.     int fval, sval;  
  162.     // fval 为点数字符差,sval 为花色字符差   
  163.     fval = *card1.face - *card2.face;  
  164.     sval = *card1.suit - *card2.suit;  
  165.     if(fval || sval)  
  166.         return 1;  // 有一个不同就不相同  
  167.     else   
  168.         return 0;  
  169. }  

 

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

洗牌、发牌算法 (打乱扑克牌顺序) 的相关文章

随机推荐

  • java-HttpURLConnection 发送POST请求 传递参数或JSON数据

    java HttpURLConnection 发送POST请求 传递参数或JSON数据 需求 三方接口 restful请求 POST方式 参数JSON 接收数据JSON HttpURLConnection发送POST请求 span clas
  • 一次DPDK-L3FWD-ACL的问题排查

    其实说起来不是什么大问题 xff0c 所有的配置按照下面这个来的 包括写入arp和配置默认路由 xff0c 不过我用的单文件那个l3fwd acl https blog csdn net sinat 20184565 article det
  • Centos7关机和重启前执行自定义脚本

    0 前言 如果你在linux系统上启动了很多自己的服务 xff0c 如果直接执行系统的reboot或者shutdown命令 xff0c 可能会发生一些异常的现象 xff0c 所以我们在重启和关机前 xff0c 应该按照我们的意愿去自定义清理
  • Ubuntu20.04系统WineHQ7.0安装微信

    提供3种Ubuntu系统安装微信的方法 xff0c 在Ubuntu20 04上验证都ok 1 WineHQ7 0安装微信 xff1a ubuntu20 04安装最新版微信 可以支持微信最新版 xff0c 但是适配的不是特别好 xff1b 比
  • 0402 0603 0805 1206焊盘封装尺寸

  • Ubuntu 上使用hexo+github打造个人blog

    最近一段时间想要搭建个人的blog xff0c 然后好好写博客 在网上大致看了下 xff0c 一致的推荐hexo来搭建个人blog xff0c 而且可以通过github挂载在公网上去 xff0c 由于个人使用的Ubuntu 16 04 xf
  • 简单使用Python爬虫爬取淘宝网页商品信息

    最近在学习爬虫 xff0c 本人还是入门级的小白 xff0c 自己跟着老师写了一些代码 xff0c 算是自己的总结 xff0c 还有一些心得 xff0c 跟大家分享一下 xff0c 如果不当 xff0c 还请各位前辈斧正 这是代码 xff1
  • 非常详细的范式讲解(1NF/2NF/3NF/BCNF)

    范式 范式在计算机方面运用广泛 xff0c 在计算机二级三级均有涉及到 xff0c 今天就来讲讲范式 要是能够认真的看完 xff0c 相信一定能够理解的 在讲范式之前 xff0c 我们先来了解有关范式的基本概念 xff0c 听懂了有利于后面
  • ubuntu18.04无法安装libesd0-dev完美解决办法

    ubuntu18 04无法安装libesd0 dev 解决办法 解决办法 span class token comment 在行尾添加如下一行的内容 span span class token function sudo span vim
  • SystemUI学习记录

    OS android 7 1 由于项目需要需要对SystemUI进行定制化 xff0c 需求比较紧急 xff0c 没有很深入的去了解其整体代码 xff0c 仅仅记录下我的用到修改记录如下 xff1a 目录中有 或者 由于在不同的系统环境拷贝
  • informer

    list watch机制 list watch有两部分组成 xff0c 分别是list和watch list非常好理解 xff0c 就是调用资源的list API罗列资源 xff0c 基于HTTP短链接实现 xff1b watch则是调用资
  • kubelet - container manager

    containerManager 负责 node 节点上运行的容器的配置信息 xff0c 如 cgroup cpu device xff1b pod的创建流程参考 xff1a http www tianfeiyu com p 61 2825
  • 理解TCP/IP网络通信原理

    为什么会有TCP IP协议 在世界上各地 xff0c 各种各样的电脑运行着各自不同的操作系统为大家服务 xff0c 这些电脑在表达同一种信息的时候所使用的方法是千差万别 就好像圣经中上帝打乱了各地人的口音 xff0c 让他们无法合作一样 计
  • python 正则表达式点号与'\n'符号的问题

    匹配除了制表符和换行符之外的所有字符 前面的元字符出现任意次 xff0c 含0 43 前面的元字符出现一次或多次 xff1f 非贪婪模式 re S可以匹配多行 S s 匹配所有字符 所以 可以使用 s S 取代 在Python中可以使用方法
  • virsh的使用与实例步骤安装虚拟机

    Libvirt 有两种控制方式 xff0c 命令行和图形界面 图形界面 xff1a 通过执行名 virt manager xff0c 启动 libvirt 的图形界面 xff0c 在图形界面下可以一步一步的创建虚拟机 xff0c 管理虚拟机
  • Jboss低版本项目热部署到Jboss7

    Jboss低版本项目热部署到Jboss7 1 Jboss7 新特性 1 1 构建在 Modular Service Container上 xff0c 充分地利用了多核处理器的能力 xff0c 并发 按需启动服务 xff0c 启动速度更快 占
  • devstack安装openstack

    devstack是一套用来给开发人员快速部署Openstack开发环境的脚本 xff0c 其实对于整个安装过程没有什么好说的 xff0c 因为脚本写的很完善 xff0c 全程无脑式安装也没什么大问题 xff0c 但是因为公司里的网络环境不给
  • RBAC vs ABAC

    K8s在1 3版本中发布了alpha版的基于角色的访问控制 xff08 Role based Access Control xff0c RBAC xff09 的授权模式 相对于基于属性的访问控制 xff08 Attribute based
  • Attach detach controller manager 流程

    一 xff1a 初始化 1 adc 调用 InitPlugins 初始化 volumeplugins 1 1 初始化 dynamicplugins Currentlyonly Flexvolume plugins are dynamical
  • 洗牌、发牌算法 (打乱扑克牌顺序)

    img alt src https img blog csdn net 20130504122806254 include lt stdio h gt include lt stdlib h gt include lt time h gt