in reply to Challenge: Egg Timer Puzzles
More generally, if X1 .. XN are the different kinds of timers you have, the egg-timer numbers you can generate from these are exactly the linear combinations of X1 .. XN. From number theory, we know that this set of numbers is the same as the multiples of GCD(X1 ... XN).
So once we know how to put exactly GCD minutes on a timer, we can time any valid egg-timer number by first generating a bunch of these GCD timers, and then running them successively many times in a row (the target number is just a multiple of this GCD).
Now all that's left is to figure out how to get GCD minutes on a timer. Fortunately, number theory can help us again! The standard algorithm for computing the GCD can also be augmented to give integers b1 .. bN such that
GCD(X1 .. XN) = b1 * X1 + b2 * X2 + ... + bN * XN(called Bezout coefficients). Now we're getting closer!
How do we get from these coefficients back to the egg-timer problem at hand? Let's look at an example: For egg timers of 7 and 4 minutes, we have
GCD(4,7) = 1 = 2*4 - 1*7So to get 1 minute of time inside a timer, we do the following two steps in parallel:
More generally, we can split the X's up into those which have positive and negative Bezout coefficients. Start the appropriate number of timers for each group, one after the other (but the two groups in parallel). The negative-coefficient group will finish earlier, in fact, there will be exactly GCD minutes left on the last timer. Since the GCD is never bigger than any of the X's, these GCD minutes of time will always fit on the last timer, no matter which order we start them in. We set aside this last timer, and make as many GCD-timers as we want!
In code, it looks like this:
OK, so this finds terrible solutions for most numbers, if you'd actually have to do these things with the timers. But they are solutions nonetheless, and you didn't really say anything about optimality of solutions. Anyway, this method has its own intrinsic beauty from the application of number theory.my @TIMERS = qw[ 4 7 ]; my $TARGET = shift || 5; my ($gcd, @c) = bezout(@TIMERS); die "$TARGET is not egg-timer-able!" if $TARGET % $gcd; my $iter = $TARGET / $gcd; my @pos = grep { $c[$_] > 0 } 0 .. $#c; my @neg = grep { $c[$_] < 0 } 0 .. $#c; @c = map abs, @c; printf qq[ Perform steps 1 & 2 in parallel: Step 1: One after the other, start the following timers: %s Step 2: One after the other, start the following timers: %s Step 2 will finish exactly $gcd minutes before step 2, so when it finishes, set aside the remaining $gcd minutes on the last timer. ], join("\n", map " $c[$_] of the $TIMERS[$_]-minute timers", @pos), join("\n", map " $c[$_] of the $TIMERS[$_]-minute timers", @neg); print "\nRepeat the whole thing $iter times to $TARGET minutes set asi +de.\n" if $iter > 1; sub bezout { if ( @_ > 2 ) { my ($g1, @c1) = bezout( @_[0,1] ); my ($g2, @c2) = bezout( $g1, @_[2..$#_] ); return ( $g2, (map { $c2[0]*$_ } @c1), @c2[1 .. $#c2] ); } my ($x, $y) = @_; return ($y,0,1) if $x % $y == 0; my ($g, $s, $t) = bezout( $y, $x % $y ); return ($g, $t, $s - $t * int($x/$y)); }
Update: see Re^3: Challenge: Egg Timer Puzzles for how you can extend this technique and get rid of the infinite # of timers assumption.
blokhead
|
|---|
| Replies are listed 'Best First'. | |
|---|---|
|
Re^2: Challenge: Egg Timer Puzzles
by GrandFather (Saint) on May 03, 2006 at 01:20 UTC | |
by blokhead (Monsignor) on May 03, 2006 at 01:29 UTC | |
by Limbic~Region (Chancellor) on May 03, 2006 at 12:46 UTC | |
by GrandFather (Saint) on May 03, 2006 at 18:43 UTC | |
by tweetiepooh (Hermit) on May 03, 2006 at 13:49 UTC | |
|
Re^2: Challenge: Egg Timer Puzzles
by Limbic~Region (Chancellor) on May 03, 2006 at 12:33 UTC |