Joseph problem (array + queue + formula iteration + recursion)

Posted by QbertsBrother on Sat, 15 Jan 2022 22:59:10 +0100

Joseph problem (array + queue + formula iteration + recursion)

The Joseph Ring problem originates from a Jewish story:
The Romans captured the bridge Tapat, and 41 people hid in a cave to escape the catastrophe. Among the 41 people, the historian Josephus (Joseph) and one of his friends. The remaining 39 people decided to commit collective suicide in order to show that they would not give in to the Romans. We have formulated a suicide plan. All 41 people form a circle. The first person starts counting clockwise, and each person who counts 3 commits suicide immediately, and then the next person starts counting again. It is still that each person who counts 3 commits suicide immediately... Until all people commit suicide. Joseph and his friends didn't want to commit suicide, so Joseph thought of a plan. They were also involved in the suicide plan, but they escaped suicide in the end.

Simplify the problem (a less bloody example):

There are n people sitting around, numbered 1 to N. now, start counting from the person numbered 1, count to m, then count again from the next number (person) listed, and count to M. go on until there is only the last person left, and output its number.

For example, enter

4 5

Output results

2

The idea of using arrays is very simple (this is what I did when I first came to this problem)

Basic idea (easy to understand): at the beginning, store all numbers in the array and initialize them to 0. Define a variable c to represent the number of people currently counted, then define a variable k to represent the number of people counted from 1 to m, and then define a variable s to represent the number of people in the circle.
The code is as follows:

#include<iostream>
using namespace std;
int main()
{
    int n,m;//n means how many people participate in the game, and m means how many people go out of the circle
    cin>>n>>m;
    int arr[1000]= {0}; //0 means people are still in the circle
    int k=0;//Count to what
    int c=0;//Current person's number
    int s=0;//Number of people out of the circle and
    while(s!=n)
    {
        c++;
        if(c>n)c=1;//When the number is greater than the total number, start counting again
        if(arr[c]==0)
        {
            k++;
            if(m==k)
            {
                arr[c]=1;//Don't count off next time after marking
                s++;
                //cout<<c<<" ";
                k=0;//Make the next person count off from 1
            }
        }
    }
    cout<<c;
}

The compilation results are as follows:

Queue method (C + +)

Compared with arrays, the queue method is better written and well understood.

Solution: store the number of people in the queue according to the number 1-n, then make a double cycle, put the team head element at the end of the team, exit the cycle every m-1, and pop up the team

The head element (equivalent to the person killed), and then cycle until there is only the last person left.

#include<iostream>
#include<queue>
using namespace std;
int main()
{
    queue<int>Q;
    int i,j,n,m;
    cin>>n>>m;
    for(i=1;i<=n;i++)
    {
        Q.push(i);
    }
    while(n!=1)
    {
        for(j=1;j<m;j++)//Equivalent to j < = M-1; Play the role of counting
        {
            Q.push(Q.front());//Put the team head element at the end of the team
            Q.pop();//Pop up queue header element
        }
        Q.pop();//The man who killed
        n--;//Total number minus one
    }
    cout<<Q.front();
}

The operation results are as follows:

Of course, the queue here can also be converted into an array, that is, f, r + +... Do it. Not here.

Method of finding law (formula method)

The core of understanding this recursive formula is to pay attention to how the subscript position of the winner changes. Every time you kill someone, you actually move the array forward by M bits. And then, in reverse, you get this recurrence.

Here to really understand, you can refer to this article
Click to open the boss's article

#include<iostream>
using namespace std;
int main()
{
    int n,m;//n means how many people participate in the game, and m means how many people go out of the circle
    int k=0;
    cin>>n>>m;//Enter n and m
    for(int i=2;i<=n;i++)
    {
        k=(k+m)%i;
    }
    cout<<k+1;//The subscript is recorded, so add 1
}

The compilation results are as follows:

Compared with recursive recursive formulas, they seem to be very similar, but their ideas are essentially different. Recursive derivation is from top to bottom

Recursive retracing; This derivation is from bottom to top, in line with normal logical thinking, and is relatively easy to understand.

The idea of recursion is complex (actually formula method), but the amount of code is short.

N in f(n,k) is the number of people who have not reported, k is the number reported, and the number of the last remaining people is on the right of the equal sign. (starting with No. 1)

Iterative method k=5

f(n,k) = [f(n-1,k) + k] % n

f(4,5)=2

f(3,5)=1

If this goes on, push it backwards.

expand:

remove from the first soldier;

f(3,5)=3

f(4,5)=2

Obviously, in terms of the previous rules, it is equivalent to counting not from 1, but from 2. So the formula is not selected! Just remember to change the number to n-1

f(n,k) = [f(n-1,k) + k] % (n-1)

f(4,5) = 2

= [f(3,5)+5] % (4-1)

=(3+5)%3

=2

Here, a variable n is added to the traversal to mark the end of recursion.

#include<iostream>
using namespace std;
int recursion(int sum,int value,int n)
{
    if(n==1)return(sum+value-1)%sum;//Sign of the end of recursion: the last person left
    else return (recursion(sum-1,value,n-1)+value)%sum;
}
int main()
{
    int n,m;//n means how many people participate in the game, and m means how many people go out of the circle
    cin>>n>>m;
    cout<<recursion(n,m,n)+1;
}

The operation results are as follows:

Of course, in addition to the above methods, there is the method of circular linked list, which is more cumbersome. We'll update it later.

The main reason is that the code of the linked list is complex, smelly and long.

summary

In fact, there are not many topics for you to write Joseph's problem directly, but more topics that combine several methods with Joseph's problem. Therefore, I think that the placement of arrays and queues must be mastered. Recursion and formula methods will be difficult to understand and write because different topics have different problems. Linked lists. If the topic is not clearly specified, don't forget it!

The above is purely personal notes. If there is anything wrong, please correct it in time.

Topics: C++ Algorithm data structure queue