今年的春晚上刘谦表演了魔术《守岁共此时》,台上台下积极互动(尤其是小尼),十分的有趣。刘谦老师的魔术不仅仅是他的高超手法,还有这背后的严谨逻辑,下面我们来用C语言来解析魔术吧。
源代码
#define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> #include<time.h> #include<stdlib.h> int main() { srand(time(NULL)); // 使用当前时间作为随机数生成器的种子 // 任意选四张牌 int card[4]; printf("请输入四个牌的数字\n"); for (int i = 0; i < 4; i++) { scanf("%d", &card[i]); // 输入四张牌的数字 } int cardend[8]; for (int i = 0; i < 4; i++) { cardend[i] = card[i]; } for (int i = 4; i < 8; i++) { cardend[i] = card[i - 4]; } // 报名字字数 printf("请输入名字个数\n"); int name = 0; scanf("%d", &name); // 输入名字个数 int x = 0; for (int i = 0; i < name; i++) { x = cardend[0]; for (int j = 0; j < 7; j++) { cardend[j] = cardend[j + 1]; } cardend[7] = x; } // 最上面三张插到中间位置 // 取随机数进行处理保证插的位置随机 // 此时剩五张牌,有四个位置 int cardmove[8]; int where = rand() % 4 + 1; // 生成一个1到4之间的随机数 for (int i = 0; i < where; i++) { cardmove[i] = cardend[i + 3]; } int num = 0; for (int i = where; i < where + 3; i++) { cardmove[i] = cardend[num]; num++; } int end = 7; for (int i = 0; i < 5 - where; i++) { cardmove[end] = cardend[end]; end--; } // 第一张牌 printf("第一张牌为%d\n", cardmove[0]); cardmove[0] = 0; for (int i = 0; i < 7; i++) { cardmove[i] = cardmove[i + 1]; } // 南方人输入1,北方人输入2,不确定3张 printf("南方人输入1,北方人输入2,不确定3张\n"); int place = 0; scanf("%d", &place); for (int i = 0; i < 8; i++) { cardend[i] = cardmove[i]; } // 根据地区移动牌 // 男生拿一张,女生拿两张 printf("男生拿一张,女生拿两张\n"); int sex = 0; scanf("%d", &sex); for (int i = 0; i < sex; i++) { cardmove[i] = 0; } int numbercard = 8 - sex; // 见证奇迹的时刻挪七张 printf("见证奇迹的时刻\n"); int magic = 7; for (int i = 0; i < magic; i++) { int first = cardmove[0]; for (int j = 0; j < numbercard; j++) { cardmove[j] = cardmove[j + 1]; } cardmove[numbercard - 1] = first; } // 扔牌 int flag = 1; while (numbercard > 1) { if (flag > numbercard) { flag -= numbercard; } while (cardmove[flag] == 0) { flag++; } cardmove[flag - 1] = 0; printf("好运留下来\n"); printf("烦恼丢出去\n"); numbercard--; } int endcard = 0; for (int i = 0; i < 7; i++) { if (cardmove[i] != 0) { endcard = cardmove[i]; } } printf("剩下的第一张为%d\n", endcard); }
源代码解读
请对照上文的代码进行翻阅
#define _CRT_SECURE_NO_WARNINGS 1
这行代码是用来定义预处理器宏,用于禁用安全警告。在这里,它可能是为了避免一些特定的安全警告(scanf)。
srand(time(NULL));
这行代码使用当前时间作为随机数生成器的种子,以便在后续使用 rand()
生成随机数时能够获得不同的随机序列。
// 任意选四张牌 int card[4]; printf("请输入四个牌的数字\n"); for (int i = 0; i < 4; i++) { scanf("%d", &card[i]); // 输入四张牌的数字 }
创建一个数组用来存贮选择的牌。
int cardend[8]; for (int i = 0; i < 4; i++) { cardend[i] = card[i]; } for (int i = 4; i < 8; i++) { cardend[i] = card[i - 4]; }
将输入的四张牌按顺序复制到名为 cardend
的数组中,并将其重复一次,以便后续的处理。
int x = 0; for (int i = 0; i < name; i++) { x = cardend[0]; for (int j = 0; j < 7; j++) { cardend[j] = cardend[j + 1]; } cardend[7] = x; }
根据输入的名字个数,将牌进行移动,具体地,将数组 cardend
中的第一个元素依次移到数组的末尾,这个过程重复了名字个数次。
// 最上面三张插到中间位置 // 取随机数进行处理保证插的位置随机 // 此时剩五张牌,有四个位置 int cardmove[8]; int where = rand() % 4 + 1; // 生成一个1到4之间的随机数 for (int i = 0; i < where; i++) { cardmove[i] = cardend[i + 3]; } int num = 0; for (int i = where; i < where + 3; i++) { cardmove[i] = cardend[num]; num++; } int end = 7; for (int i = 0; i < 5 - where; i++) { cardmove[end] = cardend[end]; end--; }
随机生成一个数 where
,然后将数组 cardend
中的一部分元素插入到数组 cardmove
的中间位置。
// 第一张牌 printf("第一张牌为%d\n", cardmove[0]); cardmove[0] = 0; for (int i = 0; i < 7; i++) { cardmove[i] = cardmove[i + 1]; }
输出数组 cardmove
中的第一个元素,并将其置为0。
// 南方人输入1,北方人输入2,不确定3张 printf("南方人输入1,北方人输入2,不确定3张\n"); int place = 0; scanf("%d", &place); for (int i = 0; i < 8; i++) { cardend[i] = cardmove[i]; }
根据用户输入的地区,移动牌的位置。
// 男生拿一张,女生拿两张 printf("男生拿一张,女生拿两张\n"); int sex = 0; scanf("%d", &sex); for (int i = 0; i < sex; i++) { cardmove[i] = 0; } int numbercard = 8 - sex;
// 见证奇迹的时刻挪七张 printf("见证奇迹的时刻\n"); int magic = 7; for (int i = 0; i < magic; i++) { int first = cardmove[0]; for (int j = 0; j < numbercard; j++) { cardmove[j] = cardmove[j + 1]; } cardmove[numbercard - 1] = first; }
对牌堆进行特定的移动,重复了7次。
// 扔牌 int flag = 1; while (numbercard > 1) { if (flag > numbercard) { flag -= numbercard; } while (cardmove[flag] == 0) { flag++; } cardmove[flag - 1] = 0; printf("好运留下来\n"); printf("烦恼丢出去\n"); numbercard--; }
根据特定的规则,不断扔掉牌,直到只剩下一张牌。
int endcard = 0; for (int i = 0; i < 7; i++) { if (cardmove[i] != 0) { endcard = cardmove[i]; } } printf("剩下的第一张为%d\n", endcard);
输出最后剩下的一张牌的数字,魔术结束。