in reply to Re: Challenge: N Jugs Problem
in thread Challenge: N Jugs Problem

It tracks water usage, if you want to optimize a solution that way, but that's commented out right now. I suspect that the shortest path solution is always the least water used solution.
I think you are wrong. I wrote my own solution (which I won't post, as several have now been posted) and found that for jugs with sizes 3 and 7, and target 11, there's a solution that requires 5 moves (fill Y, pour Y in X, pour Y in Z, fill Y, pour Y in Z), but that uses 14 water. There's a solution that requires just 11 water, but that requires 6 moves (fill Y, pour Y in X, pour Y in Z, pour X in Y, fill Y, pour Y in Z).

Of course, my program may be wrong, and I might have missed a 5 move solution that uses just 11 water.

Replies are listed 'Best First'.
Re^3: Challenge: N Jugs Problem
by ikegami (Patriarch) on Apr 14, 2009 at 22:47 UTC

    My solution agrees with your findings:

    $ perl min_steps.pl 3 7 11 5 steps: S->Y Y->Z Z->X S->Y Y->Z $ perl min_supply.pl 3 7 11 Required supply: 11 6 steps: S->Y Y->Z Z->X X->S S->Y Y->Z
Re^3: Challenge: N Jugs Problem
by BrowserUk (Patriarch) on Apr 14, 2009 at 22:12 UTC

      How many steps do you see?

      According to the OP's definition, there are 6 steps in your program.

      • S → Y (fill Y from supply)
      • Y → Z (pour Y into Z)
      • S → Y (fill Y from supply)
      • Y → Z (pour Y into Z)
      • Z → X (fill X from Z)
      • X → S (pour X into supply)

      The last step is useless, though.

      And how much water is consumed?

      Consumed is ambiguous. The OP asked to minimize how much water is needed. Your program requires 14 units of water (2*Y).

      That's 5 steps (fill Y, pour Y in Z are two separate steps), and in total 14 water is used (filling Y twice). Essentially, it's doing the same as the solution I provided: fill Y twice to collect 14, subtract the content of X making 11.
Re^3: Challenge: N Jugs Problem
by kyle (Abbot) on Apr 15, 2009 at 15:05 UTC

    I wrote my own solution (which I won't post, as several have now been posted) ...

    I'd be interested to see that.

    I did some more work on mine, and I got to where I thought it should find a minimum water solution, but it ran overnight without finishing.

      I initially had an infinite loop as well. It turned out that my program was investigating the following path:

      • X → S (pour X into supply)
      • S → X (fill X from supply)
      • X → S (pour X into supply)
      • S → X (fill X from supply)
      • X → S (pour X into supply)
      • S → X (fill X from supply)
      • ...

      The necessary step to proceed would have increased the necessary supply, but the program was busy investigating options that didn't increase the necessary supply. I ended up using a "seen" hash to avoid states I had already explored.

      Even using a brute force approach, the program should find the solution instantly (at least for the values previously discussed in this thread) unless there is no answer. My program checks for that condition as follows:

      $target % Math::Numbers->new($sz_X, $sz_Y)->gcd() == 0 or die("No solution\n");

        Yes, I have a "seen" hash already. A couple of them. Har. Anyway, I showed that mine works with an easier problem: target jug size 3, other jugs sized 4 and 1. The least water solution is six steps:

        000. [ 0/T 0/4 0/1 ] starting state 001. [ 0/T 0/4 1/1 ] fill jug 1 002. [ 1/T 0/4 0/1 ] pour jug 1 into target 003. [ 1/T 0/4 1/1 ] fill jug 1 004. [ 2/T 0/4 0/1 ] pour jug 1 into target 005. [ 2/T 0/4 1/1 ] fill jug 1 006. [ 3/T 0/4 0/1 ] pour jug 1 into target USED 3 units in 6 steps

        Fewest steps solution:

        000. [ 0/T 0/4 0/1 ] starting state 001. [ 0/T 4/4 0/1 ] fill jug 0 002. [ 0/T 3/4 1/1 ] pour jug 0 into jug 1 003. [ 3/T 0/4 1/1 ] pour jug 0 into target USED 4 units in 3 steps

        I've put my code in Re: Challenge: N Jugs Problem

      I'd be interested to see that.
      #!/usr/bin/perl use 5.010; use strict; use warnings; my $MINIMIZE_WATER = 1; my $X = 0; my $Y = 1; my $Z = 2; my $Water = 3; my $Move = 4; my $Previous = 5; my $MoveCount = 6; my @trans = ('S -> X', 'S -> Y', 'X -> Y', 'X -> Z', 'Y -> X', 'Y -> Z +', 'Z -> X', 'Z -> Y', 'X -> S', 'Y -> S'); my ($Target, @LIMITS) = @ARGV; $LIMITS[$Z] //= ~0; package State; sub new { my $class = shift; bless [@_], $class; } sub pour { my $self = shift; my ($from, $to) = @_; if ($self->[$from] + $self->[$to] <= $LIMITS[$to]) { $self->[$to] += $self->[$from]; $self->[$from] = 0; } else { $self->[$from] -= $LIMITS[$to] - $self->[$to]; $self->[$to] = $LIMITS[$to] } } sub fill { my $self = shift; my $to = shift; $self->[$Water] += $LIMITS[$to] - $self->[$to]; $self->[$to] = $LIMITS[$to]; } sub empty { my $self = shift; my $from = shift; $self->[$from] = 0; } sub trans { my $self = shift; my $trans = shift; my $New = State->new (@$self); $New->[$Move] = $trans; $New->[$Previous] = $self; $New->[$MoveCount] = $self->[$MoveCount] + 1; given ($trans) { when ('S -> X') {$New->fill($X)} when ('S -> Y') {$New->fill($Y)} when ('X -> Y') {$New->pour($X, $Y)} when ('X -> Z') {$New->pour($X, $Z)} when ('Y -> X') {$New->pour($Y, $X)} when ('Y -> Z') {$New->pour($Y, $Z)} when ('Z -> X') {$New->pour($Z, $X)} when ('Z -> Y') {$New->pour($Z, $Y)} when ('X -> S') {$New->empty($X)} when ('Y -> S') {$New->empty($Y)} default {die $trans} } $New; } sub id { my $self = shift; join ",", @$self[$X,$Y,$Z] } sub print { my $self = shift; return unless $self->[$Move]; $self->[$Previous]->print; printf "%2d. %s (X = %2d, Y = %2d, Z = %2d; Water = %2d)\n", @$self[$MoveCount, $Move, $X, $Y, $Z, $Water]; } package main; foreach my $MINIMIZE_WATER (0, 1) { my @QUEUE; my %SEEN; my $start = State->new(0, 0, 0, 0, undef, undef, 0); $SEEN{$start->id}++; push @QUEUE, $start; LOOP: while (1) { my $state = shift @QUEUE; foreach my $trans (@trans) { my $new = $state->trans($trans); next if $SEEN{$new->id}++; if ($new->[$Z] == $Target) { $new->print; last LOOP; } push @QUEUE, $new; if ($MINIMIZE_WATER) { @QUEUE = sort {$a->[$Water] <=> $b->[$Water] || $a->[$MoveCount] <=> $b->[$MoveCount]} +@QUEUE; } } } say "----"; } __END__