📜  约瑟夫斯问题使用位魔术

📅  最后修改于: 2021-05-04 12:16:27             🧑  作者: Mango

问题

这个问题以与罗马人作战的犹太历史学家弗拉维乌斯·约瑟夫斯(Flavius Josephus)的名字命名。根据约瑟夫斯的说法,他和他的犹太士兵在一个山洞里被罗马人围困和包围,他们在投降和俘获中选择了谋杀和自杀。他们决定将所有士兵围成一圈,从坐在第一个位置的士兵开始,每个士兵将按顺序杀害该士兵。因此,如果有5名士兵围成一个圈,位置编号分别为1、2、3、4、5。士兵1杀死2,然后3杀死4,然后5杀死1,然后3杀死5,因为3是唯一的一人离开,然后三人自杀。

现在,约瑟夫斯不想被谋杀或自杀。他宁愿被罗马人俘虏,并面临一个问题。他必须弄清楚他应该围成一个圈坐(假设总共有n个人,而坐在位置1的那个人有第一个谋杀的机会),以便他是最后一个站立的人,而不是自杀。将投降给罗马人。

模式

如果对n的不同值进行计算,则会在此处找到一个模式。如果n为2的真次幂,则答案始终为1。每n大于2的次幂,则答案增加2。

n soldiers 2a+ l Survivor W(n) = 2l + 1
1 1 + 0 2 * 0 + 1 = 1
2 2 + 0 2 * 0 + 1 = 1
3 2 + 1 2 * 1 + 1 = 3
4 4 + 0 2 * 0 + 1 = 1
5 4 + 1 2 * 1 + 1 = 3
6 4 + 2 2 * 2 + 1 = 5
7 4 + 3 2 * 3 + 1 = 7
8 8 + 0 2 * 0 + 1 = 1
9 8 + 1 2 * 1 + 1 = 3
10 8 + 2 2 * 2 + 1 = 5
11 8 + 3 2 * 3 + 1 = 7
12 8 + 4 2 * 4 + 1 = 9

现在,对于每个n,可以通过从数字中减去2的最大可能幂来找出约瑟夫斯的正确位置,我们得到了答案(假设n的值不是2的纯幂,否则答案为1)

N = 2 a +某物

其中,a =可能的最大功率

绝招

每当有人谈论2的幂时,想到的第一个单词是“二进制”。解决这个问题的方法是,用二进制比用十进制更容易,更短。这有一个窍门。由于我们需要扣除二进制的最大可能幂,因此该数字是最高有效位。在最初的约瑟夫斯问题中,还有另外40名士兵以及约瑟夫斯,使n = 41 。二进制数中的41是101001。如果将MSB(即最左边的1)移到最右边,则会得到010011,即19(十进制),这就是答案。在所有情况下都是如此。使用位操作可以轻松完成此操作。

C++
// C++ program for josephus problem
#include 
using namespace std;
  
// function to find the position of the Most
// Significant Bit
int msbPos(int n)
{
    int pos = 0;
    while (n != 0) {
        pos++;
  
        // keeps shifting bits to the right
        // until we are left with 0
        n = n >> 1;
    }
    return pos;
}
  
// function to return at which place Josephus
// should sit to avoid being killed
int josephify(int n)
{
    /* Getting the position of the Most Significant
        Bit(MSB). The leftmost '1'. If the number is 
    '41' then its binary is '101001'.
        So msbPos(41) = 6 */
    int position = msbPos(n);
  
    /* 'j' stores the number with which to XOR the 
        number 'n'. Since we need '100000'
    We will do 1<<6-1 to get '100000' */
    int j = 1 << (position - 1);
  
    /* Toggling the Most Significant Bit. Changing
    the leftmost '1' to '0'.
    101001 ^ 100000 = 001001 (9) */
    n = n ^ j;
  
    /* Left-shifting once to add an extra '0' to 
        the right end of the binary number 
        001001 = 010010 (18) */
    n = n << 1;
  
    /* Toggling the '0' at the end to '1' which 
    is essentially the same as putting the 
    MSB at the rightmost place. 010010 | 1
    = 010011 (19) */
    n = n | 1;
  
    return n;
}
  
// hard coded driver main function to run the program
int main()
{
    int n = 41;
    cout <


C
// C program for josephus problem
  
#include 
  
// function to find the position of the Most
// Significant Bit
int msbPos(int n)
{
    int pos = 0;
    while (n != 0) {
        pos++;
  
        // keeps shifting bits to the right
        // until we are left with 0
        n = n >> 1;
    }
    return pos;
}
  
// function to return at which place Josephus
// should sit to avoid being killed
int josephify(int n)
{
    /*  Getting the position of the Most Significant
        Bit(MSB). The leftmost '1'. If the number is 
       '41' then its binary is '101001'.
        So msbPos(41) = 6   */
    int position = msbPos(n);
  
    /* 'j' stores the number with which to XOR the 
        number 'n'. Since we need '100000'
       We will do 1<<6-1 to get '100000' */
    int j = 1 << (position - 1);
  
    /* Toggling the Most Significant Bit. Changing
       the leftmost '1' to '0'.
       101001 ^ 100000 = 001001 (9) */
    n = n ^ j;
  
    /*  Left-shifting once to add an extra '0' to 
        the right end of the binary number 
        001001 = 010010 (18) */
    n = n << 1;
  
    /* Toggling the '0' at the end to '1' which 
       is essentially the same as putting the 
       MSB at the rightmost place. 010010 | 1
       = 010011 (19) */
    n = n | 1;
  
    return n;
}
  
// hard coded driver main function to run the program
int main()
{
    int n = 41;
    printf("%d\n", josephify(n));
    return 0;
}


Java
// Java program for josephus problem
  
public class GFG 
{
    // method to find the position of the Most
    // Significant Bit
    static int msbPos(int n)
    {
        int pos = 0;
        while (n != 0) {
            pos++;
       
            // keeps shifting bits to the right
            // until we are left with 0
            n = n >> 1;
        }
        return pos;
    }
       
    // method to return at which place Josephus
    // should sit to avoid being killed
    static int josephify(int n)
    {
        /*  Getting the position of the Most Significant
            Bit(MSB). The leftmost '1'. If the number is 
           '41' then its binary is '101001'.
            So msbPos(41) = 6   */
        int position = msbPos(n);
       
        /* 'j' stores the number with which to XOR the 
            number 'n'. Since we need '100000'
           We will do 1<<6-1 to get '100000' */
        int j = 1 << (position - 1);
       
        /* Toggling the Most Significant Bit. Changing
           the leftmost '1' to '0'.
           101001 ^ 100000 = 001001 (9) */
        n = n ^ j;
       
        /*  Left-shifting once to add an extra '0' to 
            the right end of the binary number 
            001001 = 010010 (18) */
        n = n << 1;
       
        /* Toggling the '0' at the end to '1' which 
           is essentially the same as putting the 
           MSB at the rightmost place. 010010 | 1
           = 010011 (19) */
        n = n | 1;
       
        return n;
    }
      
    // Driver Method
    public static void main(String[] args)
    {
        int n = 41;
        System.out.println(josephify(n));
    }
}


Python3
# Python3 program for josephus problem
  
# Function to find the position 
# of the Most Significant Bit
def msbPos(n):
    pos = 0
    while n != 0:
        pos += 1
        n = n >> 1
    return pos
  
# Function to return at which 
# place Josephus should sit to
# avoid being killed
def josephify(n):
      
    # Getting the position of the Most
    # Significant Bit(MSB). The leftmost '1'.
    # If the number is '41' then its binary
    # is '101001'. So msbPos(41) = 6
    position = msbPos(n)
  
    # 'j' stores the number with which to XOR  
    # the number 'n'. Since we need '100000'
    # We will do 1<<6-1 to get '100000'
    j = 1 << (position - 1)
  
    # Toggling the Most Significant Bit.
    # Changing the leftmost '1' to '0'.
    # 101001 ^ 100000 = 001001 (9)
    n = n ^ j
  
    # Left-shifting once to add an extra '0'  
    # to the right end of the binary number 
    # 001001 = 010010 (18)
    n = n << 1
  
    # Toggling the '0' at the end to '1'  
    # which is essentially the same as 
    # putting the MSB at the rightmost
    # place. 010010 | 1 = 010011 (19)
    n = n | 1
  
    return n
  
# Driver Code
n = 41
print (josephify(n))
  
# This code is contributed by Shreyanshi Arun.


C#
// C# program for Josephus Problem
using System;
  
public class GFG 
{
      
    // Method to find the position 
    // of the Most Significant Bit
    static int msbPos(int n)
    {
        int pos = 0;
        while (n != 0) {
            pos++;
      
            // keeps shifting bits to the right
            // until we are left with 0
            n = n >> 1;
        }
        return pos;
    }
      
    // method to return at which place Josephus
    // should sit to avoid being killed
    static int josephify(int n)
    {
          
        // Getting the position of the Most Significant
        // Bit(MSB). The leftmost '1'. If the number is 
        // '41' then its binary is '101001'.
        // So msbPos(41) = 6 
        int position = msbPos(n);
      
        // 'j' stores the number with which to XOR  
        // the number 'n'. Since we need '100000'
        // We will do 1<<6-1 to get '100000' 
        int j = 1 << (position - 1);
      
        // Toggling the Most Significant Bit. 
        // Changing the leftmost '1' to '0'.
        // 101001 ^ 100000 = 001001 (9) 
        n = n ^ j;
      
        // Left-shifting once to add an extra '0' 
        // to the right end of the binary number 
        // 001001 = 010010 (18) 
        n = n << 1;
      
        // Toggling the '0' at the end to '1' which 
        // is essentially the same as putting the 
        // MSB at the rightmost place. 010010 | 1
        // = 010011 (19) 
        n = n | 1;
      
        return n;
    }
      
    // Driver code
    public static void Main()
    {
        int n = 41;
        Console.WriteLine(josephify(n));
    }
}
  
// This code is contributed by vt_m .


PHP
> 1;
    }
    return $pos;
}
  
// function to return at which place Josephus
// should sit to avoid being killed
function josephify($n)
{
    /* Getting the position of the Most 
       Significant Bit(MSB). The leftmost '1'.
       If the number is '41' then its binary
       is '101001'. So msbPos(41) = 6 */
    $position = msbPos($n);
  
    /* 'j' stores the number with which to 
        XOR the number 'n'. Since we need 
    '100000'. We will do 1<<6-1 to get '100000' */
    $j = 1 << ($position - 1);
  
    /* Toggling the Most Significant Bit. 
    Changing the leftmost '1' to '0'.
    101001 ^ 100000 = 001001 (9) */
    $n = $n ^ $j;
  
    /* Left-shifting once to add an extra '0'
       to the right end of the binary number 
       001001 = 010010 (18) */
    $n = $n << 1;
  
    /* Toggling the '0' at the end to '1' which 
    is essentially the same as putting the 
    MSB at the rightmost place. 010010 | 1
    = 010011 (19) */
    $n = $n | 1;
  
    return $n;
}
  
// Driver Code
$n = 41;
print(josephify($n));
  
// This code is contributed by mits
?>


输出:

19

参考:

  1. 亲爱的
  2. 维基百科

以前有关同一主题的文章:

  1. 约瑟夫斯问题|集合1(AO(n)解)
  2. 约瑟夫斯问题|集合2(k = 2时的简单解决方案)