Beefy Boxes and Bandwidth Generously Provided by pair Networks
Think about Loose Coupling
 
PerlMonks  

Re: How to determine & record all possible variations (AI::Prolog)

by Ovid (Cardinal)
on May 19, 2005 at 04:31 UTC ( [id://458517]=note: print w/replies, xml ) Need Help??


in reply to How to determine & record all possible variations for 5 items?

Well, if it's a one-off script, perhaps this is the time to learn Prolog, a language which does this sort of thing naturally:

#!/usr/local/bin/perl use strict; use warnings; use AI::Prolog 0.65; my $by_two = AI::Prolog->make_list(map { $_ * 2 } 1 .. 50); my $by_five = AI::Prolog->make_list(map { $_ * 5 } 1 .. 20); my $prolog = AI::Prolog->new(<<"END_PROLOG"); member(X,[X|Tail]). member(X,[Head|Tail]) :- member(X, Tail). one_hundred(A,B,C,D,E) :- or(member(A, [$by_five]), member(A, [$by_two])), or(member(B, [$by_five]), member(B, [$by_two])), or(member(C, [$by_five]), member(C, [$by_two])), or(member(D, [$by_five]), member(D, [$by_two])), or(member(E, [$by_five]), member(E, [$by_two])), is(100, plus(A, plus(B, plus(C, plus(D, E))))). END_PROLOG $prolog->query('one_hundred(A,B,C,D,E).'); while (my $results = $prolog->results) { print join(',' =>@{$results}[1 .. 5]), $/; }

Cheers,
Ovid

New address of my CGI Course.

Replies are listed 'Best First'.
Re^2: How to determine & record all possible variations (AI::Prolog)
by jdporter (Paladin) on May 19, 2005 at 05:01 UTC
    Wow! That's supposed to illustrate what Prolog is natural for? All I can say is Ack!
    And what would it take to modify it to generate vectors of 6 numbers instead of 5? Is there a parameter you could simply set?

      To be fair, my example is a bit more verbose than regular Prolog due to some parser limitations that I am currently working through. For example, the member/2 predicate is usually built in and doesn't need to be specified and the last line normally reads like this:

      100 is A + B + C + D + E.

      There are a few other things that would also clarify this code, but again, the parser is not yet where it should be. However, if you look at the code, you'll notice that I never told it how to find the answer. I just defined logically what the answer was and Prolog figured out how to arrive at it. If you read the code, it actually sounds fairly natural (particularly if you read my example below). It's easy to understand. Your's, on the other hand, is much less clear for me to understand from simply reading it.

      I'm also willing to bet that someone with more Prolog-fu than myself would be able to come up with a more concise solution. I just typed in what occurred to me.

      Update: And I could just put all of the numbers in one list. That makes the code simpler and faster:

      #!/usr/local/bin/perl use strict; use warnings; use AI::Prolog 0.65; my @numbers = grep { ! ($_ % 2) || ! ($_ % 5) } 1 .. 100; my $numbers = AI::Prolog->make_list(@numbers); my $prolog = AI::Prolog->new(<<"END_PROLOG"); member(X,[X|Tail]). member(X,[Head|Tail]) :- member(X, Tail). one_hundred(A,B,C,D,E) :- member(A, [$numbers]), member(B, [$numbers]), member(C, [$numbers]), member(D, [$numbers]), member(E, [$numbers]), is(100, plus(A, plus(B, plus(C, plus(D, E))))). END_PROLOG $prolog->query('one_hundred(A,B,C,D,E).'); while (my $results = $prolog->results) { print join(',' =>@{$results}[1 .. 5]), $/; }

      It's also worth noting what my solution can do that your solution cannot do. Tomorrow, your boss comes to you and says "yeah, great, but we just found out that the first and fourth numbers are always 4 and 45, respectively." So I change my query:

      $prolog->query('one_hundred(4,B,C,45,E).'); while (my $results = $prolog->results) { print join(',' =>@{$results}[1 .. 5]), $/; }

      I still get the right answers, but I didn't have to change my code (note that this still requires that the numbers be multiples of 2 or 5, because of the definition. It's trivial to alter, though.)

      Cheers,
      Ovid

      New address of my CGI Course.

        could just put all of the numbers in one list. That makes the code simpler and faster
        and
        the first and fourth numbers are always 4 and 45, respectively
        Well that raises some interesting possibilities for even greater generality. To wit: allowing to specify the exact range of each column, individually. Here's my code, modified to do it. (I agree with you that it's rather obtuse. But it is concise. :-)
        my @all_nums = 0 .. 100; my @nums = grep { !($_ % 2) or !($_ % 5) } @all_nums; # and throw some special nums in column 3: gen( [ \@nums, \@nums, [4,5], \@nums, \@all_nums, ], 100, 0, sub { print "@_\n" } ); sub gen { my( $col_ranges, $max, $used, $found, @vec ) = @_; if ( @$col_ranges == 0 ) { $found->( @vec ) if $max == $used; return; } my @col_ranges = @$col_ranges; my $col_range = shift @col_ranges; gen( \@col_ranges, $max, $used+$_, $found, @vec, $_ ) for @$col_range; }

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: note [id://458517]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others goofing around in the Monastery: (3)
As of 2024-03-29 05:05 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found