如何改进随机发牌程序以避免重复输出卡牌?
如何改进随机发牌程序以避免重复输出卡牌?
嘿,我明白你遇到的问题了——随机发牌时重复输出确实很头疼,你尝试用布尔值回滚的思路方向是对的,但可能逻辑没捋顺。我给你两种靠谱的解决方案,你可以根据需求来选:
方法一:用标记数组记录已发卡牌
这个思路最直观,就是用一个二维数组来标记已经发过的牌,每次生成新牌前先检查是否已经被发过,如果重复就重新生成,直到拿到没发过的牌。
修改后的代码如下:
#include <stdio.h> #include <time.h> #include <stdlib.h> #define SUITS 4 #define RANKS 13 int main() { int num_cards, rank, suit; const char rank_code[] = {'2', '3', '4', '5', '6', '7', '8', '9', 't', 'j', 'q', 'k', 'a'}; const char suit_code[] = {'c', 'd', 'h', 's'}; // 创建标记数组,初始化为0(0表示未发,1表示已发) int dealt[SUITS][RANKS] = {0}; srand(time(NULL)); printf("Enter number of cards in hand: "); scanf("%d", &num_cards); // 先做个简单的输入校验,避免超过总牌数 if (num_cards < 1 || num_cards > SUITS * RANKS) { printf("Invalid number! Please enter between 1 and 52.\n"); return 1; } printf("Your hand: \n"); for (int i = 0; i < num_cards; ) { suit = rand() % SUITS; rank = rand() % RANKS; // 检查这张牌是否未被发过 if (!dealt[suit][rank]) { dealt[suit][rank] = 1; // 标记为已发 printf("%c%c ", rank_code[rank], suit_code[suit]); i++; // 只有拿到有效牌才递增循环变量 } // 如果重复了,就不递增i,重新生成 } printf("\n"); return 0; }
这里要注意,我把循环里的i++移到了拿到有效牌的分支里——如果生成了重复牌,就不递增i,让循环重新生成,这样就不用手动回滚i了,逻辑更清晰。另外加了个输入校验,防止用户输入超过52的数。
方法二:Fisher-Yates洗牌算法(更高效)
如果要发的牌数量接近52张,方法一可能会因为重复生成随机数导致效率降低。这时候用Fisher-Yates洗牌算法就更合适:先把所有牌放在一个数组里,打乱顺序后直接取前N张,完全不会有重复问题。
代码示例:
#include <stdio.h> #include <time.h> #include <stdlib.h> #define SUITS 4 #define RANKS 13 #define TOTAL_CARDS (SUITS * RANKS) int main() { int num_cards; const char rank_code[] = {'2', '3', '4', '5', '6', '7', '8', '9', 't', 'j', 'q', 'k', 'a'}; const char suit_code[] = {'c', 'd', 'h', 's'}; // 创建数组存储所有牌的索引(0-51) int deck[TOTAL_CARDS]; srand(time(NULL)); printf("Enter number of cards in hand: "); scanf("%d", &num_cards); if (num_cards < 1 || num_cards > TOTAL_CARDS) { printf("Invalid number! Please enter between 1 and 52.\n"); return 1; } // 初始化牌组 for (int i = 0; i < TOTAL_CARDS; i++) { deck[i] = i; } // Fisher-Yates洗牌 for (int i = TOTAL_CARDS - 1; i > 0; i--) { int j = rand() % (i + 1); // 交换i和j位置的元素 int temp = deck[i]; deck[i] = deck[j]; deck[j] = temp; } printf("Your hand: \n"); // 取前num_cards张牌 for (int i = 0; i < num_cards; i++) { int card = deck[i]; int suit = card / RANKS; int rank = card % RANKS; printf("%c%c ", rank_code[rank], suit_code[suit]); } printf("\n"); return 0; }
这个方法的优势是不管发多少牌,效率都很高,而且逻辑上完全避免了重复,因为我们是从打乱的完整牌组里取牌,和现实中洗牌发牌的逻辑一致。
内容来源于stack exchange
相关产品推荐
相关产品推荐

