So, if one stays in the imperative world, why not just the generic problem
instead of the specific one ?

This clojure solution can solve the problem for any N (total weight) and
any M (any number of parts the stone can be spit into):

https://gist.github.com/1332020

The implementation is a bit longer than I expected, but I wanted to not
cheat by hard-wiring mathematical assumptions inside it (thus the
intermediate definition for a diff function working on unordered lists of
elements to be considered as "bags" - where the same element could appear
twice-)

2011/11/13 Jules <julesjac...@gmail.com>

> Here is a slight modification of the previous program that can be
> executed in two directions:
>
> N = [40]
> A = range(1,40)
> B = range(1,40)
> C = range(1,40)
> D = range(1,40)
>
> def valid(a,b,c,d,n):
>     weights = set(w*a+x*b+y*c+z*d for w in [-1,0,1]
>                                  for x in [-1,0,1]
>                                  for y in [-1,0,1]
>                                  for z in [-1,0,1])
>     return weights >= set(range(1,n+1))
>
> ws = [(a,b,c,d,n) for n in N
>                  for a in A
>                  for b in B if a <= b
>                  for c in C if b <= c
>                  for d in D if c <= d
>                  if a+b+c+d == n and valid(a,b,c,d,n)]
>
> Change the variable domains to execute it in the other direction:
>
> N = range(1,100)
> A = [1]
> B = [3]
> C = [9]
> D = [27]
>
> This produces the single answer (1,3,9,27,40), i.e. the only solution
> is with n = 40.
>
> What I mean by cheating is that you hard-code part of the solution.
> Would you consider the program `print "1,3,9,27"` a good solution? To
> me at least, it's non obvious that the solution would have to have a=1
> and would have to have a,b,c,d all different. Sure, for the way you
> interpreted the problem originally, a=1 is obvious. But since now you
> can subtract weights as well as adding them it is not obvious (to me).
> Why couldn't a=3 and b=4 work? Then you can still weigh 1lbs. If you
> consider it obvious, can you explain?
>
> > I'm curious if it can be written more concisely. Not sure. I note that
> most
> good solutions in Python and Haskell while fast and neat can only work
> in
> one direction.
>
> I think that you can use the same technique as the Python program.
> What are the weights we can measure with stones that weigh a,b,c,d?
> Well, for each stone we can either put it on the left scale (-a) or on
> the right scale (+a) or not on the scale at all (0). So the weights we
> can measure is the set {w*a + x*b + y*c + z*d | w,x,y,z are -1, 0 or
> 1}. So for example if w=-1, x=0, y=1, z=1 then stone A is on the left,
> stone B is not on the scale and stones C and D are on the right. The
> condition we need to satisfy is that this set contains all the weights
> {1..40}. With list/set comprehensions you can use this method
> directly, but with logic programming you need to do it slightly
> differently.
>
> You can define a relation (canmake a b c d x) which checks if a,b,c,d
> can make the weight x by creating four new finite domain variables
> w,x,y,z each with domain {-1,0,1}. Then you assert w*a+x*b+y*c+z*d ==
> x. So now you have a relation that succeeds iff weights a b c d can
> make x. Then you just have to assert every (canmake a b c d 1) ...
> (canmake a b c d 40). I don't know if cKanren has a library predicate
> for that, but something like (forall xs p) that asserts p on all
> elements of xs isn't hard to define.
>
> Whether the resulting program is more concise I don't know, but I
> think it would at least be easier to understand.
>
> Jules
>
> On Nov 13, 5:17 am, David Nolen <dnolen.li...@gmail.com> wrote:
> > checko and subchecko work together to determine whether an ordered set of
> > weights can produce integers 1 to 40 in reverse. It's more verbose since
> we
> > are preserving the ability to run our program backwards - which is
> simply a
> > byproduct of writing a purely relational program.
> >
> > I'm curious if it can be written more concisely. Not sure. I note that
> most
> > good solutions in Python and Haskell while fast and neat can only work in
> > one direction.
> >
> > I would love to see purely relational versions in Haskell, Python etc.
> even
> > if they involve bringing in a constraint solver (which alone isn't enough
> > as far as I know)
> >
> > On Saturday, November 12, 2011, Ambrose Bonnaire-Sergeant <
> abonnaireserge...@gmail.com> wrote:
> > > I'll just clarify, the "matches" function body jumps out at me.
> "checko"
> >
> > doesn't, but it's role in the problem is still clear, even if its
> > implementation is not. I think that's still an important trait.> Can
> checko be improved? I'm not sure. What does subchecko do, David?
> > > Ambrose
> >
> > > On Sun, Nov 13, 2011 at 11:37 AM, Ambrose Bonnaire-Sergeant <
> > abonnaireserge...@gmail.com> wrote:
> >
> > > "Understandability" is subjective. The meaning of the cKanren really
> >
> > jumps out at me, the python program takes a lot more for me to think
> > through.> There is nothing wrong with pruning the search space in this
> program. As
> >
> > you said, doing so reduced the Python program execution time to 2ms!
> >
> >
> >
> >
> >
> >
> >
> > > Ambrose
> >
> > > On Sat, Nov 12, 2011 at 11:30 PM, Jules <julesjac...@gmail.com> wrote:
> >
> > > The time difference is largely due to using the product library
> > > function instead of for comprehensions and the fact that the cKanren
> > > version cheats by hardcoding part of the solution, and hardcoding an
> > > extra constraint alldiff(a,b,c,d). The following code takes ~12ms with
> > > PyPy on my computer:
> >
> > > def valid(a,b,c,d):
> > >    weights = set(w*a+x*b+y*c+z*d for w in [-1,0,1]
> > >                                  for x in [-1,0,1]
> > >                                  for y in [-1,0,1]
> > >                                  for z in [-1,0,1])
> > >    return weights >= set(range(1,41))
> >
> > > ws = [(a,b,c,d) for a in range(1,40)
> > >                for b in range(1,40) if a <= b
> > >                for c in range(1,40) if b <= c
> > >                for d in range(1,40) if c <= d
> > >                if a+b+c+d == 40 and valid(a,b,c,d)]
> >
> > > If you cheat with `for a in [1]` instead of `for a in range(1,40)` and
> > > changing the <= to < (the same cheat as the alldiff), then the
> > > execution time drops to 2ms.
> >
> > > Since we don't seem to be going to agree on the definition of
> > > declarative, lets use another word: nice. Lets define niceness as
> > > understandability and closeness to mathematical specification. I agree
> > > that the matches definition is already nice: it handles the
> > > constraints and symmetry breaking nicely (better than the Python
> > > version if you ignore syntactic issues e.g. a+b+c+d=40 is more
> > > convoluted). But the checko and subchecko leave a lot to be desired.
> > > So my question is: can the cKanren version be improved so that it also
> > > becomes nice?
> >
> > > Jules
> >
> > > On 12 nov, 07:16, David Nolen <dnolen.li...@gmail.com> wrote:
> > >> Also note that even given all this generality over the Python code -
> the
> > >> earlier Python implementation takes ~300ms and this implementation
> takes
> >
> > >> >900ms on my machine.
> >
> > >> Quite a bit slower than ~12ms. Inferring 40 takes even less time of
> > course
> > >> - ~8ms.
> >
> > >> But really the execution time is just icing on the declarative cake ;)
> >
> > >> David
> >
> > >> On Fri, Nov 11, 2011 at 8:09 PM, Jules <julesjac...@gmail.com> wrote:
> > >> > Here is a new program. Perhaps you would consider this declarative:
> >
> > >> > def valid(a,b,c,d):
> > >> >    weights = set(w*a+x*b+y*c+z*d for (w,x,y,z) in
> > >> > product([-1,0,1],repeat=4))
> > >> >    return weights >= set(range(1,41))
> >
> > >> > ws = [(a,b,c,d) for (a,b,c,d) in product(range(1,41),repeat=4)
> > >> >                if a <= b <= c <= d and a+b+c+d == 40 and
> > >> > valid(a,b,c,d)]
> >
> > >> > On 12 nov, 01:48, Jules <julesjac...@gmail.com> wrote:
> > >> > > Are we reading the same cKanren code? I'll give you that the
> matches
> > >> > > definition is declarative, but then read checko and subchecko.
> They
> > >> > > are all about (recursive) control flow. Where does the
> specification
> > >> > > say anything remotely close to the checko and subchecko
> relations? In
> > >> > > contrast to this, the Python set comprehensions have minimal
> control
> > >> > > flow. Yeah, the standard Python implementation has a certain
> order of
> > >> > > executing the comprehensions, but so does the cKanren
> implementation
> > >> > > when executing the predicates. My Python program doesn't depend on
> >
> > > --
> > > You received this message because you are subscribed to the Google
> > > Groups "Clojure" group.
> > > To post to this group, send email to clojure@googlegroups.com
> > > Note that posts from new members are moderated - please be patient with
> > your first post.
> > > To unsubscribe from this group, send email to
> > > clojure+unsubscr...@googlegroups.com
> > > For more options, visit this group at
> > >http://groups.google.com/group/clojure?hl=en
>
> --
> You received this message because you are subscribed to the Google
> Groups "Clojure" group.
> To post to this group, send email to clojure@googlegroups.com
> Note that posts from new members are moderated - please be patient with
> your first post.
> To unsubscribe from this group, send email to
> clojure+unsubscr...@googlegroups.com
> For more options, visit this group at
> http://groups.google.com/group/clojure?hl=en
>

-- 
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clojure@googlegroups.com
Note that posts from new members are moderated - please be patient with your 
first post.
To unsubscribe from this group, send email to
clojure+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en

Reply via email to