首先祝大家新年快乐!
刘谦带领全国人民一起玩扑克牌魔术。有网友形容,好像是一场规模巨大的春晚团建。也有人表示,刘谦魔术回归,熟悉的感觉又回来了。在这个魔术中你是否完成卡牌的配对呢?
其实这是一个经典约瑟夫环问题,有大佬已经通过数学公式推出来了:
小尼到底哪错了?北大同学揭秘春晚魔术
魔术过程
第一步:选出四张卡片ABCD。撕成两半,变成ABCDABCD
第二步:按名字数移动,不管移动几张,最后总是变成ABCDABCD(牌序变化,但搭配相对位置不变)
第三步:插3张卡片到中间,变成DXXXXXXD (中间是什么其实无所谓,只看两边)
第四步:将第一张卡片D藏好,XXXXXXD
第五步:按南方人北方人移动牌,依然是XXXXXXD
第六步:按男女扔牌,男:XXXXXD;女:XXXXD
第七步:见证奇迹的时刻,男:X1X2X3X4DX5;女:X1X2DX3X4
第八步:好运留下来!烦恼丢出去!
第一轮,男:X1X3D;女:X1DX4
第二轮,男:X1D;女:D
第三轮,男:D;女:D
到最后,藏好的卡片完美配对手里的卡片。
Java代码实现
package com.liuqian;
import java.util.ArrayList;
import java.util.Random;
import java.util.Scanner;
public class CardShuffle {
public static void main(String[] args) {
// 初始卡片cards队列,4张牌
ArrayList<Integer> cards = new ArrayList<Integer>();
cards.add(5);
cards.add(3);
cards.add(4);
cards.add(9);
System.out.println("卡牌 = " + cards);
// 一、把4张牌撕开成8张,放到原来4张牌后
int cardSize = cards.size();
for (int i = 0; i < cardSize; i++) {
cards.add(cards.get(i));
}
System.out.println("1.把4张牌撕开成8张 = " + cards);
// 二、名字有几个字,名字有几个字就从队头拿几张牌放到队尾
System.out.print("*你的名字:");
Scanner scanner = new Scanner(System.in);
int name_len = scanner.next().length();
for (int i = 0; i < name_len; i++) {
cards.add(cards.remove(0));
}
System.out.println("2.按名字取牌 = " + cards);
// 三、拿起最上面三张,把这三张整体插进剩下卡片的中间任意位置
ArrayList<Integer> first_three = new ArrayList<Integer>(cards.subList(0, 3));
ArrayList<Integer> other_cards = new ArrayList<Integer>(cards.subList(3, cards.size()));
Random random = new Random();
int index = random.nextInt(4) + 1;
other_cards.addAll(index, first_three);
cards = new ArrayList<Integer>(other_cards);
System.out.println("3.拿起最上面三张插到任意位置 = " + cards);
// 四、把第一张牌放到屁股下
int key_card = cards.remove(0);
System.out.println("4.把第一张牌放到屁股下 = " + cards);
System.out.println("屁股底下卡牌是:" + key_card);
// 五、按南北方人取牌,重复上述过程
System.out.print("*你是南方人还是北方人?");
int south_north = scanner.next().startsWith("北") ? 1 : 2;
ArrayList<Integer> first_cards = new ArrayList<Integer>(cards.subList(0, south_north));
other_cards = new ArrayList<Integer>(cards.subList(south_north, cards.size()));
index = random.nextInt(8 - south_north - 1) + 1;
other_cards.addAll(index, first_cards);
cards = new ArrayList<Integer>(other_cards);
System.out.println("5.按南北方人取牌 = " + cards);
// 六、按性别取牌,并撒出去
System.out.print("*你是男生还是女生?");
int gender = scanner.next().startsWith("男") ? 1 : 2;
for (int i = 0; i < gender; i++) {
cards.remove(0);
}
System.out.println("6.按性别取牌 = " + cards);
// 七、洗牌,“见,证,奇,迹,的,时,刻”(经典的约瑟夫环问题)
for (int i = 0; i < 7; i++) {
cards.add(cards.remove(0));
}
System.out.println("7.简称奇迹的时刻 = " + cards);
// 八、好运留下来,烦恼丢出去
while (cards.size() > 1) {
cards.add(cards.remove(0));// 留下来
cards.remove(0);// 丢出去
}
System.out.println("8.好运留下来,烦恼丢出去 = " + cards);
System.out.println("[屁股底下那张牌:" + key_card + "] == [手里最后一张牌:" + cards.get(0) + "]");
System.out.println();
System.out.println("财运滚滚!龙年大吉!o(* ̄▽ ̄*)ブ");
}
}
实验结果
约瑟夫环问题(Josephus Problem)是一个著名的数学问题,源于古代犹太历史学家弗拉维奥·约瑟夫斯(Flavius Josephus)的著作《犹太古代史》中的一个故事。根据故事内容,当时有一组被罗马军队包围的犹太人,他们决定宁愿自杀,也不愿被俘虏。于是,他们围成一个圆圈,从一个人开始,每隔一个人就自杀,直到剩下最后一个人。
这个问题提出了一个有趣的数学挑战:给定总人数n和每次报数的间隔m,如何确定最后剩下的那个人的位置?这个问题不仅有着深厚的数学背景,还涉及到算法设计和递归思想的应用。
往期遇到过的约瑟夫环问题:蓝桥杯 ADV-280 书院主持人
在解决约瑟夫环问题的过程中,可以使用循环、递归或数学公式等不同的方法。每种方法都有其优缺点,因此在实际应用中需要根据具体情况选择最合适的方法。
无论是从数学分析的角度,还是从算法实现的角度,约瑟夫环问题都是一个非常有趣和有挑战性的问题。它不仅能锻炼我们的数学思维能力,还能提升我们的算法设计和编程实现能力。