
A CP Solution for XKCD NP Complete Restaurant Order - hartror
http://www.roryhart.net/code/xckd-np-complete-restaurant-order/
======
lake99
Of course, Prolog makes it easy too.

    
    
        ?- member(Fruit,[0,1,2,3,4,5,6,7]),
           member(Fries,[0,1,2,3,4,5,6,7]),
           member(Salad,[0,1,2,3,4,5]),
           member(Wings,[0,1,2,3,4]),
           member(Sticks,[0,1,2,3]),
           member(Sampler,[0,1,2]),
           1505 is Fruit*215 + Fries*275 + Salad*335 + Wings*355 + Sticks*420 + Sampler*580.
           Fruit = Sampler, Sampler = 1,
           Fries = Salad, Salad = Sticks, Sticks = 0,
           Wings = 2 ; % << END OF ANSWER #1
           Fruit = 7,
           Fries = Salad, Salad = Wings, Wings = Sticks, Sticks = Sampler, Sampler = 0 ; % << END OF ANSWER #2
           false.

~~~
maaaats
As a person not heavily invested in Prolog, can you explain how that works? Is
it just branching by _choice_ s from the different groups and it
backtracks/fails back up to the last choice if constraints are not met?

~~~
kaoD
Pretty much that, yeah.

[http://en.wikipedia.org/wiki/Unification_%28computer_science...](http://en.wikipedia.org/wiki/Unification_%28computer_science%29#Unification_in_logic_programming)

------
swannodette
In Clojure's core.logic it's also pretty succinct ;)

    
    
       (run* [mf ff ss hw ms sp]
         (fd/in mf ff ss hw ms sp (fd/interval 0 10))
         (fd/eq
           (= (+ (* mf 215) (* ff 275) (* ss 335)
                 (* hw 355) (* ms 420) (* sp 580))
              1505)))
    

Also the post seems incorrect there seem to be two extra results that do not
add up to $15.05?

~~~
amouat
I wondered how long it would be until a core.logic version appeared :)

I think the original post has one of the prices wrong.

BTW thanks for working on this stuff swannodette - I hope to look into
core.logic in the not too distant future...

EDIT: Yes, his price for salad is 3.25 instead of 3.35

------
simias
I must say I'm not familiar at all with constraint programming, but it looks
like something I've been wanting to know for a long time.

A practical problem I have right now is writing a device driver that needs to
configure Phase Locked Loops in order to generate certain frequencies. However
the setup is not straightforward, I have to configure a set of divisors,
multipliers and some muxes. To make matters worse, at each step I have
constraints for the possible frequency range at that point. And finally I
sometimes have several of those PLL in series.

The only solution I have right now is using a C program that bruteforces all
combinations, checks if the constraints are satisfied and outputs an array of
configs for the subset of frequencies I need. If I need a new frequency I have
to re-run my program and update the array. It works but is far from ideal.

Unfortunately all the tutorials I find online for this CP thing are using
prolog or some third party library in C++, which is of course not practical
for kernel programming...

If I could implement a clever solver that would run fast enough (unlike my
bruteforce solution) I could just do it dynamically in the driver and it'd be
much more elegant.

But I really don't know where to start. Any pointers/things to google for?

~~~
lifebeyondfife
Are you proposing to write a simple constraint solver in C? If it's specific
to your problem i.e. not too general, it's not the craziest idea I've ever
heard.

I don't mind fielding questions over the basic theory if you want to email me
(iain at my domain).

~~~
simias
That's very nice of you! I'll gather my thoughts and send you an email soon.
Thanks a lot.

------
graycat
His restaurant problem is a special case of the knapsack problem where we have
some items to carry, a value of each, and a weight of each, and a knapsack
with a maximum weight we can carry, and we want to know what items to put in
the knapsack to maximize the value but not exceed the maximum weight.

For the restaurant problem, for each item on the menu, just set both the value
and the weight to the price, and for the maximum weight of the knapsack just
use the budget.

Now, knapsack problems are in NP-complete, as I recall, but they are, in
practice, among the easiest to solve. The usual recommendation for solving a
large knapsack problem is via dynamic programming.

------
205guy
Something is missing from this discussion. The sample problem in the xkcd
comic is from a class of NP-complete problems, but being small, it has a
solution in "pseudo-polynomial time"[1] that can be solved by a variety of
tools, as demonstrated in these comments.

Even though this simple example can be solved, the general problem (from a set
of N integers, find all subsets that add up to a given integer x) is
nonetheless NP-complete. The method of computation (Excel, Prolog, constraint
programming, Turing machine) does not change this fact.

[1] <https://news.ycombinator.com/item?id=5780552>

------
mrgoldenbrown
Why 0..10? I think it would be instructional to mention how you chose these
domains. Is minizinc smart enough to only go as high as necessary? If you had
put 0..1000 would it have tested all 1000 _1000_ 1000 _1000_ 1000*1000
combinations, or is it smart enough to ignore fruit > 7 and salad > 2?

~~~
lifebeyondfife
There are three phases to Constraint Programming: (1) Model (2) Constraints
(3) Search & Propagation.

The first step in solving a problem with CP is to come up with a
representation for a solution. This involves deciding what your variables will
represent (in this case, "How many orders of each appetiser") and what is
their possible domain of values (in this case, "0 to 10" has been chosen,
quite arbitrarily).

The solver itself is generally dumb. It will propagate some constraints
sensibly but mostly it will just search over the entire space - equal to the
size of the domain to the power of the number of variables. 11^6 = 1.8
million, quite a small CP problem.

There's a bit of an art in creating a good model for a CSP (Constraint
Satisfaction Problem) to keep the search space as small as it needs to be.

EDIT: Just to be clear...

The way constraint solvers work is to make a decisions in a Depth First Search
manner and judge the validity of the new state after each choice. So after
fruit > 7 and salad > 2, bounds consistency on the constraint will rule out
the possibility of reaching a solution from that node in the search tree.

~~~
hartror
Yup pure laziness as it was a toy problem. Though in my experiments with a
gecode sudoku model its propagation was amazing, reducing the search space to
a handful of branches. Another toy problem of course but one of a
significantly larger size. Am I over estimating the power of propagation?

~~~
mzl
Propagation power really depends on the problem. For some problems,
propagation is amazingly good, and for some it does not do much at all.

One can characterize propagation algorithms for the amount of propagation they
do. A domain consistent propagation algorithm will make all deductions that
are possible to make in a certain state, removing all values for variables
that are not a part of any solution to that constraint. Loosely, a bounds
consistent propagation algorithm will do the same, but only for the bounds of
the variables.

The constraint used in Sudoku (called all_different or distinct) has really
good propagation algorithms (both bounds and domain consistent), and is one of
the cornerstones of constraint programming.

------
illicium
This is just a linear programming problem. One-liner solution in Mathematica,
just for kicks:

    
    
        In[1]:= Solve[
            Rationalize[2.15 a + 2.75 b + 3.35 c + 3.55 d + 4.20 e + 5.80 f == 15.05] && 
            a >= 0 && b >= 0 && c >= 0 && d >= 0 && e >= 0 && f >= 0,
            {a, b, c, d, e, f}, Integers
        ]
    
        Out[1]= {{a -> 1, b -> 0, c -> 0, d -> 2, e -> 0, f -> 1},
                 {a -> 7, b -> 0, c -> 0, d -> 0, e -> 0, f -> 0}}

~~~
davmre
Linear programming as it's usually defined allows for the solution to involve
arbitrary real numbers, and it's that flexibility that allows general LPs to
be solved in polynomial time. The restriction to integers turns this into an
integer programming problem, which is NP-Complete. Though that doesn't seem to
bother Mathematica (which presumably has a similar sort of constraint solver
working under the hood). :-)

------
stcredzero
_> So it’s not even that I figured out something clever. It’s just I have the
patience to do the thing that no one would think of, or that anyone would ever
bother to do. And sometimes that’s key. It’s just like I have that much free
time._

<http://www.maa.org/mathhorizons/MH-Sep2012_XKCD.html>

------
krenoten
Is the Z3 theorem prover an example of a CP tool? For those interested, Z3 is
one of the world's fastest theorem provers and has been open sourced by
Microsoft. Theorem provers have many interesting applications, but one of my
favorites is using them to represent a program's state and reasoning about the
exploitability of bugs. It has bindings to C, C++, .NET and Python. Check it
out if it interests you: <http://z3.codeplex.com/> or try it online at
<http://rise4fun.com/z3py>

~~~
nitrogen
Before you download Z3 and dive in, be aware that Z3 is _not_ Open Source.
It's licensed under a non-commercial license[0], which is not compatible with
the Open Source definition[1]. Muddying the term Open Source will diminish its
descriptive usefulness.

[0] <http://z3.codeplex.com/SourceControl/latest#LICENSE.txt>

[1] <http://opensource.org/osd#fields-of-endeavor>

------
mistermcgruff
Personally, I'd solve this using an integer programming approach, but CP is
really cool.

The IBM CPLEX CP optimizer is best for this kind of stuff IMHO.

~~~
hartror
We use CPLEX for LPs but I've not read much on the CPLEX CP solver. Maybe
because it isn't free so the academics stick with the OSS ones? How does it
compare to gecode?

~~~
mzl
One of the driving forces behind the development of Gecode was that academics
used to use the pre-cursor to IBM CP Optimizer, ILOG Solver, in research.

As for comparing Gecode with CP Optimizer, the largest difference IBM CP
Optimizer is a closed-source commercial system, with paid support. CP
Optimizer also has some (as far as I've seen) amazing automatic search
heuristics, automatic symmetry breaking, and more advanced scheduling
constraints. Gecode on the other hand is open for modifications, has nice
parallel search built in, and can be embedded easily. Given a problem where
both systems have the constraints, and the search heuristic is fixed, my guess
is that they would be mostly equivalent in speed

~~~
hartror
I have a cplex license on my work machine so that is another avenue to
explore!

~~~
mzl
Note that CPLEX and CP Optimizer are two completely different products. AFAIK,
a license key for one of them can not be used for the other. But I've never
actually used either myself, so don't take my word for it.

Also, since you seem to be interested in using Minizinc for modelling, you can
use other back-ends than the built in one for solving models.

For some more code examples, see also Håkan Kjellerstrands blog
(<http://www.hakank.org/constraint_programming_blog/>) which, among other
things, contains numerous solutions to the xkcd problem suing different
systems.

~~~
hartror
I have tested out using gecode as a minizinc backend as I like the look of
gint. I've been skimming hakank.org but it is a little advance for me at the
moment.

Is there a way to get a minizinc model into cp optimizer?

~~~
mzl
I have no idea if it is possible to use Minizinc models with CP Optimizer, but
I would guess not. It doesn't feel like a feature that would be of business
interest to IBM to develop.

As for Håkans pages, <http://www.hakank.org/common_cp_models/#xkcd> has a list
of 21 implementations of the xkcd problem.

------
andrewcooke
cp is fascinating. if people are interested, here's a write-up from a similar
exploration, using choco (a constraint solver in java) to solve a problem on
stackoverflow:

write-up - [http://isti.bitbucket.org/2012/04/07/pipes-clojure-
choco-5.h...](http://isti.bitbucket.org/2012/04/07/pipes-clojure-choco-5.html)

problem - [http://stackoverflow.com/questions/9689436/backtracking-
solu...](http://stackoverflow.com/questions/9689436/backtracking-solution-for-
programming-exercise-fitting-pipes/9692556#9692556)

and another problem on stackoverflow that i was going to solve with cp, but
which ended up being analytic -
[http://stackoverflow.com/questions/7076349/is-there-a-
good-w...](http://stackoverflow.com/questions/7076349/is-there-a-good-way-to-
do-this-type-of-mining/7237972#7237972)

~~~
nickknw
I agree, it is! I did a light comparison[0] between a logic programming
solution to sudoku (Prolog) and one using constraint programming (Scala +
JaCoP). I thought Prolog was a little nicer because it was shorter and more
declarative, but the JaCoP solution was still great compared to any non CP-
solution.

[0] - [http://nickknowlson.com/blog/2012/08/06/seven-languages-
week...](http://nickknowlson.com/blog/2012/08/06/seven-languages-
week-3-day-3/)

------
bjourne
Here is a brute force solution in factor:

    
    
        6 [ 10 iota >list ] replicate >list lcartesian-product* [ { 215 275 335 355 420 580 } [ * ] 2map sum 1505 = ] lfilter list>array .
    

Kind of cheating, but the solution space is so small that brute forcing it
becomes viable.

------
woqe
If anyone is interested in the problem in general, it is equivalent to finding
solutions to a Diophantine equation.
<http://en.wikipedia.org/wiki/Diophantine_equation>

~~~
gkatsi
It is not, because it is a single linear equality and all variables are >= 0.
This is not just nitpicking: solving Diophantine problems is in general
undecidable, but this problem can be solved in pseudo-polynomial time (linear
in the sum of all coefficients)

~~~
cantos
The study of solutions to equations only in the positive integers, or other
subsets of integers also falls under Diophantine equations, as far as I know.

Subset sum problems are obviously decidable since the number of values that
must be tried to find a solution is finite. However, subset sum is NP complete
even when restricted to positive integers [Garey and Johnson, p223] so there
is no polynomial time algorithm.

~~~
gkatsi
It is NP-complete in the weak sense: the reduction from, say, CNF-SAT,
introduces very large coefficients (in the order of 2^n). The pseudo-
polynomial algorithm has complexity linear in the magnitude of the
coefficients, not in the size of their binary representation (hence pseudo).
If the coefficients are small, that algorithm is efficient. See:
<http://en.wikipedia.org/wiki/Pseudo-polynomial_time>

------
mariorz
Some time ago I wrote a wrapper for PyGLPK and used the same problem as
example:

[https://github.com/mariorz/pytorovich/blob/master/examples/x...](https://github.com/mariorz/pytorovich/blob/master/examples/xkcd.py)

------
igul222
Does anyone know what algorithm this CP implementation is using behind the
scenes? I'd be a lot more impressed if it were using dynamic programming than
plain brute force.

~~~
hartror
Constraint propagation + depth first search.

------
rscale
Excel makes this easy as well:

<http://cl.ly/image/1X2S121l2C3O>

No nerd cred, but it crosses the finish line.

~~~
swannodette
Wow I did not know that Excel ships with a constraint solver and has since
1991 - <http://www.utexas.edu/courses/lasdon/design3.htm>

~~~
mistermcgruff
It's not a constraint programming solver, but rather a linear programming
solver. The algorithm the comment above used is "branch and bound" on top of
the Simplex method to handle the integer constraints. Tutorial here:
[http://analyticsmadeskeezy.com/2012/08/29/optimal-
blending-o...](http://analyticsmadeskeezy.com/2012/08/29/optimal-blending-of-
cocaine/) Wikipedia here: <http://en.wikipedia.org/wiki/Linear_programming>
<http://en.wikipedia.org/wiki/Simplex_algorithm>
<http://en.wikipedia.org/wiki/Branch_and_bound>

