'How does printing Josephus sequence work?

The CSES problem Josephus Problem I requires us to print the sequence of how people are chosen for n people and k = 2. I found an elegant solution to this here. Basically, the code is similar to this:

void J(int n)
{
    int a = 1, b = 0;
    while (n > 0)
    {
        for (int i = 2; i <= n; i += 2)
        {
            cout << a * i + b << ' ';
        }
        if (n & 1)
            cout << a + b << ' ', b += a;
        else
            b -= a;
        a <<= 1;
        n >>= 1;
    }
}

Can someone explain why it works?



Solution 1:[1]

I made a few changes to the code to illustrate how it works.

void J(int n)
{
    int a = 1, b = 0;
    while (n > 0)
    {
        std::cout << "n=" << n << std::endl;
        std::cout << "a=" << a << ", b=" << b << std::endl;
        for (int i = 2; i <= n; i += 2)
        {
            std::cout << a * i + b << ' ';
        }

        if (n & 1) // equivalent to n % 2 == 1 (if n is odd)
            std::cout << '*' << a + b << ' ', b += a;
        else
            b -= a;
        
        std::cout << std::endl;
        a <<= 1; // a = a * 2;
        n >>= 1; // n = n / 2;
    }
}

The first thing to note is that the variables a and b are initialized to values that allow us to compute the positions of people who will be eliminated as we go around the circle (also using the iterator i in the for loop). The initial values of a = 1, b = 0 are so we can eliminate every even numbered person on the first iteration.

Next, every time through the while(n) loop, half of the remaining people will be eliminated. If n is odd, we loop back to the beginning of the circle and also eliminate the person at the start of the current iteration.

I think the tricky part here is how the values of a and b change in the if statement. If n is odd we increment b by the value of a. If n is even we subtract the value of a from b. Then we double the value of a to get ready for the next iteration around the circle. The values of a and b are chosen to account for the gaps in the circle left by eliminating positions in the previous iterations.

Finally, we divide n by 2 before the next iteration, since there are half as many people left in the circle. Here's a sample run for n=19.

Output:

n=19
a=1, b=0
2 4 6 8 10 12 14 16 18 *1
n=9
a=2, b=1
5 9 13 17 *3
n=4
a=4, b=3
11 19
n=2
a=8, b=-1
15
n=1
a=16, b=-9
*7

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Bill the Lizard