代码解密刘谦春晚魔术

1 前言

随着2024 年春晚的完美落幕,刘谦的魔术表演再度成为了全国人们热议的焦点。作为一名程序员我们尝试从编程的角度来揭秘刘谦的魔术,通过代码穷举所有情况验证是否剩下的两张牌都一致。

2 魔术步骤

1、准备4张扑克牌并平均撕成两份,叠放在一起,形成8个半张牌的形式:ABCD-ABCD。

2、将牌堆顶的数量为名字字数的牌移至牌堆底。这一步实际上并不改变牌的本质顺序,只是营造神秘感。

3、将前三张牌放在牌堆中间,并取出牌堆顶的牌放置在一旁。此操作确保头尾两张牌相同。

4、取出牌堆顶的若干张牌插入牌堆中间。具体取牌数依据地域差异,如南方人取1张,北方人取2张,若不确定则取3张。但这一步在魔术中并不起决定性作用。

5、男生扔掉牌堆顶1张,女生扔掉牌堆顶2张。这一步是为了调整牌的数量,以适应后续的操作。

6、执行"见证奇迹的时刻"循环。每说一个字,就取出牌堆顶的牌放置在牌堆底。这一步骤通过多次循环操作,改变了牌的顺序。

7、执行关键操作:从牌堆顶开始,每次先将牌堆顶的一张牌放在牌堆底,再扔掉牌堆顶的一张牌。重复此操作直到只剩一张牌。检查这张牌和最初放置在一旁的牌是否吻合。若吻合,则魔术成功。

需要注意的是,这个魔术的关键在于通过特定的操作步骤和牌的数量变化,最终找到和最初放置在一旁的牌相同的牌。而地域、性别等因素在魔术中并不起决定性作用,更多的是为了增加魔术的趣味性和神秘感。

3 代码实现

代码如下:

ini 复制代码
package com.mrxu.admin.test;

public class MoShuTest {

    /**
     * 计算某种情况下 最后剩余的两张牌
     * @param nameLength 名字长度
     * @param pre3ToMiddle 前三张插入的中间位置
     * @param district 根据地区获得的数据 1、2、3
     * @param districtToMiddle 地区获得的数据插入的中间位置
     * @param sex 性别变量
     */
    private static void run(int nameLength,int pre3ToMiddle,int district,int districtToMiddle,int sex) {
        char firstChar,lastChar;
        // 1 准备四张扑克牌,将它们平均撕成两份,并叠放在一起,形成八张半张牌的形式,即"ABCD-ABCD"的形式。
        char[] orgArray = {'A','B','C','D','A','B','C','D'};
        // 2 将牌堆顶的牌数量为名字字数的牌移至牌堆底。这个步骤其实并不影响牌的顺序,只是为了营造神秘感
        orgArray = moveToBottom(orgArray,nameLength);
        // 3 从牌堆顶取出三张牌,随意地插入到牌堆的中间位置。这一步是为了确保头尾两张牌是一样的。
        orgArray = moveToMiddle(orgArray,3,pre3ToMiddle);
        // 4 将牌堆顶的一张牌取出,放置在一旁,这张牌将作为后续验证的关键牌。
        firstChar = orgArray[0];
        orgArray = removeTop(orgArray,1);
        // 5 根据南北地区不同,从牌堆顶取出一定数量的牌插入到牌堆中间。这一步骤中的牌数选择可以根据实际情况进行调整,南方人取1张,北方人取2张,如果不确定可以取3张。但实际上这个步骤并不会影响魔术的成功与否。
        orgArray = moveToMiddle(orgArray,district,districtToMiddle);
        // 5 如果是男生,就扔掉牌堆顶的一张牌;如果是女生,就扔掉牌堆顶的两张牌。这一步是为了让男女生的牌堆数量有所不同,增加魔术的趣味性。
        orgArray = removeTop(orgArray,sex);
        // 6 执行"见证奇迹的时刻"循环,即每说一个字就从牌堆顶取出一张牌放置在牌堆底。这个步骤会改变牌的顺序,但不会影响头尾两张牌的一致性。
        for(int i = 0;i < "见证奇迹的时刻".length();i++) {
            orgArray = moveToBottom(orgArray,1);
        }
        // 7 最后,从牌堆顶开始,每次先将牌堆顶的一张牌放在牌堆底,再扔掉牌堆顶的一张牌。重复以上操作直到只剩一张牌。检查这张牌和之前放置在一旁的牌是否吻合。如果吻合,则魔术成功。
        while(orgArray.length != 1) {
            orgArray = moveToBottom(orgArray,1);
            orgArray = removeTop(orgArray,1);
        }
        lastChar = orgArray[0];
        System.out.println(firstChar+":"+lastChar);
    }

    /**
     * 移动前n张牌到最下面
     * @param orgArray 牌数据
     * @param count 移动数量
     * @return 移动后的牌数据
     */
    private static char[] moveToBottom(char[] orgArray,int count) {
        char[] rsArray = new char[orgArray.length];
        for(int i = 0;i < orgArray.length;i ++) {
            if(i < count) {
                rsArray[orgArray.length - count + i] = orgArray[i];
            } else {
                rsArray[i - count] = orgArray[i];
            }
        }
        return rsArray;
    }

    /**
     * 移动上面moveCount张牌到下面中间位置
     * @param orgArray 牌数据
     * @param moveCount 移动数量
     * @param middlePoint 插入中间位置
     * @return 插入后的牌数据
     */
    private static char[] moveToMiddle(char[] orgArray,int moveCount,int middlePoint) {
        char[] rsArray = new char[orgArray.length];
        for(int i = 0;i < orgArray.length;i++) {
            if(i < moveCount) {
                rsArray[middlePoint+i] = orgArray[i];
            }
            else {
                if(i < moveCount+middlePoint) {
                    rsArray[i-moveCount] = orgArray[i];
                }
                else {
                    rsArray[i] = orgArray[i];
                }
            }
        }
        return rsArray;
    }

    /**
     * 删除上面n张牌
     * @param orgArray 牌数据
     * @param count 删除数量
     * @return 删除后的牌数据
     */
    private static char[] removeTop(char[] orgArray,int count) {
        char[] rsArray = new char[orgArray.length - count];
        for(int i = count;i < orgArray.length;i++) {
            rsArray[i-count] = orgArray[i];
        }
        return rsArray;
    }

    public static void main(String[] args) {
        int allCount = 0;
        for(int nameLength = 1;nameLength <= 8;nameLength ++) {
            for(int pre3ToMiddle = 1;pre3ToMiddle <= 4;pre3ToMiddle ++) {
                for(int district = 1;district <= 3;district ++) {
                    for(int districtToMiddle = 1;districtToMiddle <= 6-district;districtToMiddle ++) {
                        for(int sex = 1;sex <= 2;sex ++) {
                            run(nameLength,pre3ToMiddle,district,districtToMiddle,sex);
                            allCount++;
                        }
                    }
                }
            }
        }
        System.out.println("总次数:"+allCount);
    }

}

3.1 run 给特定参数输出最终剩余的两张牌

3.2 moveToBottom方法,移动前n张牌到最下面

3.3 moveToMiddle方法,移动上面n张牌到下面中间位置

3.4 removeTop方法,删除上面n张牌

3.5 main方法,穷举所有情况输出结果

最终输出有768种情况,没中情况下留下的两张数字都一样。

4 总结

刘谦老师魔术本质其实就是约瑟夫环的问题。作为一名程序员应有把现实问题通过程序解决的能力,对编程感兴趣的可以关注我,我会定期分享编程技术。

相关推荐
用户21411832636021 小时前
手把手教你用Claude制作专属PPT生成器-从模板学习到自动生成全流程实战
后端
计算机毕设匠心工作室3 小时前
【python大数据毕设实战】全面皮肤病症状数据可视化分析系统、Hadoop、计算机毕业设计、包括数据爬取、数据分析、数据可视化、机器学习、实战教学
后端·python·mysql
摆烂工程师3 小时前
2025年12月最新的 Google AI One Pro 1年会员教育认证通关指南
前端·后端·ai编程
qq_12498707533 小时前
基于SpringBoot+vue的小黄蜂外卖平台(源码+论文+部署+安装)
java·开发语言·vue.js·spring boot·后端·mysql·毕业设计
代码与野兽4 小时前
AI交易,怎么让LLM自己挑选数据源?
前端·javascript·后端
天天摸鱼的java工程师4 小时前
JDK 25 到底更新了什么?这篇全景式解读带你全面掌握
java·后端
非鱼feiyu4 小时前
自关联数据表查询优化实践:以 Django + 递归 CTE 构建树结构为例
数据库·后端·django
零日失眠者4 小时前
这5个Python库一旦掌握就离不开
后端·python
幌才_loong4 小时前
.NET8 × Redis 实战宝典:从配置到落地,搞定高并发缓存就这篇!
后端·.net
用户8356290780514 小时前
如何使用 Python 从 Word 文档中批量提取表格数据
后端·python