约瑟夫问题

  1. 问题
  2. 分析
  3. 实现

问题

约瑟夫问题是:有 n 只猴子,按顺时针方向围成一圈选大王(编号为 1~n),从第 1 号开始报数,一直数到 m,数到 m 的猴子退到圈外,剩下的猴子再接着从 1 开始报数。就这样,直到圈内只剩下一只猴子时,这个猴子就是猴王。编程求输入 n、m 后,输出最后猴王的编号。

输入数据:每行是用空格分开的两个整数,第一个是 n,第二个是 m(0\<m, n\<=1 000 000)。最后一行是:
0 0

输出要求:对于每行输入数据(最后一行除外),输出数据也是一行,即最后猴王的编号。

输入样例

1
2
3
4
6 2
12 4
8 3
0 0

输出样例

1
2
3
5
1
7

分析

这就是一个简单的模拟题,由其模拟过程中的元素动态删除及报数的顺序性的特点,可以使用链表结构来完成。

不过我们不应该只将问题思考到这里就结束了。根据这个问题的特点,我们往往能够很容易联想到它是否能在数学上得到更简单或者简洁的解决。因此我们可以发现,约瑟夫问题中存在一个递推公式:

$$
f(n,m) = (f(n-1,m) + m) \% n
$$

$f(n,m)$表示,有$n$个猴子时,每报到$m$踢出那只猴子,最终胜利者的编号。如此,假设$n = 11, m = 3$时,可以根据上述公式逆推得到:

  • $f(1,3)=0$:只有1只猴子了,那只猴子就是获胜者,它的下标位置是0
  • $f(2,3)=(f(1,3)+3)\%2=3\%2=1$:在有2只猴子的时候,胜利者的下标位置为1
  • $f(3,3)=(f(2,3)+3)\%3=4\%3=1$:在有3只猴子的时候,胜利者的下标位置为1
  • $f(4,3)=(f(3,3)+3)\%4=4\%4=0$:在有4只猴子的时候,胜利者的下标位置为0
  • ……
  • $f(11,3)=(f(10,3)+3)\%11=…=6$:在有11只猴子的时候,胜利者的下标位置为6

因此约瑟夫问题是个在描述上十分简单,甚至可以直接暴力求解的问题在数学上却有了有趣的规律,就此我们就可以构造更精致的代码。

实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
/* 模拟实现 */

#include <list>
#include <iostream>
using namespace std;
int main()
{
list<int> monkeys;
int n, m;
while (true) {
cin >> n >> m;
if (n == 0 && m == 0)
break;
monkeys.clear(); //清空list容器
for (int i = 1; i <= n; ++i) //将猴子的编号放入list
monkeys.push_back(i);
list<int>::iterator it = monkeys.begin();
while (monkeys.size() > 1) { //只要还有不止一只猴子,就要找一只猴子让其出列
for (int i = 1; i < m; ++i) { //报数
++it;
if (it == monkeys.end())
it = monkeys.begin();
}
it = monkeys.erase(it); //删除元素后,迭代器失效,
//要重新让迭代器指向被删元素的后面
if (it == monkeys.end())
it = monkeys.begin();
}
cout << monkeys.front() << endl; //front返回第一个元素的引用
}
return 0;
}
1
2
3
4
5
6
7
8
9
10
11
/* 数学实现 */
#include <bits/stdc++.h>
using namespace std;
int main(void)
{
int n,m,i,s=0;
scanf("%d%d",&n,&m);
for (i=2; i<=n; i++)
s=(s+m)%i;
cout<<s+1<<endl;
return 0 ;

转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 yxhlfx@163.com

文章标题:约瑟夫问题

本文作者:红尘追风

发布时间:2015-08-22, 14:34:21

原始链接:http://www.micernel.com/2015/08/22/%E7%BA%A6%E7%91%9F%E5%A4%AB%E9%97%AE%E9%A2%98/

版权声明: "署名-非商用-相同方式共享 4.0" 转载请保留原文链接及作者。

目录