Monday, 1 June 2009

Implement MIN in Forth without Conditional Code

While lurking in the #forth channel on irc.freenode.net I was pointed to an interesting problem from the Gforth tutorial. The problem is to implement MIN in Forth without any conditionally executed code.

DO, LOOP, BEGIN, UNTIL, REPEAT, WHILE, IF, ELSE and THEN are all forbidden.  MIN removes the top two values from the data stack then puts the lower of the two back on.

Can you implement MIN under these conditions? If so I challenge you implement it in as few Forth words as possible! Let me know if your implementation is under 10 words :-)

12 comments:

  1. : min 2dup - abs - + 2 / ;

    If a < b, |a-b| = b-a, so min = a = ((a+b)-(b-a))/2 = (a+b)-|a-b|)/2.

    If b < a, |a-b| = a-b, so min = b = ((a+b)-(a-b))/2 = (a+b)-|a-b|)/2.

    So ((a+b)-|a-b|)/2 is always the right answer. 7 words, 6 if you have a builtin 2/.

    ReplyDelete
  2. tardieu: that's a neat solution. My technique looks ugly by comparison.

    However, it's possible to implement MIN in less words :-)

    ReplyDelete
  3. : min 2dup > 1+ pick 2nip ;

    ReplyDelete
  4. llogiq: great solution, mine is similar:

    : min 2dup < 1+ roll nip ;

    ReplyDelete
  5. Great - in fact I like your solution more than my own. Btw. I did not "get" forth, until researching it a little bit for solving this small puzzle and finding Rich Jones' impressively documented implementation. Thank you.

    ReplyDelete
  6. Er...what Forth system are you using? Admittedly 2nip is not a standard word, but usually it is a double cell version of nip, not "nip nip", so 2dup > 1+ pick 2nip would not be a solution...

    ReplyDelete
  7. Anonymous: You're right. Thank you for finding my error.

    ReplyDelete
  8. This discussion was a great help working through the tutorial in the Gforth manual.

    However, I think there is an error in John's solution: the conditional is in the wrong direction.

    John wrote:

    : min 2dup < 1+ roll nip ;

    ... but I believe the correct solution is:

    : min 2dup > 1+ roll nip ;

    ReplyDelete
  9. Thanks! I was trying to do it without pick - just with comparisons and bitwise logic, which I don't think is possible.

    Impressive solutions!

    ReplyDelete
  10. With bitwise logic only:
    : min ( a b - x ) 2dup < tuck 0= and -rot and or ;

    ReplyDelete
    Replies
    1. As an explanation: ands true with the smaller one, ands false with the larger one (making it 0), then ors those two together, to get the smaller one.

      Delete
  11. One word shorter than my previous anonymous answers:
    : min 2dup > and tuck 0= and or ;

    With commented stack as we go, where x/y means x if > was true, y if not:

    : min ( a b -- x )
    2dup > ( a b t/f )
    and ( a b/0 )
    tuck 0= ( b/0 a f/t )
    and ( b/0 0/a )
    or ; ( b/a )

    ReplyDelete