学习笔记---C和python模拟洗发牌过程

学习笔记—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")  #格式化输出

运行结果
在这里插入图片描述
好了,本文到这也就结束了,这便是我的学习笔记。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Python-AI Xenon

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值