春晚魔术和约瑟夫问题

104 篇文章 8 订阅

春晚刘谦的魔术涉及到置换群和约瑟夫问题,最终的结果是魔术开始时确定的几个变量确定好的,扑克牌只是道具和障眼法。网上一查发现这个问题发现颇有历史渊源,群论是法国21岁天才数学家伽罗瓦的惊世之作。约瑟夫问题则是17世纪的法国数学家加斯帕在《数目的游戏问题》中讲了这样一个故事:15个教徒和15 个非教徒在深海上遇险,必须将一半的人投入海中,其余的人才能幸免于难,于是想了一个办法:30个人围成一圆圈,从第一个人开始依次报数,每数到第九个人就将他扔入大海,如此循环进行直到仅余15个人为止。问怎样排法,才能使每次投入大海的都是非教徒。

编程实现这个过程:

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

int last_one(int n, int m)
{
	int res = 0;

	if (n == 1) {
		return 1;
	}

	for (int i = 2; i < n + 1; ++i) {
		res = (res + m) % i;
	}

	return res + 1;
}

int main(void)
{
	int n, m, i, kill = 0, t = 0, s = 0;

	scanf("%d %d", &n, &m);
	printf("%s line %d, total object %d, kill every %d object.\n",
	       __func__, __LINE__, n, m);

	// the first object are reserved, the first index start from
	// 1, end to n.
	bool *array = malloc(sizeof(bool) * (n + 1));
	if (array == NULL) {
		printf("%s line %d, alloc object buffer failure.\n",
		       __func__, __LINE__);
		return -1;
	}

	for (i = 0; i < (n + 1); i ++) {
		array[i] = false;
	}

	do {
		++t;
		if (t > n)
			t = 1;
		if (!array[t])
			s++;
		if (s == m) {
			s = 0;
			printf("%d ", t);

			// kill this object.
			array[t] = true;
			kill++;
		}
	} while (kill != n); // all has been killed.

	printf("kill %d object, last one is %d.\n", kill, last_one(n, m));
	free(array);

	return 0;
}

可见杀戮顺序是9 18 27 6 16 26 7 19 30 12 24 8 22 5 23 11 29 17 10 2 28 25 1 4 15 13 14 3 20 21,所以只要保证9 18 27 6 16 26 7 19 30 12 24 8 22 5 23位置上的人都是非教徒,则15名教徒能够全部存活下来。

所以,只要把非教徒安排在5,6,7,8,9,12,16,18,19,22,23,24,26,27,30位置上,能够达到仅杀死非教徒的目的。

参考文章

约瑟夫环问题(链表 + 公式)-CSDN博客


结束

  • 11
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

papaofdoudou

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

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

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

打赏作者

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

抵扣说明:

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

余额充值