Compressing boolean formulae

19

2

Syntax

~ not
/\ and
\/ or
t true
f false
P, Q, FISH, etc: variables

(Operators are given in order of precedence)

Introduction

Some boolean formulae can be changed to different forms to make them shorter. For example, the formula

~(~P /\ ~Q)

can be changed to the shorter form

P\/Q

while the formula

P \/ ~P

can be changed to the shorter form

t

Challenge

In this challenge, you are required to write a program that, given any boolean formula using only /\, \/, ~, t, f, parentheses, boolean variables (in uppercase), and whitespace, outputs a shortest form (since there may be more than one shortest form) in characters of that expression which is equivalent for all assignments of the variables. Shortest code (in any language) wins. I/O can be done in any reasonable manner.

Also, since answers are difficult to verify, it would be helpful (but isn't required) to include a brief explanation of how the code works.

Lily Chung

Posted 2014-03-10T01:49:20.837

Reputation: 358

In your "Challenge" section you do not mention any whitespace, but your examples have them. Should I handle them? – Victor Stafusa – 2014-03-10T01:53:51.927

@Victor Edited. – Lily Chung – 2014-03-10T01:56:08.993

What are you counting as the size of the expression: operators + variables + constants (i.e. ignoring parentheses), or characters (so that part of the solution is removing whitespace)? – Peter Taylor – 2014-03-10T08:23:42.717

@PeterTaylor Characters. – Lily Chung – 2014-03-10T15:24:29.350

Is it allowed to use third party lexer/parser library? – Florent – 2014-03-11T10:22:04.320

@Florent It is not. – Lily Chung – 2014-03-11T12:37:49.703

Do we have to handle (P) (which results in P)? – Florent – 2014-03-11T16:39:50.283

@Florent Yes, definitely. – Lily Chung – 2014-03-11T17:40:59.227

I'm not sure code-golf is the best tag for this challenge... – Florent – 2014-03-11T21:18:44.927

@Florent Why not? Shortest code wins. – Lily Chung – 2014-03-11T21:49:36.600

4I think Florent's point is that it is a difficult problem to solve in general, let alone to golf. Among other things, the parser will need to be able to determine if two arbitrary formulae have the same truth conditions. Reducing p ^ ~p is easy enough if p is atomic, but how about ((A^B)v(A^C)) ^ ~(A^(BvC)) ? I think it is a cool problem and I am curious to see some responses. But if you want short solutions, the problem could be made more conducive to golfing by A. using prefix notation and B. providing a list of required reductions. – dfernig – 2014-03-12T02:34:34.947

@dfernig I briefly considered both of your suggestions (and regrettably bumped this question by editing and then rolling-back, oops...) , but I believe that (A) doesn't actually change the difficulty of the problem much, while (B) makes it much less interesting. – Lily Chung – 2014-03-12T04:18:15.380

1I have a valid (golfed) solution in more than 5000 characters. This is ridiculous... It is composed of a tokenizer, an AST-parser, an AST-rewriter and an expression generator. – Florent – 2014-03-12T13:27:20.280

1

Mathematica can do it in one function call (BooleanMinimize)

– Florent – 2014-03-12T15:07:18.090

@Florent I believe that Mathematica's BooleanMinimize will not always output the shortest form in characters - correct me if I'm wrong. – Lily Chung – 2014-03-12T15:25:15.790

@IstvanChung Try it on Wolfram! It's unbeatable. – Florent – 2014-03-12T16:44:44.403

@IstvanChung You have chosen a non-minimizable expression. Look at the DNF notation of this one

– Florent – 2014-03-12T20:01:52.020

let us continue this discussion in chat

– Lily Chung – 2014-03-12T20:05:40.360

1For the record, I have a 498-character solution now, whose sha256sum is b9c98d088b78c30bb2108008a064a7b95722a4694d90ddad94a025c2eb4ed30a. I'll post the actual code at a later date, as I don't want to stifle creativity. – Lily Chung – 2014-03-13T02:40:19.500

Answers

2

Python 616

Not particularly efficient, but works in reasonable time for inputs whose results are around 5 or 6 characters. To check a string to see if it matches, it loops through every possible combination of truth/false values for all the variables and makes sure each one agrees. Using this it checks every possible string comprised of the relevant characters (not even necessarily a syntactically correct one).

It actually will print every equivalent expression (of every size) and does not actually ever terminate.

Code:

c=['t','f'];o=['1 ','0 ']
def e(s,v):
 for k in v:s=s.replace(k,v[k])
 return eval(s)
def z(t,p='~/\\() '):
 w=[]
 if p=='':return[t]*(t not in['']+c)
 for s in t.split(p[0]):w.extend(z(s,p[1:]))
 w.sort(key=lambda v:-len(v));return w
def m(v):
 l=list('~\\/()')+v
 for s in l:yield s
 for r in m(v):
    for s in l:yield s+r
def n(x):
 if x<1:yield []
 else:
    for l in n(x-1):
     for b in o:yield[b]+l
t=raw_input();v=z(t)+c;l=len(v)
for s in m(v):
 g=1
 for y in n(l):
    y[-2:]=o;d=dict(zip(v+['/\\','\\/','~'],y+['and ','or ','not ']))
    try:
     if e(s,d)!=e(t,d):g=0
    except:g=0
 if g:print s

Input/Ouput:

> ~(~P /\ ~Q)
Q\/P
P\/Q
...

> P /\ ~P
f
~t
...

> (P \/ Q) /\ P
P
(P)
...

KSab

Posted 2014-03-10T01:49:20.837

Reputation: 5 984

2

Oh right, I forgot to ever actually post my answer. It uses essentially the exact same approach which KSab's answer uses, but prints only the shortest valid expression.

Python3, 493 484

e=lambda x:eval(x.replace('\\/','+').replace('/\\','%'),None,w)
class V(int):
 def __add__(s,o):return V(s|o)
 def __mod__(s,o):return V(s*o)
 def __invert__(s):return V(1-s)
import re;from itertools import product as P;t=V(1);f=V(0);i=input();v=re.findall('[A-Z]+',i)
for k in range(1,len(i)):
 for m in P(i+'~/\\tf()',repeat=k):
  m=''.join(m)
  try:
   for d in P((V(0),V(1)),repeat=len(v)):
    w=dict(zip(v,d))
    if e(m)!=e(i):raise
  except:continue
  print(m);exit()
print(i)

Edit: fixed a bug where parentheses might not be generated, and golfed the computation of m a bit. Also replaced -s+1 with 1-s.

Lily Chung

Posted 2014-03-10T01:49:20.837

Reputation: 358