Detail the Joseph ring problem and its related C language algorithm implementation

  • 2020-04-02 03:14:26
  • OfStack

Joseph ring problem

N people in a circle number sequence, starting from 1 by 1, 2, 3...... Number in order, the person who reported p out of the circle, the rest of the people from 1, 2, 3, and then the person who reported p out of the circle, and so on.    
Please output the original number of each person in exit order  


Algorithm thought
Recursion by mathematical induction.

Both the linked list and the array implementations have one thing in common: to simulate the entire game process, the program is not only tedious to write, but the time complexity is as high as O(nm). If nm is too large, the result cannot be calculated in a short time. We note that the original problem was simply to figure out the number of the final winner, not to ask the reader to simulate the whole process. So if you want to be efficient, you have to break the rules and implement a little bit of math.

For the sake of discussion, let's change the question a little bit.
Problem description: n individuals (no. 0~(n-1)), count from 0, report (m-1) exit, the remaining people continue to count from 0. Find the number of the winner.

We know that after the first person (the number must be m%n-1) leaves the column, the remaining n-1 people form a new Joseph ring (starting with the number k=m%n) :
K + 1 k + 2 k... N minus 2, n minus 1, 0, 1, 2... K minus 2 and start at k with 0.
Now let's switch their Numbers:

K.         -- > 0
K + 1     -- > 1
K + 2     -- > 2
.
.
K - 2     -- > N - 2
K - 1     -- > N - 1
If we know the solution to this subproblem: for example, x is the winner, wouldn't changing this x back from the table above be exactly the solution for n people? !!!!! The formula for going back is very simple, which I believe you can deduce: x'=(x+k)%n

How do we know the solution to the problem of n minus 1 personal Numbers? Yeah, we just need to know the individual solution to n minus 2. What's the personal solution to n minus 2? Of course, the n minus 3 case -- this is obviously a backwardation problem! Ok, so here's the idea. Here's the formula for recursion:

Let f[I] represent the number of the final winner when I play the game and m exits. The final result is naturally f[n].

The recursive formula
F [1] = 0;
[I] = f (f [I - 1) + m) % I; (I > 1)

Implementation method


A circular linked list
Create a circular linked list with N elements, then iterate and count from the top of the list. If the cardinality is I == m, kick out the element and continue the loop until the current element exits the loop at the same time as the next element

       


#include <stdio.h> 
  #include <stdlib.h> 
  #include <string.h> 
    
  typedef struct lnode 
  { 
    int pos; 
    struct lnode *next; 
  } lnode; 
    
    
   
  void create_ring(lnode **root, int loc, int n) 
  { 
    lnode *pre, *current, *new; 
    current = *root; 
    pre = NULL; 
    
    while (current != NULL) { 
      pre = current; 
      current = current->next; 
    } 
    
    new = (lnode *)malloc(sizeof(lnode)); 
    new->pos = loc; 
    new->next = current; 
    
    if (pre == NULL) { 
      *root = new; 
    } else { 
      pre->next = new; 
    } 
    
    //Circular linked list
    if (loc == n) { 
      new->next = *root; 
    } 
  } 
    
   
  void kickoff_ring(lnode *head, int p) 
  { 
    int i; 
    lnode *pre, *pcur; 
    pre = pcur = head; 
    
    while (pcur->next != pcur) { 
      for (i = 1; i < p; i ++) { 
        pre = pcur; 
        pcur = pcur->next; 
      } 
    
      printf("%d ", pcur->pos); 
      pre->next = pcur->next; 
      free(pcur); 
      pcur = pre->next; 
    } 
    printf("%dn", pcur->pos); 
    free(pcur); 
  } 
    
    
  void print_ring(lnode *head) 
  { 
    lnode *cur;  
    cur = head; 
    
    while (cur->next != head) { 
      printf("%d ", cur->pos); 
      cur = cur->next; 
    } 
    printf("%dn", cur->pos); 
  } 
    
  int main() 
  { 
    int i, p, n; 
    lnode *head; 
    
    while (scanf("%d %d", &n, &p) != EOF) { 
      //Build the loop list
      for (i = 1, head = NULL; i <= n; i ++) 
        create_ring(&head, i, n); 
    
      //Joseph ring
      if (p != 1)  
        kickoff_ring(head, p); 
      else 
        print_ring(head); 
    } 
    
    return 0; 
  } 

      / * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
              Problem: 1188
              User: wangzhengyi
              Language: C
              Result: Accepted
              Time: 110 ms
              Memory: 912 KB
      * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /  

Two, array simulation
The idea is similar to a circular list, with less of the process of building a circular list

     


 #include <stdio.h> 
  #include <stdlib.h> 
   
  int main() 
  { 
    int i, index, p, n, remain, delete[3001], flag[3001] = {0}; 
   
    while (scanf("%d %d", &n, &p) != EOF) { 
      remain = n; 
      index = 0; 
      while (remain >= 1) { 
        for (i = 0; i < n; i ++) { 
          if (flag[i] == 0) { 
            //Count off
            index ++; 
            //P out of the loop
            if (index == p) { 
              //Withdraw from the outside
              flag[i] = 1; 
              //Count off again
              index = 0; 
              delete[remain - 1] = i + 1; 
              remain --; 
            }   
          }   
        } 
      } 
   
      //Output the serial number of each exit person
      for (i = n - 1; i >= 0; i --) { 
        if (i == 0) { 
          printf("%dn", delete[i]); 
        } else { 
          printf("%d ", delete[i]); 
        } 
      } 
    } 
   
    return 0; 
  } 

      3. Mathematical derivation
             


#include <stdio.h> 
   
  int main(void) 
  { 
    int i, n, m, last; 
   
    while (scanf("%d", &n) != EOF && n != 0) { 
      //Receive a count
      scanf("%d", &m); 
   
      //Joseph ring problem
      for (i = 2, last = 0; i <= n; i ++) { 
        last = (last + m) % i; 
      } 
      printf("%dn", last + 1); 
    } 
   
    return 0; 
  } 


Related articles: