|
| 1 | +//! # An Elephant Named Joseph |
| 2 | +//! |
| 3 | +//! The title is a reference to the [Josephus problem](https://en.wikipedia.org/wiki/Josephus_problem). |
| 4 | +//! We can solve both parts efficiently in `O(1)` constant storage without needing any |
| 5 | +//! auxiliary data strucutres. |
| 6 | +//! |
| 7 | +//! ## Part One |
| 8 | +//! |
| 9 | +//! Part one is exactly the Josephus problem with k = 2. Each round the number of elves is either |
| 10 | +//! odd or even. If the number of elves is even then every second elf is eliminated and the starting |
| 11 | +//! elf remains the same, for example starting with 8 elves and a step size of 1: |
| 12 | +//! |
| 13 | +//! 1 ~~2~~ 3 ~~4~~ 5 ~~6~~ 7 ~~8~~ |
| 14 | +//! |
| 15 | +//! The next round we double the step size used to find elves from 1 to 2: |
| 16 | +//! |
| 17 | +//! 1 ~~2~~ ~~3~~ ~~4~~ 5 ~~6~~ ~~7~~ ~~8~~ |
| 18 | +//! |
| 19 | +//! In the special case that the number of elves is power of two then the starting elf will always |
| 20 | +//! win. |
| 21 | +//! |
| 22 | +//! If the number of elves is odd then the last elf will also eliminate the starting elf, |
| 23 | +//! so the new starting elf increases by the step size. |
| 24 | +//! |
| 25 | +//! ~~1~~ ~~2~~ 3 ~~4~~ 5 |
| 26 | +//! |
| 27 | +//! We can represent this as a loop |
| 28 | +//! |
| 29 | +//! ```none |
| 30 | +//! let mut n = <starting number of elves> |
| 31 | +//! let mut step = 1; |
| 32 | +//! let mut winner = 1; |
| 33 | +//! while n > 1 { |
| 34 | +//! if n % 2 == 1 { |
| 35 | +//! winner += step * 2; |
| 36 | +//! } |
| 37 | +//! n /= 2; |
| 38 | +//! step *= 2; |
| 39 | +//! } |
| 40 | +//! ``` |
| 41 | +//! |
| 42 | +//! If we examine the loop we can see that the winner is simply the binary digits of `n` multiplied |
| 43 | +//! by two, excluding the highest bit, with one added. For example for 5 elves: |
| 44 | +//! |
| 45 | +//! ```none |
| 46 | +//! n = 5 = 101 |
| 47 | +//! n * 2 = 10 = 1010 |
| 48 | +//! n minus high bit = 010 |
| 49 | +//! n plus one = 011 = 3 |
| 50 | +//! ``` |
| 51 | +//! |
| 52 | +//! The [`ilog2`] function will return the number of zeroes before the highest one bit, for example |
| 53 | +//! `10.ilog2() = 3`. We can then shift a bit by that amount and subtract to get the result in |
| 54 | +//! constant `O(1)` time. |
| 55 | +//! |
| 56 | +//! |
| 57 | +//! ## Part Two |
| 58 | +//! |
| 59 | +//! Part two is a variant of the problem. We solve in `log(n)` time by working *backwards* |
| 60 | +//! from the winning elf until we reach the starting number of elves. |
| 61 | +//! Starting with the winning elf `a` it must have eliminated its neighbour to the right: |
| 62 | +//! |
| 63 | +//! `a` => `a b` |
| 64 | +//! |
| 65 | +//! We then choose the previous elf to the left wrapping around to elf `b` in this case. Elf `b` |
| 66 | +//! must have eliminated its neighbour 1 step to the right: |
| 67 | +//! |
| 68 | +//! `a b` => `a b c` |
| 69 | +//! |
| 70 | +//! Play passes to the left, in this case elf `a` must have eliminated an elf 2 steps away |
| 71 | +//! |
| 72 | +//! `a b c` => `a b d c` |
| 73 | +//! |
| 74 | +//! Play passes to the left, wrapping around to elf `c` that must have eliminated an elf 2 steps |
| 75 | +//! away |
| 76 | +//! |
| 77 | +//! `a b d c` => `a e b d c` |
| 78 | +//! |
| 79 | +//! Now that we have 5 elves our starting elf `a` is one step away from `c` so the answer is 2. |
| 80 | +//! |
| 81 | +//! [`ilog2`]: u32::ilog2 |
| 82 | +use crate::util::parse::*; |
| 83 | + |
| 84 | +pub fn parse(input: &str) -> u32 { |
| 85 | + input.unsigned() |
| 86 | +} |
| 87 | + |
| 88 | +pub fn part1(input: &u32) -> u32 { |
| 89 | + let mut elf = *input; |
| 90 | + |
| 91 | + elf *= 2; |
| 92 | + // Remove highest 1 bit |
| 93 | + elf -= 1 << elf.ilog2(); |
| 94 | + // Elves use 1-based indexing |
| 95 | + elf += 1; |
| 96 | + |
| 97 | + elf |
| 98 | +} |
| 99 | + |
| 100 | +pub fn part2(input: &u32) -> u32 { |
| 101 | + let target = *input; |
| 102 | + let mut elf = 0; |
| 103 | + let mut size = 1; |
| 104 | + |
| 105 | + while size < target { |
| 106 | + let remaining = target - size; |
| 107 | + |
| 108 | + // The trick to log(n) time is that we can handle all elves greater than or less than |
| 109 | + // the starting point in one pass, greatly increasing efficiency. |
| 110 | + if elf > size / 2 { |
| 111 | + // If the elf is greater than the half way point, then an elf will be inserted before |
| 112 | + // it. This cancels out moving to the previous elf so our position remains the same. |
| 113 | + let possible = 2 * elf - size; |
| 114 | + size += possible.min(remaining); |
| 115 | + } else { |
| 116 | + // The next elf will be the one before us. If we are at the start then wrap around |
| 117 | + // to the last elf. |
| 118 | + if elf >= remaining { |
| 119 | + elf -= remaining; |
| 120 | + size += remaining; |
| 121 | + } else { |
| 122 | + elf += size; |
| 123 | + size = elf + 1; |
| 124 | + } |
| 125 | + } |
| 126 | + } |
| 127 | + |
| 128 | + // The winning position is at 0, so its number is the distance from the starting elf. |
| 129 | + target - elf |
| 130 | +} |
0 commit comments