C 程序 - 为什么我的洗牌功能不能正常工作?
C Program - Why is my card shuffling function not working correctly?
我正在创建一个纸牌洗牌函数,它遍历传递的数组,调用另一个函数 swap 以将传递的每个元素更改为另一个,并将该元素存储到另一个名为 shuffledDeck 的数组中。然后我从函数中返回洗牌后的牌组。我在调用该函数时得到了一些洗牌,但大多数都留在同一个地方。我不确定为什么甲板的其余部分不像其他人那样移动。谁能指出我的函数有什么问题可以解释输出?
初始化的卡片组与我从我的函数中获得的输出。
Unshuffled
[AH] [2H] [3H] [4H] [5H] [6H] [7H] [8H] [9H] [10H] [JH] [QH] [KH]
[AD] [2D] [3D] [4D] [5D] [6D] [7D] [8D] [9D] [10D] [JD] [QD] [KD]
[AC] [2C] [3C] [4C] [5C] [6C] [7C] [8C] [9C] [10C] [JC] [QC] [KC]
[AS] [2S] [3S] [4S] [5S] [6S] [7S] [8S] [9S] [10S] [JS] [QS] [KS]
Shuffled
[AD] [AH] [2H] [3H] [4H] [5H] [6H] [7H] [8H] [9H] [10H] [JH] [QH]
[KH] [KH] [2D] [3D] [4D] [5D] [6D] [7D] [8D] [9D] [10D] [JD] [QD]
[KD] [AC] [2C] [3C] [4C] [5C] [6C] [7C] [8C] [9C] [10C] [JC] [QC]
[KC] [AS] [2S] [3S] [4S] [5S] [6S] [7S] [8S] [9S] [10S] [JS] [QS]
我的功能
void swap(char *(*deck)[13], int r, int c)
{
time_t t;
srand((unsigned) time(&t));
int rowToSwap = -1; //will hold generated row and column to swap
int colToSwap = -1;
while(rowToSwap < 0 || rowToSwap > 3 && colToSwap < 0 || colToSwap > 12){ //while row to swap and col to swap are outside appropriate ranges
rowToSwap = rand() % (3 + 1 - 0);
colToSwap = rand() % (12 + 1 - 0);
}
char *temp = deck[rowToSwap][colToSwap]; //swap by elements using a temp holder
deck[rowToSwap][colToSwap] = deck[r][c];
deck[r][c] = temp;
}
char *(*shuffleDeck(char *(*deck)[13]))[13]
{
char *(*shuffledDeck)[13] = malloc(4 * sizeof(*shuffledDeck));
for(int i = 0; i < 4; i++){
for(int j = 0; j < 13; j++){
swap(deck, i, j);
shuffledDeck[i][j] = deck[i][j];
}
}
return shuffledDeck;
}
首先,您可以删除 while 循环,只为行和列调用一次 rand % value。其次,您应该用常量替换那些整数。为什么用 3+1-0
而不是 NUM_ROWS
?第三,您应该只调用 srand 一次,否则您会重置随机数生成器的种子。
最后,你代码中的逻辑错误。我认为你错过了最后一个标签的函数定义,但看起来问题是在交换中,你交换了卡片,并且在你的 shuffle_deck 函数中你还设置 shuffled_deck 等于洗牌值。要么就地交换牌组中的牌(即交换),要么复制一份并洗牌。
@chux 建议使用 Fisher-Yates 洗牌。 Fisher-Yates 的“由内而外”变体允许新牌组在从旧牌组复制时洗牌。 Fisher-Yates 在一维数组上工作,但可以使用一点数学来修改它以用于二维数组:
char *(*shuffleDeck(char *(*deck)[13]))[13]
{
char *(*shuffledDeck)[13] = malloc(4 * sizeof(*shuffledDeck));
/* Use "inside out" Fisher-Yates shuffle, modified for 2-D array. */
for(int i = 0; i < 4; i++) {
for(int j = 0; j < 13; j++) {
int b = randInt(i * 13 + j + 1);
int a = b / 13;
b -= 13 * a;
/*
* Note: a*13+b <= i*13+j,
* so &shuffledDeck[a][b] <= &shuffledDeck[i][j]
*/
if (a != i || b != j) {
shuffledDeck[i][j] = shuffledDeck[a][b];
}
shuffledDeck[a][b] = deck[i][j];
}
}
return shuffledDeck;
}
上面使用的b = randInt(i * 13 + j + 1)
函数调用returns一个小于其参数值的随机整数。参数 i * 13 + j + 1
比目前洗牌的张数多 1。 a = b / 13;
b -= a * 13;
语句将该随机数转换为行 a
、列 b
。至关重要的是,a * 13 + b <= i * 13 + j
,因此随机位置 (a,b) 小于或等于位置 (i,j)。
如果随机位置(a,b)小于当前位置(i,j),则将洗好的牌组中的一张牌从位置(a,b)复制到位置(i,j)。那么洗牌后的随机位置 (a,b) 要么从未设置过(如果随机位置与当前位置相同),要么包含一个陈旧的副本。在任何一种情况下,未洗牌的当前位置 (i,j) 的牌都会被复制到洗牌后的随机位置 (a,b)。
最后,未洗牌的所有牌都被复制到洗牌后随机但唯一的位置。
下面是上面使用的 randInt
函数的实现:
#include <stdlib.h>
/* Return random integer from 0 to n-1 (for n in range 1 to RAND_MAX+1u) */
int randInt(unsigned int n) {
unsigned int x = (RAND_MAX + 1u) / n;
unsigned int limit = x * n;
int s;
do {
s = rand();
} while (s >= limit);
return s / x;
}
请注意,简单的 rand() % n
通常会导致略有偏差的结果。上面的 randInt(n)
函数使用 rand()
但会产生无偏差的结果。
随机数生成器需要由 srand
函数播种。这应该在程序中只完成一次,例如来自 main
函数:
#include <stdlib.h>
#include <time.h>
int main(void)
{
srand(time(NULL));
/* other stuff */
}
我正在创建一个纸牌洗牌函数,它遍历传递的数组,调用另一个函数 swap 以将传递的每个元素更改为另一个,并将该元素存储到另一个名为 shuffledDeck 的数组中。然后我从函数中返回洗牌后的牌组。我在调用该函数时得到了一些洗牌,但大多数都留在同一个地方。我不确定为什么甲板的其余部分不像其他人那样移动。谁能指出我的函数有什么问题可以解释输出?
初始化的卡片组与我从我的函数中获得的输出。
Unshuffled
[AH] [2H] [3H] [4H] [5H] [6H] [7H] [8H] [9H] [10H] [JH] [QH] [KH]
[AD] [2D] [3D] [4D] [5D] [6D] [7D] [8D] [9D] [10D] [JD] [QD] [KD]
[AC] [2C] [3C] [4C] [5C] [6C] [7C] [8C] [9C] [10C] [JC] [QC] [KC]
[AS] [2S] [3S] [4S] [5S] [6S] [7S] [8S] [9S] [10S] [JS] [QS] [KS]
Shuffled
[AD] [AH] [2H] [3H] [4H] [5H] [6H] [7H] [8H] [9H] [10H] [JH] [QH]
[KH] [KH] [2D] [3D] [4D] [5D] [6D] [7D] [8D] [9D] [10D] [JD] [QD]
[KD] [AC] [2C] [3C] [4C] [5C] [6C] [7C] [8C] [9C] [10C] [JC] [QC]
[KC] [AS] [2S] [3S] [4S] [5S] [6S] [7S] [8S] [9S] [10S] [JS] [QS]
我的功能
void swap(char *(*deck)[13], int r, int c)
{
time_t t;
srand((unsigned) time(&t));
int rowToSwap = -1; //will hold generated row and column to swap
int colToSwap = -1;
while(rowToSwap < 0 || rowToSwap > 3 && colToSwap < 0 || colToSwap > 12){ //while row to swap and col to swap are outside appropriate ranges
rowToSwap = rand() % (3 + 1 - 0);
colToSwap = rand() % (12 + 1 - 0);
}
char *temp = deck[rowToSwap][colToSwap]; //swap by elements using a temp holder
deck[rowToSwap][colToSwap] = deck[r][c];
deck[r][c] = temp;
}
char *(*shuffleDeck(char *(*deck)[13]))[13]
{
char *(*shuffledDeck)[13] = malloc(4 * sizeof(*shuffledDeck));
for(int i = 0; i < 4; i++){
for(int j = 0; j < 13; j++){
swap(deck, i, j);
shuffledDeck[i][j] = deck[i][j];
}
}
return shuffledDeck;
}
首先,您可以删除 while 循环,只为行和列调用一次 rand % value。其次,您应该用常量替换那些整数。为什么用 3+1-0
而不是 NUM_ROWS
?第三,您应该只调用 srand 一次,否则您会重置随机数生成器的种子。
最后,你代码中的逻辑错误。我认为你错过了最后一个标签的函数定义,但看起来问题是在交换中,你交换了卡片,并且在你的 shuffle_deck 函数中你还设置 shuffled_deck 等于洗牌值。要么就地交换牌组中的牌(即交换),要么复制一份并洗牌。
@chux 建议使用 Fisher-Yates 洗牌。 Fisher-Yates 的“由内而外”变体允许新牌组在从旧牌组复制时洗牌。 Fisher-Yates 在一维数组上工作,但可以使用一点数学来修改它以用于二维数组:
char *(*shuffleDeck(char *(*deck)[13]))[13]
{
char *(*shuffledDeck)[13] = malloc(4 * sizeof(*shuffledDeck));
/* Use "inside out" Fisher-Yates shuffle, modified for 2-D array. */
for(int i = 0; i < 4; i++) {
for(int j = 0; j < 13; j++) {
int b = randInt(i * 13 + j + 1);
int a = b / 13;
b -= 13 * a;
/*
* Note: a*13+b <= i*13+j,
* so &shuffledDeck[a][b] <= &shuffledDeck[i][j]
*/
if (a != i || b != j) {
shuffledDeck[i][j] = shuffledDeck[a][b];
}
shuffledDeck[a][b] = deck[i][j];
}
}
return shuffledDeck;
}
上面使用的b = randInt(i * 13 + j + 1)
函数调用returns一个小于其参数值的随机整数。参数 i * 13 + j + 1
比目前洗牌的张数多 1。 a = b / 13;
b -= a * 13;
语句将该随机数转换为行 a
、列 b
。至关重要的是,a * 13 + b <= i * 13 + j
,因此随机位置 (a,b) 小于或等于位置 (i,j)。
如果随机位置(a,b)小于当前位置(i,j),则将洗好的牌组中的一张牌从位置(a,b)复制到位置(i,j)。那么洗牌后的随机位置 (a,b) 要么从未设置过(如果随机位置与当前位置相同),要么包含一个陈旧的副本。在任何一种情况下,未洗牌的当前位置 (i,j) 的牌都会被复制到洗牌后的随机位置 (a,b)。
最后,未洗牌的所有牌都被复制到洗牌后随机但唯一的位置。
下面是上面使用的 randInt
函数的实现:
#include <stdlib.h>
/* Return random integer from 0 to n-1 (for n in range 1 to RAND_MAX+1u) */
int randInt(unsigned int n) {
unsigned int x = (RAND_MAX + 1u) / n;
unsigned int limit = x * n;
int s;
do {
s = rand();
} while (s >= limit);
return s / x;
}
请注意,简单的 rand() % n
通常会导致略有偏差的结果。上面的 randInt(n)
函数使用 rand()
但会产生无偏差的结果。
随机数生成器需要由 srand
函数播种。这应该在程序中只完成一次,例如来自 main
函数:
#include <stdlib.h>
#include <time.h>
int main(void)
{
srand(time(NULL));
/* other stuff */
}