主页
搜索
最近更新
数据统计
申请密钥
系统公告
1
/
1
请查看完所有公告
刘谦 2024 春晚魔术原理证明
最后更新于 2025-07-31 13:01:42
作者
CleverRaccoon
分类
个人记录
复制 Markdown
查看原文
删除文章
更新内容
## 刘谦 2024 春晚魔术原理证明 ### 魔术内容  ### 程序证明 根据魔术内容可以写出以下 $\textit{C++}$ 程序,运行程序,如果按照正确格式输入且电脑运行处于正常状态,那么输出的最后一行一定是 `魔术成功!`。 ```cpp #include <bits/stdc++.h> #include <conio.h> using namespace std; class MagicTrick { public: MagicTrick (string pokers) { if (pokers.size() != 4) { cout << "输入格式错误!" << endl; exit(0); } this->pokers = pokers; } void run(void); void print(string, bool); private: string pokers, top; void tearInHalf(void); void nameOperation(void); void middleAndAway(void); void middle(void); void throwAway(void); void behind(void); void behindAndThrow(void); string input(string); void line(void); }; int main() { string pokers; cout << "请输入四张初始扑克牌:"; cin >> pokers; MagicTrick magicTrick(pokers); magicTrick.run(); return 0; } void MagicTrick::run () { tearInHalf(); nameOperation(); middleAndAway(); middle(); throwAway(); behind(); behindAndThrow(); } void MagicTrick::print (string message = "", bool top = false) { cout << message; if (top) { cout << this->top << endl; } else { cout << pokers << endl; } getch(); } string MagicTrick::input (string message = "") { cout << message; string res; cin >> res; return res; } void MagicTrick::line () { cout << "--------------------------------" << endl; } void MagicTrick::tearInHalf () { line(); pokers = pokers + pokers; print("将扑克牌撕成两半,并叠在一起得到:"); } void MagicTrick::nameOperation () { line(); string name = input("请输入您的姓名:"); int nameLength = (int)(name.size()) >> 1; while (nameLength--) { pokers = pokers.substr(1) + pokers[0]; } print("将牌堆顶数量为“名字字数”的牌移至牌堆底可得:"); } void MagicTrick::middleAndAway () { line(); pokers = pokers.substr(3, 3) + pokers.substr(0, 3) + pokers.substr(6, 2); print("将前三张牌放在牌堆中间得:"); top = pokers[0]; pokers.erase(0, 1); print("取出牌堆顶的牌,放置在一旁得:"); } void MagicTrick::middle () { line(); int places = input("请输入您是南方人还是北方人(南方人:1,北方人:2,不确定:3):")[0] - '0'; pokers = pokers.substr(places, 2) + pokers.substr(0, places) + pokers.substr(places + 2); print("取出牌堆顶的若干张牌插入牌堆中间得:"); } void MagicTrick::throwAway () { line(); int sex = input("请输入您的性别(男:1,女:2):")[0] - '0'; pokers.erase(0, sex); print("男生扔掉牌堆顶1张,女生扔掉牌堆顶2张得:"); } void MagicTrick::behind () { line(); string words[] = {"见", "证", "奇", "迹", "的", "时", "刻"}; cout << "“见证奇迹的时刻”,每说一个字就取出牌堆顶一张牌放置在牌堆底得:" << endl; getch(); for (int i = 0; i < 7; i++){ pokers = pokers.substr(1) + pokers[0]; print(words[i] + ":"); } print("最终的结果:"); } void MagicTrick::behindAndThrow () { line(); cout << "从牌堆顶开始,每次先将堆顶的一张牌放在牌堆底,再扔掉牌堆顶的一张牌,重复以上操作直到只剩一张牌:" << endl; getch(); do { pokers = pokers.substr(1) + pokers[0]; print("先将堆顶的一张牌放在牌堆底:"); pokers.erase(0, 1); print("扔掉牌堆顶的一张牌:"); } while (pokers.size() > 1); print("刚刚放置在一旁的扑克牌为:", true); print("现在剩下的一张扑克牌为:"); if (top == pokers) { cout << "魔术成功!" << endl; } else { cout << "魔术失败!" << endl; } } ``` ### 数学证明 设这副牌为 `abcd`,取出来的牌为 $K$。 0. 初始时为 `abcd`; 1. 撕开后重叠可得 `abcdabcd`; 2. 将多少张牌堆顶上的牌放到牌堆底本质上不会改变牌之间的**相对顺序**,故仍设为 `abcdabcd`; 3. 可能会出现以下几种情况:`dabcabcd` 或 `daabcbcd` 或 `dababccd` 或 `dabcabcd`,可以发现,将前三张牌插入中间,无论如何插入,牌堆顶的牌和牌堆底的牌都是一样的,故取出的牌 $K$ 为 `d`,牌堆底的牌亦为 `d`; 4. 第 $3$ 步由于牌堆顶和牌堆底都一样,所以第 $4$ 步可以保证无论取 $1$ 还是 $2$ 还是 $3$ 张牌,都不会取到 $K$,因此 $K$ 仍在牌堆底,此时牌堆中的牌为 `xxxxxxd`; 5. 第 $4$ 步操作后,由于 $K$ 仍在牌堆底,所以无论仍 $1$ 张牌还是仍 $2$ 张牌都不会使 $K$ 被扔掉,故牌堆中仍有 $K$,牌堆中男生的牌为 `xxxxxd`,女生手中的牌为 `xxxxd`; 6. `见证奇迹的时刻` 共 $7$ 个字,故男生的牌会变为 `xxxxdx`,女孩子手中的牌会变为 `xxdxx`; 7. 观察步骤 $6$ 中男女生手中的牌,可以发现 $K$ 无论是在男生手中还是在女生的手中,都在从左往右数的第奇数个(男:从左往右第 $5$ 个是 $K$,女:从左往右第 $3$ 个是 $K$),接下来模拟这个过程: - 首先将堆顶的一张牌放在牌堆底(男:`xxxdxx`,女:`xdxxx`),然后扔掉一张牌(男:`xxdxx`,女:`dxxx`); - 再将堆顶的一张牌放在牌堆底(男:`xdxxx`,女:`xxxd`),扔掉一张牌(男:`dxxx`,女:`xxd`),这时候会发现女孩子的 $K$ 被完美地避开了“扔掉”这个残忍的环节; - 放牌底(男:`xxxd`,女:`xdx`),然后扔掉(男:`xxd`,女:`dx`),会发现男孩子的 $K$ 也避开了“扔掉”这个残忍的环节; - 放牌底(男:`xdx`,女:`xd`),然后扔掉(男:`dx`,女:`d`),女孩子手中的 $K$ 再次成功的避开了“扔掉”这个残忍的环节,同时她手中只有一张牌了,就是 $K$,刚刚取出来的那张牌; - 由于男生的牌还有 $2$ 张,所以继续,放牌底(男:`xd`),然后扔掉(男:`d`),会发现男生的 $K$ 也幸存下来了。 观察发现,女生初始的 `xxdxx` 就是由男生的 `xxxxdx` 变来的,所以虽然性别不同,手中的扑克牌数不同,但是原理是一样的。魔术设计者先撕开扑克牌,使牌变为 `abcdabcd` 型,然后通过将前三张扑克牌插入到中间(即,将 `abc` 插入到 `dabcd` 的两个 `d` 之间),使观众手中的卡牌变为 `dxxxxxxd` 型,然后将 $K$ 设为开头的 `d`,并将其删掉,变为 `xxxxxxd`,然后将 $1\sim 3$ 张扑克牌插入中间,卡牌仍为 `xxxxxxd` 型,接着男生的牌变为 `xxxxxd`,女生的变为 `xxxxd`,最后两步可以抽象为约瑟夫问题,会发现 $6$ 个人,数 $2$ 个数淘汰一个人的情况下,第 $5$ 个人正好可以“幸存”(即最后一个淘汰),而 $5$ 个人,数 $2$ 个数淘汰一个人的情况下,第 $3$ 个人正好可以“幸存”(即最后一个淘汰),故魔术设计者利用 `见证奇迹的时刻` 这 $7$ 个字,将男生的牌变为 `xxxxdx`,女生的牌变为 `xxdxx`,然后使男生、女生手中最后剩下的牌皆为 `d`,由于放置在一边的 $K$ 也是 `d`,所以这两张牌都可以找到它们的另一半。 综上,证毕。
正在渲染内容...
点赞
13
收藏
0