据说著名犹太历史学家 Josephus有过以下的故事:在罗马人占领乔塔帕特后,39 个犹太人与Josephus及他的朋友躲到一个洞中,39个犹太人决定宁愿死也不要被敌人抓到,于是决定了一个自杀方式,41个人排成一个圆圈,由第1个人开始报数,每报数到第3人该人就必须自杀,然后再由下一个重新报数,直到所有人都自杀身亡为止。然而Josephus 和他的朋友并不想遵从。首先从一个人开始,越过k-2个人(因为第一个人已经被越过),并杀掉第
k个人。接着,再越过k-1个人,并杀掉第
k个人。这个过程沿着圆圈一直进行,直到最终只剩下一个人留下,这个人就可以继续活着。问题是,给定了和,一开始要站在什么地方才能避免被处决?Josephus要他的朋友先假装遵从,他将朋友与自己安排在第16个与第31个位置,于是逃过了这场死亡游戏。
[1]
17世纪的法国数学家加斯帕在《数目的游戏问题》中讲了这样一个故事:15个教徒和15 个非教徒在深海上遇险,必须将一半的人投入海中,其余的人才能幸免于难,于是想了一个办法:30个人围成一圆圈,从第一个人开始依次报数,每数到第九个人就将他扔入大海,如此循环进行直到仅余15个人为止。问怎样排法,才能使每次投入大海的都是非教徒。
问题分析与算法设计
约瑟夫问题并不难,但求解的方法很多;题目的变化形式也很多。这里给出一种实现方法。
题目中30个人围成一圈,因而启发我们用一个循环的链来表示,可以使用结构
数组来构成一个循环链。结构中有两个成员,其一为指向下一个人的指针,以构成环形的链;其二为该人是否被扔下海的标记,为1表示还在船上。从第一个人开始对还未扔下海的人进行计数,每数到9时,将结构中的标记改为0,表示该人已被扔下海了。这样循环计数直到有15个人被扔下海为止。
约瑟夫问题是个有名的问题:N个人围成一圈,从第一个开始报数,第M个将被杀掉,最后剩下一个,其余人都将被杀掉。例如N=6,M=5,被杀掉的顺序是:5,4,6,2,3,1。
分析:
(1)由于对于每个人只有死和活两种状态,因此可以用布尔型数组标记每个人的状态,可用true表示死,false表示活。
(2)开始时每个人都是活的,所以数组初值全部赋为false。
(3)模拟杀人过程,直到所有人都被杀死为止。
约瑟夫问题pascal代码1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | var
a: array [ 1..20 ] of integer ;
n,m,i,j,k,n1,m1: integer ;
begin
readln(m,n);
for i:= 1 to m do
a[i]:=i;
m1:=m;
n1:= 1 ;
while m1> 0 do
begin
j:=(n+n1- 1 - 1 ) mod m1 + 1 ;
n1:=j;
m1:=m1- 1 ;
writeln (a[j]);
for k:=j to m1 do
a[k]:=a[k+ 1 ];
end ;
end .
|
约瑟夫问题C++代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | #include<iostream>
using namespace std;
main()
{
bool a[101]={0};
int n,m,i,f=0,t=0,s=0;
cin>>n>>m;
do
{
++t;
if (t>n)
t=1;
if (!a[t])
s++;
if (s==m)
{
s=0;
cout<<t<< ' ' ;
a[t]=1;
f++;
}
} while (f!=n);
}
|
c++代码2
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 33 34 35 36 | #include <iostream>
#include <iomanip>
#include <cmath>
using namespace std;
int main()
{
int a[8],i,t,k;
for (i=1;i<=8;i++)
{
a[i]=1;
}
t=8;
k=0;
while (t>0)
{
for (i=1;i<=8;i++)
{
if (a[i]==1)
{
k++;
if (k==5)
{
k=0;
a[i]=0;
cout<<setw(3)<<i;
t--;
if (t==0)
{
break ;
}
}
}
}
}
return 0;
}
|
无论是用链表实现还是用数组实现都有一个共同点:要模拟整个游戏过程,不仅程序写起来比较烦,而且时间复杂度高达O(nm),当n,m非常大(例如上百万,上千万)的时候,几乎是没有办法在短时间内出结果的。我们注意到原问题仅仅是要求出最后的胜利者的序号,而不是要读者模拟整个过程。因此如果要追求效率,就要打破常规,实施一点数学策略。
为了讨论方便,先把问题稍微改变一下,并不影响原意:
问题描述:n个人(编号0~(n-1)),从0开始报数,报到(m-1)的退出,剩下的人继续从0开始报数。求胜利者的编号。
我们知道第一个人(编号一定是(m-1)mod n) 出列之后,剩下的n-1个人组成了一个新的
约瑟夫环(以编号为k=m mod n的人开始):
k k+1 k+2 ... n-2,n-1,0,1,2,... k-2
并且从k开始报0。
我们把他们的编号做一下转换:
k --> 0
k+1 --> 1
k+2 --> 2
...
...
k-2 --> n-2
变换后就完完全全成为了(n-1)个人报数的子问题,假如我们知道这个子问题的解:例如x是最终的胜利者,那么根据上面这个表把这个x变回去不刚好就是n个人情况的解吗?!!变回去的公式很简单,相信大家都可以推出来:x'=(x+k) mod n
如何知道(n-1)个人报数的问题的解?对,只要知道(n-2)个人的解就行了。(n-2)个人的解呢?当然是先求(n-3)的情况 ---- 这显然就是一个倒推问题!好了,思路出来了,下面写
递推公式:
令f表示i个人玩游戏报m退出最后胜利者的编号,最后的结果自然是f[n]
递推公式
f[1]=0;
f[i]=(f[i-1]+m) mod i; (i>1)
有了这个公式,我们要做的就是从1-n顺序算出f的数值,最后结果是f[n]。因为实际生活中编号总是从1开始,我们输出f[n]+1
由于是逐级递推,不需要保存每个f,程序也是异常简单:
约瑟夫问题pascal代码2
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | var
n,m,i,s,p: integer ;
a: array [ 1..10000 ] of integer ;
begin
read(n,m);
for i:= 1 to n do
a[i]:= 1 ;
p:= 0 ;s:= 0 ;
repeat
for i:= 1 to n do
begin
if a[i]= 0
then continue;
s:=s+a[i];
if s=m then
begin
write (i, ' ' );打印出圈者;
a[i]:= 0 ;
p:=p+ 1 ;
s:= 0 ;
end ;
end ;
until p=n;
end .
|
约瑟夫问题pascal代码3
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | Var
a: array [ 1..100 ] of integer ;
n,m,i,j,p: integer ;
Begin
write ( 'Input n,m:' );
readln(n,m);
for i:= 1 to n do
a[i]:=i;
p:= 1 ;
for i:= 1 to n do
begin
j:= 0 ;
while j<m do
begin
if a[p]<> 0 then j:=j+ 1 ;
if p=n then p:= 1 else p:=p+ 1 ;
end ;
if p<> 1
then begin write (a[p- 1 ], ' ' );a[p- 1 ]:= 0 ; end
else begin write (a[n], ' ' );a[n]:= 0 ; end ;
end ;
end .
|
约瑟夫问题pascal代码4
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | Var
a: array [ 1..100 ] of integer ;
n,m,p,i,j: integer ;
Begin
readln(n,m);
for i:= 1 to n- 1 do
a[i]:=i+ 1 ;
a[n]:= 1 ;
p:=n;
for i:= 1 to n do
begin
for j:= 1 to m- 1 do
p:=a[p];
write (a[p], ' ' );
a[p]:=a[a[p]];
end ;
End .
|
约瑟夫问题c++
1 2 3 4 5 6 7 8 9 10 | #include <iostream>
using namespace std;
const int m = 3;
int main()
{
int n, f = 0;
cin >> n;
for ( int i = 1; i <= n; i++) f = (f + m) % i;
cout << f + 1 << endl;
}
|
约瑟夫问题pascal代码5
1 2 3 4 5 6 7 8 | var n,m,i,s: integer ;
begin
write ( 'N M =' );
read(n,m);
for i:= 2 to n do
s:=(s+m) mod i;
writeln ( 'The winner is ' ,s+ 1 );
end .
|
这个算法的时间复杂度为O(n),相对于模拟算法已经有了很大的提高。算n,m等于一百万,一千万的情况不是问题了。可见,适当地运用数学策略,不仅可以让编程变得简单,而且往往会成倍地提高算法执行效率。
约瑟夫问题python代码
该程序基于python3.x实现
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 | nums = 41
call = 3
peoples = []
for _ in range (nums):
peoples.append( True )
result = []
num = 1
while ( any (peoples)):
for index,people in enumerate (peoples):
if people:
if num = = call:
peoples[index] = False
result.append(index + 1 )
num = 1
else :
num + = 1
print ( '-' * 25 )
print ( '
总数为%d,报数为%d' % (nums,call))
print ( '约瑟夫序列为:
%s
' % result)
print ( '-' * 25 )
|
约瑟夫问题约瑟夫问题10e100版(from vijios)
描述 Description
n个人排成一圈。从某个人开始,按
顺时针方向依次编号。从编号为1的人开始顺时针“一二一”报数,报到2的人退出圈子。这样不断循环下去,圈子里的人将不断减少。由于人的个数是有限的,因此最终会剩下一个人。试问最后剩下的人最开始的编号。
输入格式 Input Format
一个正整数n,表示人的个数。输入数据保证数字n不超过100位。
输出格式 Output Format
一个正整数。它表示经过“一二一”报数后最后剩下的人的编号。
样例输入 Sample Input
9
样例输出 Sample Output
3
时间限制 Time Limitation
各个测试点1s
注释 Hint
样例说明
当n=9时,退出圈子的人的编号依次为:
2 4 6 8 1 5 9 7
最后剩下的人编号为3
初见这道题,可能会想到模拟。可是数据实在太大啦!!
我们先拿手来算,可知n分别为1,2,3,4,5,6,7,8...时的结果是1,1,3,1,3,5,7,1...
有如下规律:从1到下一个1为一组,每一组中都是从1开始递增的奇数,且每组元素的个数分别为1,2,4...
这样就好弄了!!
大体思路如下:
①read(a)
②b:=1,c:=1{b为某一组的元素个数,c为累计所加到的数}
③while c<a do (b:=b*2,c:=b+c){超过目标时停止加数}
⑥c:=c-b{退到前一组}
⑦x:=a-c{算出目标为所在组的第几个元素}
⑧ans:=x*2-1{求出该元素}
⑨write(ans)
有了思路,再加上高精度就可以了。我写的代码比较猥琐,因为是先把上面的思路敲进去,再写过程,又把一些简单的过程合到主程序中了,所以有点乱,也有点猥琐。起提供思路的作用还是完全可以的吧~~~
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 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 | var
a,b,c: array [ 1..105 ] of longint ;
la,lb,lc,i: longint ;
s: string ;
procedure incc;
var
i: integer ;
begin
for i:= 1 to 105 do
c:=c+b;
for i:= 1 to 104 do
if c> 9 then
begin
c:=c+cdiv10;
c:=c mod 10 ;
end ;
end ;
functioncxiaoa: boolean ;
vari: integer ;
begin
cxiaoa:= false ;
fori:=105downto1do
ifc<athenbegincxiaoa:= true ;break; end
elseifc>athenbreak;
end ;
proceduredoubleb;
vari: integer ;
begin
fori:=1to105dob:=b* 2 ;
fori:=1to104doifb>9then
begin
b:=b+bdiv10;
b:=bmod10;
end ;
end ;
proceduredecc;
vari,j: integer ;
begin
fori:=1to104do
ifc>=bthenc:=c-belse
begin
j:=i+ 1 ;
whilec[j]=0doinc(j);
whilej>ido
begin
c[j]:=c[j]- 1 ;
c[j- 1 ]:=c[j- 1 ]+ 10 ;
dec(j);
end ;
c:=c-b;
end ;
end ;
procedurefua;
vari: integer ;
begin
fori:=1to104do
ifa>cthena:=a-celse
begin
a:=a- 1 ;
a:=a+ 10 ;
a:=a-c;
end ;
end ;
procedureoutit;
vari,j: integer ;
begin
fori:=1to105doa:=a* 2 ;
fori:=1to104doifa>9then
begin
a:=a+adiv10;
a:=amod10;
end ;
ifa[ 1 ]>0thena[ 1 ]:=a[ 1 ]-1else
begin
j:= 2 ;
whilea[j]=0doinc(j);
whilej>1do
begin
a[j]:=a[j]- 1 ;
a[j- 1 ]:=a[j- 1 ]+ 10 ;
dec(j);
end ;
a[ 1 ]:=a[ 1 ]- 1 ;
end ;
fori:=105downto1doifa>0thenbeginj:=i;break; end ;
fori:=jdownto1dowrite(a);
end ;
begin
readln(s);
la:=length(s);
fori:=ladownto1doa:=ord(s[la+ 1 -i])-ord( '0' );
b[ 1 ]:= 1 ;
c[ 1 ]:= 1 ;
whilecxiaoado
begin
doubleb;
incc;
end ;
decc;
fua;
outit;
end .
|
约瑟夫问题问题表述
一. 问题描述:
一堆猴子都有编号,编号是1,2,3 ...m,这群猴子(m个)按照1-m的顺序围坐一圈,从第1开始数,每数到第N个,该猴子就要离开此圈,这样依次下来,直到圈中只剩下最后一只猴子,则该猴子为大王。
约瑟夫
"密码问题"
问题描述:编号为1、2、3、...、N的N个人按顺时针方向围坐一圈,每人持有一个密码(正整数)。从指定
编号为1的人开始,按顺时针方向自1开始顺序报数,报到指定数M时停止报数,报M的人出列,并将
他的密码作为新的M值,从他在顺时针方向的下一个人开始,重新从1报数,依此类推,直至所有的
人全部出列为止。请设计一个程序求出出列的顺序,其中N≤30,M及密码值从键盘输入。
二. 基本要求:
(1) 输入数据:输入m,n m,n 为整数,n<m
(2)中文提示按照m个猴子,数n 个数的方法,输出为大王的猴子是几号 ,建立一个函数来实现此功能
约瑟夫问题编程解决
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 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 | #include <stdio.h>
#include <malloc.h>
#defineLENsizeof(structmonkey)//定义structmonkey这个类型的长度
struct monkey
{
int num;
struct monkey *next;
};
struct monkey *create( int m)
{
struct monkey *head,*p1,*p2;
inti;
p1=p2=( struct monkey*) malloc (LEN);
head=p1;
head->num=1;
for (i=1,p1->num=1;i<m;i++)
{
p1=( struct monkey*) malloc (LEN);
p1->num=i+1;
p2->next=p1;
p2=p1;
}
p2->next=head;
return head;
}
struct monkey *findout( struct monkey *start, int n)
{
int i;
struct monkey *p;
i=n;
p=start;
for (i=1;i<n-1;i++)
p=p->next;
return p;
}
struct monkey *letout( struct monkey *last)
{
struct monkey *out,*next;
out=last->next;
last->next=out->next;
next=out->next;
free (out);
return next;
}
int main()
{
int m,n,i,king;
struct monkey *p1,*p2;
printf ( "请输入猴子的个数m:
" );
scanf ( "%d" ,&m);
printf ( "每次数猴子的个数n:
" );
scanf ( "%d" ,&n);
if (n==1)
{
king=m;
}
else
{
p1=p2=create(m);
for (i=1;i<m;i++)
{
p2=findout(p1,n);
p1=p2;
p2=letout(p1);
p1=p2;
}
king=p2->num;
free (p2);
}
printf ( "猴王的编号是:%d
" ,king);
return 0;
}
|
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 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 | #include<stdio.h>
#include<string.h>
#include<stdlib.h>
intfre(charmok[],intk)
{
inti;
printf ( "
猴子编号:
" );
for (i=0;mok[i]!= ' ' ;i++)
printf ( "%d " ,mok[i]);
for (i=k;mok[i]!= ' ' ;i++)
{
mok[i]=mok[i+1];
}
putchar ( '
' );
for (i=0;mok[i]!= ' ' ;i++)
printf ( "%d " ,mok[i]);
printf ( "
按回车继续下一轮:
" );
getch();
return0;
}
int main()
{
char mok[50];
int i;
int n,s,b;
int j,k;
mok[0]=1;
printf ( "请输入猴子的总数:
" );
scanf ( "%d" ,&n);
for (i=1;i<n;i++)
{
mok[i]=i+1;
}
mok[n]= ' ' ;
printf ( "请输入循环单位:
" );
scanf ( "%d" ,&s);
b=n;
for (j=1,k=0;;j++,k++)
{
if (b==1)
{
b=mok[0];
break ;
}
if (j==s)
{
printf ( "
它出列了:%d
" ,mok[k]);
fre(mok,k);
b--;
j=1;
}
if (mok[k+1]== ' ' )
k=-1;
}
printf ( "
最终大王是他:%d
" ,b);
return0;
}
|
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 | #include<stdio.h>
#include<malloc.h>
int main()
{
int *person,i,node,n,m;
scanf ( "%d%d" ,&n,&m);
person=( int *) malloc ( sizeof ( int )*(n+1));
for (i=1;i<n;i++)
{
person[i]=i+1;
}
person[n]=1;
node=1;
while (node!=person[node])
{
for (i=1;i<m-1;i++)
{
node=person[node];
}
printf ( "%d" ,person[node]);
person[node]=person[person[node]];
node=person[node];
}
printf ( "%d" ,node);
return 0;
}
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | var
a:array[ 1. . 10000 ]ofinteger;
n,s,i,j:integer;
begin
read(m,n);
fori: = 1tomdoa [i]: = 1 ;
j: = 0 ;
fori: = 1tomdo
begin
s: = 0 ;
whiles<ndo
begin
ifj<mtheninc(j)
elsej: = 1 ;
s: = s + a[j];
end;
write(j);
a[j]: = 0 ;
end;
end.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 | #include<iostream>
#include<conio.h>
using namespace std;
main()
{
int n,m,i,s=0;
cout<< "N:" ;cin>>n;
cout<< "M:" ;cin>>m;
for (i=2;i<=n;i++)
s=(s+m)%i;
cout<< "新的大王是:" <<s+1;
getch();
}
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | #include<stdio.h>
#include<conio.h>
intmain( void )
{
intn,i=0,m,p;
scanf ( "%d%d" ,&n,&m);
while (++i<=n)
{
p=i*m;
while (p>n)
p=p-n+(p-n-1)/(m-1);
printf ( "%d
" ,p);
}
getch();
return0;
}
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | #include<iostream>
usingnamespacestd;
intking(intM,intN)
{
intk=0;
for (inti=2;i<=M;i++)
k=(k+N)%i;
return ++k;}i
ntmain()
{
intn,m;
while ( scanf ( "%d%d" ,&n,&m)&&n&&m)
{
cout<<king(n,m)<<endl;
}
return0;
}
|
php有非常完善的数据结构模拟方案,可以非常简洁的解决这样的问题!
1 2 3 4 5 6 7 8 9 10 11 12 13 | function king( $n , $m ){
$monkey = range(1, $n );
$i =0;
while ( count ( $monkey )>1){
$i +=1;
$head = array_shift ( $monkey );
if ( $i % $m !=0 ){
array_push ( $monkey , $head );
}
}
return $monkey [0];
}
echo '剩余' ,king(3,4), '号猴子' ;
|
3. Python遍历数组
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | def Monkey(n, M):
if n = = 0 or M = = 1 :
return 0
L = list ( map ( list , list ( enumerate ([ True ] * n,start = 1 ))))
counter = 0
while [L[i][ 1 ] for i in range ( len (L))].count( True ) > 1 :
for i in range ( len (L)):
if L[i][ 1 ] = = True :
counter + = 1
if counter % M = = 0 and L[i][ 1 ] = = True :
L[i][ 1 ] = False
return list ( filter ( lambda x:x[ 1 ] = = True , L))[ 0 ][ 0 ]
Monkey( 2009 , 3 )
|
约瑟夫问题笔算解决
笔算解决约瑟夫问题
在M比较小的时候 ,可以用笔算的方法求解,
M=2
即N个人围成一圈,1,2,1,2的报数,报到2就去死,直到只剩下一个人为止。
当N=2^k的时候,第一个报数的人就是最后一个死的,
对于任意的自然数N 都可以表示为N=2^k+t,其中t<n/2
于是当有t个人去死的时候,就只剩下2^k个人 ,这2^k个人中第一个报数的就是最后去死的。这2^k个人中第一个报数的人就是2t+1
于是就求出了当M=2时约瑟夫问题的解:
求出不大于N的最大的2的整数次幂,记为2^k,最后一个去死的人是2(N-2^k)+1
M=3
即N个人围成一圈,1,2,3,1,2,3的报数,报到3就去死,直到只剩下一个人为止。
此时要比M=2时要复杂的多
我们以N=2009为例计算
N=2009,M=3时最后被杀死的人记为F(2009,3),或者可以简单的记为F(2009)
假设这种情况下还剩下n个人,则下一轮将杀死[n/3]个人,[]表示小于等于这个数的最大整数,还剩下n-[n/3]个人
设这n个人为a1,a2,...,a(n-1),an
从a1开始报数,一圈之后,剩下的人为a1,a2,a4,a5,...a(n-n mod 3-1),a(n-n mod 3+1),..,an
于是可得:
1、这一轮中最后一个死的是a(n-n mod 3),下一轮第一个报数的是a(n-n mod 3+1)
2、若3|n,则最后死的人为新一轮的第F(n-[n/3])个人
若n mod 3≠0 且f(n-[n/3])<=n mod 3则最后死的人为新一轮的第n-[n/3]+F(n-[n/3])-(n mod 3)人
若n mod 3≠0 且f(n-[n/3])>n mod 3则最后死的人为新一轮的第F(n-[n/3])-(n mod 3)人
3、新一轮第k个人对应原来的第 3*[(k-1)/2]+(k-1)mod 2+1个人
综合1,2,3可得:
F(1)=1,F(2)=2,F(3)=2,F(4)=1,F(5)=4,F(6)=1,
当f(n-[n/3])<=n mod 3时 k=n-[n/3]+F(n-[n/3])-(n mod 3),F(n)=3*[(k-1)/2]+(k-1)mod 2+1
当f(n-[n/3])>n mod 3时 k=F(n-[n/3])-(n mod 3) ,F(n)=3*[(k-1)/2]+(k-1)mod 2+1
这种算法需要计算 [log(3/2)2009]次 这个数不大于22,可以用笔算了
于是:
第一圈,将杀死669个人,这一圈最后一个被杀死的人是2007,还剩下1340个人,
第二圈,杀死446人,还剩下894人
第三圈,杀死298人,还剩下596人
第四圈,杀死198人,还剩下398人
第五圈,杀死132人,还剩下266人
第六圈,杀死88人,还剩下178人
第七圈,杀死59人,还剩下119人
第八圈,杀死39人,还剩下80人
第九圈,杀死26人,还剩下54人
第十圈,杀死18人,还剩36人
十一圈,杀死12人,还剩24人
十二圈,杀死8人,还剩16人
十三圈,杀死5人,还剩11人
十四圈,杀死3人,还剩8人
十五圈,杀死2人,还剩6人
F(1)=1,F(2)=2,F(3)=2,F(4)=1,F(5)=4,F(6)=1,
然后逆推回去
F(8)=7 F(11)=7 F(16)=8 f(24)=11 f(36)=16 f(54)=23 f(80)=31 f(119)=43 f(178)=62 f(266)=89 f(398)=130
F(596)=191 F(894)=286 F(1340)=425 F(2009)=634
约瑟夫问题规律解决
视频
[2]
中给出了经典的约瑟父问题的数学解法,当猴子选王问题的N=2时就是经典的约瑟父问题
对于经典约瑟父问题,视频中的解法是:
视频中给出的解释是:
当
时,序号为1的人总是是最后留下来的人。对于
,当去掉
个人后,剩下的人正好组成
个人围成的圈,此圈中的序号1的人将是最后留下来的人。而对应到原来的圈,这个人的序号就是
,因为去掉
个人时正好就跳过了
个人,而下一个人的序号就是
。
推广到猴子选王问题,从以上解法不难看出,解法就是把2换成N,即:
3)所以,最后留下来的猴子的序号为
,mod是取余数,例如:3 mod 2 = 1
--------------------------
m=8, N=3,8=3^1+5, 按照他的算法,此时N=3,l=5, 按照他的算法最后剩下来的是8,事实上很容易直接验算最后留下来的是7,上面的公式是错误的。