约瑟夫环问题——学习笔记

约瑟夫环问题,经典的算法问题。

这个问题说的是一个小故事。在很久以前,有n个犹太人遭到敌人的追击,他们逃到了一个山洞中,大部分的人决定宁愿去死也不要让敌人抓住。他们围成一圈,一个个轮流报数,报到k的人就要当场自杀,然后下一个人从1开始重新数数。而约瑟夫不想自杀,于是他灵机一动,站到了圈的一个位置上,结果其他人一个个都自杀了,最后只剩下约瑟夫一个人还活着,最终他逃出了山洞。那么问题来了,约瑟夫站的是哪个位置呢?

这个问题的实质就是,n个人围成一个圈轮流数数,每次淘汰数到k的那个人,求最后剩下的人是谁。

这个问题有很多解法,但大多都是比较复杂的解决方法,在这里我先介绍一种巧妙的递推方式。先上递推关系式:

F[1]=0;
F[n]=(F[n-1]+k) mod n;

刚看这个公式估计会有点蒙,待我一步步说来。

首先为了编程方便,我们假定n个人的编号为0,1,...,n-1。

然后,F[n]是我们的目标函数,也就是最后剩下那个人的编号。

我们先假设已经知道F[n-1]的值,例如F[n-1]=P,也就是

0,1,...,P-1,P,P+1,...,N-2

那么我们怎么通过这个P来求的所需要的F[n]呢,实际上只需简单绕个弯。

由于n与n-1之间只相差1,那么只要在n个人中淘汰掉一位,那么剩下的n-1人的情况就是已知的了。

也就是说,在下面的数列中:

0,1,...,k-1,k,k+1,...,N-1

由于是从0开始,所以数到k的应该是k-1。

拿掉k-1以后,剩下就是n-1个数了,我们将拿去k-1后的数列由k为起点重新排列得到:

k,k+1,...,N-1,0,1,...

可知数列第P位是最后一人,对应与上面数列的第k+P位

由于当k+P>n-1时将从0开始,所以有了(k+P)mod n,也就是上面的公式:

F[n]=(F[n-1]+k) mod n;

而当n=1时,数列只有一个数,故F[1]=0!


好啦,公式推导完毕,不废话上代码!

int Fun(int k,int n) {
	int x = 0;
	for (int i = 0; i < n; i++) {
		x = (x + k) % (i+1);
	}
	return x;
}

非常简洁,比所谓的链表法啊,数组删除法要简洁的多了。

PS:顺便推荐 Tank_long 博主写的文章,里面有上面算法的优化算法,非常聪明的方法,,,有空我一起讲解下。就这样。




猜你喜欢

转载自blog.csdn.net/jjwwwww/article/details/81008765