In Perl Appetizers, Randall Munroe writes that he missed a solution when double-checking xkcd #287 with some Perl that used floating point math. Naturally, since this is an NP-complete problem and you need backtracking to solve it in the general case (well, unless you know how to solve NP-complete problems in polynomial time…), I had to post how to do it with Perl regexes…
Unfortunately his comment section mangled the indentation of the generalised code, so I’m posting this here instead, so I have something linkable that’s more permanent than a pastebin.
#!/usr/bin/perl
use strict;
use warnings;
use re 'eval';
my %menu = (
'mixed fruit' => 215,
'french fries' => 275,
'side salad' => 335,
'hot wings' => 355,
'mozarella sticks' => 420,
'sampler plate' => 580,
);
my $total = 1505;
##########################################
my %order;
my @order_test = (
map qq{
(?:
(?: x{$menu{$_}} ) # consume the given number of units
# worked, so increase the number of orders for this thing;
# `local` makes this temporally scoped
(?{ local \$order{ "\Q$_\E" } = \$order{ "\Q$_\E" } + 1 })
)*
},
keys %menu,
);
my $menu_evaluator_regex = qr{
\A @order_test \z # find a match for these orders
(?{
my $order = join ', ', (
map "$order{$_} $_",
sort { $menu{$a} <=> $menu{$b} }
keys %order
);
print "$order\n";
})
(?!) # fail at last possible moment
# so engine will backtrack for more matches
}x;
my $total_units = "x" x $total;
$total_units =~ m/$menu_evaluator_regex/;
Re:Pseudo-Polynomial Time Solutions
Aristotle on 2007-10-30T23:08:39
Oh, I know that this particular problem can be done with no backtracking.
I mean, do you think I am seriously proposing regexes as the best approach to this problem?
:-) Re:Pseudo-Polynomial Time Solutions
Serpent on 2007-10-31T04:37:25
No - I was replying to your "Obviously, backtracking is needed" comment, that is all.
Using regular expressions for this is a nifty hack that I enjoyed reading about.
Re:Pseudo-Polynomial Time Solutions
Aristotle on 2007-11-01T16:19:36
Ah, oh. Yeah, the general case. Are you referring to things like Branch&Bound?
Re:Pseudo-Polynomial Time Solutions
Serpent on 2007-11-04T07:21:29
I'm referring to general dynamic programming.
Building a table can help solve this problem in O(M+N), where M is linear with the price goal (the higher the price, the longer this algorithm takes) and N is linear with the number of items you have to choose from.
If one builds a table where the rows represent a price (0 cents, 5 cents, 10 cents, etc, up to the goal price) and the columns represent the items, then moving from the top-left to the bottom-right, one can fill in the table based only on information above and to the left. When the table is filled in, the bottom-right-hand cell contains the information needed to print all solutions to the problem.
I have code, if you are interested.