You're forced to commit suicide, and you will use math to save yourself!
weird, huh? but this actually happened. Quoting from Wikipedia:
"The problem is named after Flavius Josephus, a Jewish historian living in the 1st century. According to Josephus' account of the siege of Yodfat, he and his 40 soldiers were trapped in a cave, the exit of which was blocked by Romans. They chose suicide over capture and decided that they would form a circle and start killing themselves using a step of three. Josephus states that by luck or maybe by the hand of God, he and another man remained the last and gave up to the Romans."
so how can we solve this problem?
We'll consider the case where we repeatedly execute the second person in the circle (k = 2) in a circle of size N. We want to find the position of the person who will be the last to execute himself, but of course he wouldn't, he would like to survive!
let's see the case for N = 1, Solution = 1 as this is the only person in the circle.
for N = 2, Solution = 1, as the second person will execute himself and "1" remains.
for N = 3, we have circle = (1, 2, 3). 2 will execute himself, then we're now standing at "3" as the first element and we have circle = (1, 3). the second person will execute himself "1", then the survivor is "3".
for N = 8, we have circle = (1, 2, 3, 4, 5, 6, 7, 8).
in the first run, "2", "4", "6" and "8" will all be executed and we'll end up with (1, 3, 5, 7).
we repeat again, "3" and "7" will be executed". we now have "1" and "3". "3" will be executed, now we have "1" as the survivor.
here is a list of the results for some N.
N = 1, S = 1
N= 2, S = 1
N = 3, S = 3
N = 4, S = 1
N = 5, S = 3
N = 6, S = 5
N = 7, S = 7
N = 8, S = 1
How can we solve this with code?
Trivial solution is to model the group as a circular linked list and repeatedly delete every second element until the size of the list is zero.
Code for this is something like
This is an O(N) time complexity solution and also O(N) space. Can we do better?
Yes!
With a few observations, if we have a circle of size (2 * N), meaning that it has an even number of people, like (1, 2, 3, 4, 5, 6). After the first run, "2", "4", "6" are eliminated and we have "1", 3", and "5". A circle of size "3" where each person id is multiplied by "2" then "1" is subtracted from it.
So, in order to find the solution of a circle with size N where N is even, we simply find the solution of circle (N/2) then multiply it by "2" and subtract "1".
in other means, S(N) = 2 * S(N-1) - 1, where N is even.
What if we have a circle of odd size?
(1, 2, 3, 4, 5, 6, 7) let's say it's of size (2 * N + 1).
After the first run, "2, 4, 6" are deleted which eliminates the "N" part, we then need to eliminate one more item to get to the smaller subproblem, so the next element to be eliminated is "1".
We end up with "3, 5, 7". Comparing it to "1, 2, 3". we see that each element is multiplied by "2", then added to "1"
so the total recurrence is:
S(N) = 2 * S(N/2) - 1, where N is even
S(N) = 2 * S(N/2) + 1, where N is odd
We can simply write a recursive solution for this with the base case of N(1) = 1, the complexity of this solution is O(lg N) as it divides the size of problem by two.
code for this can be something like
Can we do better than O(lg n)? Yes we can!
Let's see some other simple observations
N = 1, S = 1
N= 2, S = 1
N = 3, S = 3
N = 4, S = 1
N = 5, S = 3
N = 6, S = 5
N = 7, S = 7
N = 8, S = 1
N = 9, S = 3
N = 10, S = 5
N = 11, S = 7
N = 12, S = 9
N = 13, S = 11
N = 14, S = 13
N = 15, S = 15.
You can see that at every power of "2", solution resets to "1" then, "2" is added repeatedly till we reach the next power of 2. This can be proved by mathematical induction. So one way to solve this is the following:
if M is the biggest power of "2" that is less than or equal to N, let L = N - M;
Now multiply L by 2 as we're adding "2" at every step, then finally add "1". The result will be S = 2 * L + 1.
let's see some example
for N = 14, the last power of "2" is "8", set L = N - 8 = 6;
S = 2 * L + 1 = 13!
how can we find the last power of "2" greater than or equal to N? this is simply the leftmost set "1" bit in N if we represent it in binary.
Now, how can we write code for this in O(1)? it should be something like this:
This code will work if int is 32-bit size. It's called bit smearing and is used to find the left-most "1" bit, we're then going to reset it and that will simply subtract the power of two from the number.
we'll then shift it left by "1" to multiply it by two and add "1".
You can search or think of solutions for the general case when k != 2.
See you soon...
weird, huh? but this actually happened. Quoting from Wikipedia:
"The problem is named after Flavius Josephus, a Jewish historian living in the 1st century. According to Josephus' account of the siege of Yodfat, he and his 40 soldiers were trapped in a cave, the exit of which was blocked by Romans. They chose suicide over capture and decided that they would form a circle and start killing themselves using a step of three. Josephus states that by luck or maybe by the hand of God, he and another man remained the last and gave up to the Romans."
Problem Statement |
|
There are people standing in a circle waiting to be executed. The counting out begins at some point in the circle and proceeds around the circle in a fixed direction. In each step, a certain number of people are skipped and the next person is executed. The elimination proceeds around the circle (which is becoming smaller and smaller as the executed people are removed), until only the last person remains, who is given freedom. The task is to choose the place in the initial circle so that you are the last one remaining and so survive. |
We'll consider the case where we repeatedly execute the second person in the circle (k = 2) in a circle of size N. We want to find the position of the person who will be the last to execute himself, but of course he wouldn't, he would like to survive!
let's see the case for N = 1, Solution = 1 as this is the only person in the circle.
for N = 2, Solution = 1, as the second person will execute himself and "1" remains.
for N = 3, we have circle = (1, 2, 3). 2 will execute himself, then we're now standing at "3" as the first element and we have circle = (1, 3). the second person will execute himself "1", then the survivor is "3".
for N = 8, we have circle = (1, 2, 3, 4, 5, 6, 7, 8).
in the first run, "2", "4", "6" and "8" will all be executed and we'll end up with (1, 3, 5, 7).
we repeat again, "3" and "7" will be executed". we now have "1" and "3". "3" will be executed, now we have "1" as the survivor.
here is a list of the results for some N.
N = 1, S = 1
N= 2, S = 1
N = 3, S = 3
N = 4, S = 1
N = 5, S = 3
N = 6, S = 5
N = 7, S = 7
N = 8, S = 1
How can we solve this with code?
Trivial solution is to model the group as a circular linked list and repeatedly delete every second element until the size of the list is zero.
Code for this is something like
typedef struct Node { int value; Node *pNext; } Node; int josephus(int N) { Node *head, *curr; // Error case if(N <= 0) return -1; // Create a circular list of people with // 1 -> 2 -> 3 -> .... -> N -> 1 curr = head = new Node(); head->value = 1; for(int i = 2; i <= N; ++i) { curr->pNext = new Node(); curr = curr->pNext; curr->value = i; } curr->pNext = head; curr = head; // While not everyone is dead while(N > 1) { Node *tmp = curr->pNext; curr->pNext = tmp->pNext; delete tmp; curr = curr->pNext; --N; } return curr->value; }
This is an O(N) time complexity solution and also O(N) space. Can we do better?
Yes!
With a few observations, if we have a circle of size (2 * N), meaning that it has an even number of people, like (1, 2, 3, 4, 5, 6). After the first run, "2", "4", "6" are eliminated and we have "1", 3", and "5". A circle of size "3" where each person id is multiplied by "2" then "1" is subtracted from it.
So, in order to find the solution of a circle with size N where N is even, we simply find the solution of circle (N/2) then multiply it by "2" and subtract "1".
in other means, S(N) = 2 * S(N-1) - 1, where N is even.
What if we have a circle of odd size?
(1, 2, 3, 4, 5, 6, 7) let's say it's of size (2 * N + 1).
After the first run, "2, 4, 6" are deleted which eliminates the "N" part, we then need to eliminate one more item to get to the smaller subproblem, so the next element to be eliminated is "1".
We end up with "3, 5, 7". Comparing it to "1, 2, 3". we see that each element is multiplied by "2", then added to "1"
so the total recurrence is:
S(N) = 2 * S(N/2) - 1, where N is even
S(N) = 2 * S(N/2) + 1, where N is odd
We can simply write a recursive solution for this with the base case of N(1) = 1, the complexity of this solution is O(lg N) as it divides the size of problem by two.
code for this can be something like
int josephus(int N) { if(N <= 0) return -1; else if (N == 1) return 1; else { // testing the least significant bit to know // if this is an odd number if(N&1) return 2 * josephus(N/2) + 1; else return 2 * josephus(N/2) - 1; } }
Let's see some other simple observations
N = 1, S = 1
N= 2, S = 1
N = 3, S = 3
N = 4, S = 1
N = 5, S = 3
N = 6, S = 5
N = 7, S = 7
N = 8, S = 1
N = 9, S = 3
N = 10, S = 5
N = 11, S = 7
N = 12, S = 9
N = 13, S = 11
N = 14, S = 13
N = 15, S = 15.
You can see that at every power of "2", solution resets to "1" then, "2" is added repeatedly till we reach the next power of 2. This can be proved by mathematical induction. So one way to solve this is the following:
if M is the biggest power of "2" that is less than or equal to N, let L = N - M;
Now multiply L by 2 as we're adding "2" at every step, then finally add "1". The result will be S = 2 * L + 1.
let's see some example
for N = 14, the last power of "2" is "8", set L = N - 8 = 6;
S = 2 * L + 1 = 13!
how can we find the last power of "2" greater than or equal to N? this is simply the leftmost set "1" bit in N if we represent it in binary.
Now, how can we write code for this in O(1)? it should be something like this:
This code will work if int is 32-bit size. It's called bit smearing and is used to find the left-most "1" bit, we're then going to reset it and that will simply subtract the power of two from the number.
we'll then shift it left by "1" to multiply it by two and add "1".
int josephus(int n) { int m = n; if(n <= 0) return -1; m |= (m >> 16); m |= (m >> 8); m |= (m >> 4); m |= (m >> 2); m |= (m >> 1); m ^= (m >> 1); n &= ~m; return (n << 1)|1; }
You can search or think of solutions for the general case when k != 2.
See you soon...