学习笔记—C和python模拟洗发牌过程
前言
这久学习结构体数组的时候接触到的一个程序实列—模拟洗牌和发牌
一副扑克有52张牌,分为4种花色(Suit):黑桃(Spades)、红桃(Hearts)、草花(Clubs)、方块(Diamonds)。每种花色又有13张牌面(Face):A,2,3,4,5,6,7,8,9,10, Jack, Queen, King。
进入正文
问题分析: 显然每张牌由两个元素组成:花色、牌面。为了表示一张牌,我们可以设计如下的结构体表示一张牌的花色和牌面,花色和牌面分别用字符数组来表示:
struct CARD
{
char suit[10]; //花色
char face[10]; //牌面
};
完成发牌的过程就是将52张牌按照随机顺序存放。
首先,需要设计一个由52个元素组成的整型数组result用来存放发牌结果,resultf0]代表发的第1张牌,result[1]代表发的第2张牌,……,result[51]代表发的最后一张牌。
其次,用上面声明的structCARD结构体类型定义一个有52个元素的结构体数组card,按花色与牌面的顺序存放52张牌,即card[0]=("Spades”,“A”, card[1]=("Spades”,“2”),…card[51]=(“Diamonds”,“K”)。
然后,用函数rand)随机生成一个0~51间的随机数(假设为3),存于result[0]中,则result[0]代表第1张要发的牌是card[3],card[3]=("Spades”,"4”)表示第1张发的牌是黑桃4。其余类推,再发第2张牌,······,直到发完52张牌。
将上述过程用算法描述如下:
step 1 定义两个有52个元素的一维结构体数组:
int result[52] ; //存放洗牌结果
struct CARD card[52]; // 顺序存放52张扑克牌
step 2产生0-51的随机数,将其放于result内。
step 3i=i+1,判断i是否大于等于52。如果>=52,则转step 4;否则,转step2。
step 4输出结果。
细心的读者会发现,该算法存在一个致命的问题:在重复step2时,产生的随机数可能与以前产生的随机数相同。如果相同,则意味着在52张牌中会出现两张以上相同的牌。为避免出现此问题,需增加一步,判断新出现的随机数以前是否出现过。若出现过,则放弃此次产生的随机数,重新生成;若未出现过,则保留此次生成的随机数。修改算法如下:
step 1 定义两个有52个元素的一维结构体数组 result和card。
step 2 产生0~51的随机数 m,将其放于result[内。
step 3判断 result在以前(resutf0]result[i-1])是否出现过。若出现过,则转step2:若没出现过,则tt。step 4 判断i是否大于等于52。如果>=52,则转 step 5;否则,转step 2。
step 5输出洗牌结果。
编写程序如下:
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <stdlib.h>
struct CARD
{
char suit[10]; //花色
char face[10]; //牌面
};
int main(int argc, char const *argv[])
{
char *Suit[] = {"Spades", "Hearts", "Clubs", "Diamonds"};
char *Face[] = {"A", "2", "3", "4", "5", "6", "7", "8", "9", "10",
"Jack", "Queen", "King"};
int i, j;
int result[52] = {0};
struct CARD card[52];
for (i=0; i<52; i++)
{
result[i] = -1; //将数组result各元素赋值为-1,可选择0~51外的任意数
}
for (i=0; i<52; i++) //将card[i]按顺序赋值
{
strcpy(card[i].suit, Suit[i/13]);
strcpy(card[i].face, Face[i%13]);
}
srand(time(NULL)); //产生随机数种子
i = 0; //清发牌计数值
while (1)
{
result[i] = rand() % 52; //产生0~51的随机数
for (j=0; j<i; j++)
{ //如果产生的随机数以前出现过,则放弃此次产生随机数
if (result[j] == result[i]) break;
}
if (j<i) continue; //出现相同的值,重新产生随机数
i++; //若没有出现相同的值,计数器+1
if (i>=52) break; //若52个随机数产生完毕,则退出循环
}
for (i=0; i<52; i++) //输出发牌结果
{
printf("%10s %5s\n", card[result[i]].suit, card[result[i]].face);
}
return 0;
}
该方法虽能实现题目要求,但仍然存在缺陷,因为随着随机数数量的增加,新的随机数与已经产生的随机数相同的可能性越来越大。假设已产生了第51个随机数,当产生第52个随机数时,0~51之间只有一个数没产生,该数产生的几率非常低,因而有可能出现算法延迟问题。
为此,用for循环语句打乱52张牌(数组下标0~51)的排列顺序。每次循环,程序都选择一个0~51 的随机数 j,然后将数组中当前的数组元素 card]与随机选出的数组元素card[j]互换。经52次交换,即可把牌洗好。算法描述如下:
step 1声明结构体类型,定义有52个元素的一维结构体数组card。
step 2将扑克牌按顺序放在card[52]中。
step 3 将计数器初始化为零,即i=0。
step 4产生一个0~51的随机数j,将card[]与card[]的内容交换。
step 5 计数器加1,即i++。判断i是否大于等于51。如果>=52,则转step 6;否则,转step 4。
step 6输出洗牌结果。
由此改进后的最终程序如下:
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <stdlib.h>
struct CARD
{
char suit[10]; //花色
char face[10]; //牌面
};
int main()
{
char *Suit[] = {"Spades", "Hearts", "Clubs", "Diamonds"};
char *Face[] = {"A", "2", "3", "4", "5", "6", "7", "8", "9", "10",
"Jack", "Queen", "King"};
int i, j;
int result[52] = {0};
struct CARD card[52], temp;
for (i=0; i<52; i++) //将牌顺序存放
{
strcpy(card[i].suit, Suit[i/13]);
strcpy(card[i].face, Face[i%13]);
}
srand(time(NULL)); //产生随机数种子
for (i=0; i<52; i++) //洗牌过程,打乱牌的先后顺序
{
j = rand() % 52;
temp = card[i];
card[i] = card[j];
card[j] = temp;
}
for (i=0; i<52; i++) //输出发牌结果
{
printf("%10s%10s\n", card[i].suit, card[i].face);
}
system("pause");
return 0;
}
运行结果如下:
下面是我用python写的相同的过程,确实比C要简单不少
import random
# 列表储存花色和牌面
suit = ["Spades", "Hearts", "Clubs", "Diamonds"]
face = ["A", "2", "3", "4", "5", "6", "7", "8", "9", "10",
"Jack", "Queen", "King"]
temp = [] #储存洗牌结果
res = [] #储存发牌结果
# 将花色和牌面组合
for i in range(4):
for j in range(13):
temp = face[j] + " " + (suit[i])
res.append(temp)
# 洗牌并输出结果
li = random.sample(res, len(res))
for i in range(len(res)):
print("{:^10s}".format(li[i]), "\033[0;34m") #格式化输出
运行结果
好了,本文到这也就结束了,这便是我的学习笔记。