刘谦魔术yyds,尼格买提大呼魔法失灵!连夜肝的模拟代码玩起来!

今年的春晚依然没让人失望,岳云鹏的相声稳定输出,依然尬的抠脚,喊两声燕子都比这节目效果好,还有不忍吐槽的强行塞广告,比如某书的广告非常硬,就差怼观众脸上了。但是不可否认有一些亮点,比如年锦的文化寓意还是相当给力的,建模效果很好。。但。。最让我印象深刻的,也是我心目中的春晚第一节目,当属刘谦的魔术,有趣有互动,发现不对劲的尼格买提表情绝对是最佳笑点,仿佛全世界只有他一个人没对上哈哈哈。

其实刘谦魔术的亮点不在于魔术的原理,而在于能在春晚舞台上呈现好的寓意,并和魔术的表演过程丝滑融合,这一切绝对归功于刘谦调动现场氛围的能力。所以,下面的分析主要面对的是对背后原理感兴趣的同学,我会讲的通俗易懂,文末附上了模拟刘谦魔术的java代码,玩一玩还是挺有意思的,就是有点对不起保洁阿姨(疯狂丢牌)。

先回顾下刘谦魔术的流程,大致分成八步。

  1. 首先,取四张牌,将它们朝下洗牌,对折两次,然后撕成八个半张。
  2. 其次,依据名字的字数(N),将第一张放到底,重复N次。
  3. 接着,将前三张插入到中间,同时将第一张藏起。
  4. 然后,南方的人拿1张,北方的人拿2张,不确定的人拿3张,将它们插入到中间(非最底)。
  5. 随后,男生丢弃前两张,而女生丢弃一张。
  6. 第六步,进行7次洗牌的操作,用刘谦的话说,下面是 见 证 奇 迹 的 时 刻 。
  7. 第七步,循环执行操作,即进行一次洗牌后扔一次牌(将第一张放到底,将第二张扔出去)。
  8. 最后,手上会只剩下半张牌,并且和第三步藏起来的半张牌凑成完整的一张。

问题来了,为什么开始藏的半张和最后剩下的半张刚好是完整的一张呢?我们逐步模拟下整个魔术的流程,整体很容易理解,之后还可以调试下代码,整个模拟过程可以帮助你体会数学的神奇!先简单复现下这个过程:

  • 第一步:撕成八个半张,此时八张可以分成两组,每组有四张牌,并且每个数字会有重复的两个半张。

  • 第二步需要将最顶上的牌和与名字字数相同的牌数放到最底下。每个人放的牌数可能不一样,但是不会改变牌的相对顺序,假设最后一张牌是8,那牌的相对顺序就XXX8XXX8,X表示任意牌。这样表示的原因是,X是我们不需要关心的数字,而底牌才是所有干扰项里的不变量。

  • 第三步:从牌顶拿出三张牌插入到中间,这时顺序变成了8XXXXXX8。藏的牌是第一张是数字8。

  • 第四步:根据南北方归属往中间插入不同的牌,顺序变成XXXXXX8(只剩7张,藏了1张)。小尼就是在这一步出错的,他把顶上的两张错放入了底部,悲剧的开始哈哈哈。

  • 第五步:男生丢弃1张,女生丢弃2张。男生手里剩余的是XXXXX8(剩6张),而女生手里是XXXX8(剩5张)。

  • 第六步:有趣的和“见证奇迹的时刻”七字招牌语结合,从牌顶往底部放7张牌。男生手里的牌顺序为XXXX8X,女生手里的牌顺序为XX8XX。注意,这里分成两个分支,但是终点相同。

  • 第七步:留一张放到最底,然后丢一张。接着,循环进行,直到只剩一张。
    男生:XX8XX -> XX8 -> 8X -> 8,
    女生:8XXX -> XX8 -> 8X -> 8

注意,这里每一步都是左边第一个数字移到右边,并丢弃第二位数,可以发现,第三步藏的牌和最后剩的牌都是8,刚好凑成一张!

为什么呢?实际上这里用到了一个著名的数学问题,约瑟夫问题,下面写下刘谦魔术的java版本代码,大家可以用着玩一玩,理论上知道了原理,是可以设计出更多奇思妙想的小魔术的~接下来,就是见证奇迹的时刻~

public class TechGuideMagic {

        public static void main(String[] args) {
            // 模拟牌的顺序
            List<Integer> q = new ArrayList<>();
            int head = 0, tail = 0;

            // 先随机生成 4 张牌
            while (q.size() != 4) {
                int num = new Random().nextInt(13) + 1;
                if (!q.contains(num)) {
                    q.add(num);
                    tail = tail + 1;
                }
            }

            System.out.println("你抽出4张牌,顺序为" + q);

            // 打乱顺序
            Collections.shuffle(q);
            System.out.println("你开始洗牌,顺序变为" + q);

            for (int i = 0; i < 4; i++) {
                q.add(q.get(i));
                tail = tail + 1;
            }

            System.out.println("你对折后撕开,变为8张,顺序为" + q);

            // 输入当前信息
            Scanner scanner = new Scanner(System.in);
            System.out.print("输入姓名: ");
            String name = scanner.nextLine();
            System.out.print("输入性别: ");
            String sex = scanner.nextLine();
            System.out.println("你的名字长度: " + name.length());

            for (int i = 0; i < name.length(); i++) {
                q.add(q.get(head));
                head += 1;
                tail += 1;
            }

            System.out.println("你移动N(名字长度)张牌到底,牌的顺序变为" + q.subList(head, tail));

            List<Integer> p = new ArrayList<>();
            for (int i = 0; i < 3; i++) {
                p.add(q.get(head));
                head += 1;
            }

            int s = tail - head;

            for (int i = 0; i < s / 2; i++) {
                q.add(q.get(head));
                head += 1;
                tail += 1;
            }

            for (int i = 0; i < p.size(); i++) {
                q.add(p.get(i));
                tail += 1;
            }

            for (int i = 0; i < s - s / 2; i++) {
                q.add(q.get(head));
                head += 1;
                tail += 1;
            }

            System.out.println("你取三张牌放中间后,顺序变为" + q.subList(head, tail));

            // 把第一张藏好,放在key中
            int key = q.get(head);
            head += 1;
            System.out.println("藏好(放在屁股下)的牌号是" + key + ",手中剩余的牌顺序为" + q.subList(head, tail));

            int k = new Random().nextInt(3) + 1;
            System.out.println("南方人取1张牌放中间,北方人取2张,不知道的取3张");
            System.out.println("你取了" + k + "张放中间");

            p.clear();
            for (int i = 0; i < k; i++) {
                p.add(q.get(head));
                head += 1;
            }

            s = tail - head;

            for (int i = 0; i < s / 2; i++) {
                q.add(q.get(head));
                head += 1;
                tail += 1;
            }

            for (int i = 0; i < p.size(); i++) {
                q.add(p.get(i));
                tail += 1;
            }

            for (int i = 0; i < s - s / 2; i++) {
                q.add(q.get(head));
                head += 1;
                tail += 1;
            }

            System.out.println("南北方操作完后的顺序为" + q.subList(head, tail));

            int cardsToThrow;
            if (sex.equals("男")) {
                head += 1;
                cardsToThrow = 1;
            } else {
                head += 2;
                cardsToThrow = 2;
            }

            System.out.println("你是" + sex + "的, 丢掉" + cardsToThrow + "张牌(保洁阿姨对不起),手里的牌顺序变为" + q.subList(head, tail));

            System.out.println("你大喊:见 证 奇 迹 的 时 刻");
            System.out.println("你再次洗牌,把第1张牌放到底,重复7次");

            for (int i = 0; i < 7; i++) {
                q.add(q.get(head));
                head += 1;
                tail += 1;
            }

            System.out.println("你手上的牌顺序为" + q.subList(head, tail));
            System.out.println("你大喊:好运留下来,烦恼全扔掉!");
            System.out.println("你把第1张牌放到底,第二张牌扔掉,重复直到手上只剩一张。");

            while (head + 1 != tail) {
                q.add(q.get(head));
                head += 1;
                tail += 1;
                q.add(q.get(head));
                head += 1;
            }

            System.out.println("第三步放在屁股下的牌为" + key + ", 手上的牌为" + q.subList(head, tail));
            System.out.println("尼格买提:我的魔法打败了魔术,全世界只有我没对上【哭脸】");
        }
    }
    // vx公众号关注TechGuide,笔试题库,闪电速递!

调试的过程记录,你也可以玩起来~

你抽出4张牌,顺序为[1, 10, 9, 7]
你开始洗牌,顺序变为[1, 10, 7, 9]
你对折后撕开,变为8张,顺序为[1, 10, 7, 9, 1, 10, 7, 9]
输入姓名: TechGuide
输入性别: 男
你的名字长度: 9
你移动N(名字长度)张牌到底,牌的顺序变为[10, 7, 9, 1, 10, 7, 9, 1]
你取三张牌放中间后,顺序变为[1, 10, 10, 7, 9, 7, 9, 1]
藏好(放在屁股下)的牌号是1,手中剩余的牌顺序为[10, 10, 7, 9, 7, 9, 1]
南方人取1张牌放中间,北方人取2张,不知道的取3张
你取了2张放中间
南北方操作完后的顺序为[7, 9, 10, 10, 7, 9, 1]
你是男的, 丢掉1张牌(保洁阿姨对不起),手里的牌顺序变为[9, 10, 10, 7, 9, 1]
你大喊:见 证 奇 迹 的 时 刻
你再次洗牌,把第1张牌放到底,重复7次
手上的牌顺序为[10, 10, 7, 9, 1, 9]
你大喊:好运留下来,烦恼全扔掉!
你把第1张牌放到底,第二张牌扔掉,重复直到手上只剩一张。
第三步放在屁股下的牌为1, 手上的牌为[1],magic~
尼格买提:我的魔法打败了魔术【哭脸】
  • 9
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

TechGuide

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值