约瑟夫问题 约瑟夫问题由来已久,而这个问题的解法也在不断改进,只是目前仍没有一个极其高效的算法(log 以内)解决这个问题。
问题描述 n 个人标号 0 , 1 , ⋯ , 𝑛   − 1 0 , 1 , ⋯ , n − 1 0 0 𝑘 k 
这个经典的问题由约瑟夫于公元 1 世纪提出,尽管他当时只考虑了 𝑘   = 2 k = 2 
过程 朴素算法 最朴素的算法莫过于直接枚举。用一个环形链表枚举删除的过程,重复 𝑛   − 1 n − 1 Θ ( 𝑛 2 ) Θ ( n 2 ) 
简单优化 寻找下一个人的过程可以用线段树优化。具体地,开一个 0 , 1 , ⋯ , 𝑛   − 1 0 , 1 , ⋯ , n − 1 𝑘 k 
线性算法 设 𝐽 𝑛 , 𝑘 J n , k 𝑛 , 𝑘 n , k 
𝐽 𝑛 , 𝑘 = ( 𝐽 𝑛 − 1 , 𝑘 + 𝑘 ) m o d 𝑛 J n , k = ( J n − 1 , k + k ) mod n 这个也很好推。你从 0 0 𝑘 k 𝑘   − 1 k − 1 𝑛   − 1 n − 1 𝑛   − 1 n − 1 𝑘 k Θ ( 𝑛 ) Θ ( n ) 
实现  int   josephus ( int   n ,   int   k )   { 
   int   res   =   0 ; 
   for   ( int   i   =   1 ;   i   <=   n ;   ++ i )   res   =   ( res   +   k )   %   i ; 
   return   res ; 
} 
对数算法 对于 𝑘 k 𝑛 n Θ ( 𝑘 l o g  𝑛 ) Θ ( k log  n ) 
考虑到我们每次走 𝑘 k ⌊ 𝑛 𝑘 ⌋ ⌊ n k ⌋ 𝑛   − ⌊ 𝑛 𝑘 ⌋ n − ⌊ n k ⌋ ⌊ 𝑛 𝑘 ⌋   ⋅ 𝑘 ⌊ n k ⌋ ⋅ k 𝑛   − 𝑛 m o d 𝑘 n − n mod k 𝑘 k 𝑛   − ⌊ 𝑛 𝑘 ⌋ n − ⌊ n k ⌋ 𝑘 k 1 1 0 0 𝑘 k 𝑛 n 𝑛 n 0 0 𝑘 𝑘 − 1 k k − 1 
实现   1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
10 
11 
12 int   josephus ( int   n ,   int   k )   { 
   if   ( n   ==   1 )   return   0 ; 
   if   ( k   ==   1 )   return   n   -   1 ; 
   if   ( k   >   n )   return   ( josephus ( n   -   1 ,   k )   +   k )   %   n ;    // 线性算法 
   int   res   =   josephus ( n   -   n   /   k ,   k ); 
   res   -=   n   %   k ; 
   if   ( res   <   0 ) 
     res   +=   n ;    // mod n 
   else 
     res   +=   res   /   ( k   -   1 );    // 还原位置 
   return   res ; 
} 
可以证明这个算法的复杂度是 Θ ( 𝑘 l o g  𝑛 ) Θ ( k log  n ) 𝑥 x 𝑛 ( 1 − 1 𝑘 ) n ( 1 − 1 k ) 
𝑛 ( 1 − 1 𝑘 ) 𝑥 = 1 n ( 1 − 1 k ) x = 1 解这个方程得到
𝑥 = − l n  𝑛 l n  ( 1 − 1 𝑘 ) x = − ln  n ln  ( 1 − 1 k ) 下面我们证明该算法的复杂度是 Θ ( 𝑘 l o g  𝑛 ) Θ ( k log  n ) 
证明  考虑 l i m 𝑘 → ∞ 𝑘 l o g  ( 1 − 1 𝑘 ) lim k → ∞ k log  ( 1 − 1 k ) 
 l i m 𝑘 → ∞ 𝑘 l o g  ( 1 − 1 𝑘 ) = l i m 𝑘 → ∞ l o g  ( 1 − 1 𝑘 ) 1 / 𝑘 = l i m 𝑘 → ∞ d d 𝑘 l o g  ( 1 − 1 𝑘 ) d d 𝑘 ( 1 𝑘 ) = l i m 𝑘 → ∞ 1 𝑘 2 ( 1 − 1 𝑘 ) − 1 𝑘 2 = l i m 𝑘 → ∞ − 𝑘 𝑘 − 1 = − l i m 𝑘 → ∞ 1 1 − 1 𝑘 = − 1 lim k → ∞ k log  ( 1 − 1 k ) = lim k → ∞ log  ( 1 − 1 k ) 1 / k = lim k → ∞ d d k log  ( 1 − 1 k ) d d k ( 1 k ) = lim k → ∞ 1 k 2 ( 1 − 1 k ) − 1 k 2 = lim k → ∞ − k k − 1 = − lim k → ∞ 1 1 − 1 k = − 1 所以 𝑥   ∼ 𝑘 l n  𝑛 , 𝑘   → ∞ x ∼ k ln  n , k → ∞ − l n  𝑛 l n  ( 1 − 1 𝑘 )   = Θ ( 𝑘 l o g  𝑛 ) − ln  n ln  ( 1 − 1 k ) = Θ ( k log  n ) 
本页面主要译自博文 Задача Иосифа  与其英文翻译版 Josephus Problem 。其中俄文版版权协议为 Public Domain + Leave a Link;英文版版权协议为 CC-BY-SA 4.0。 
2023/2/18 07:57:07 ,更新历史 在 GitHub 上编辑此页! Enter-tainer , sshwy , F1shAndCat , FFjet , iamtwz , Ir1d CC BY-SA 4.0  和 SATA