Maximum Satisfiability Problem
Max 3 SAT:
Variant of boolean SAT. N boolean vars and m clauses like (x or y or z) and you choose truth assignments that maximizes the number of clauses you satisfy.
Approximation Algorithm
No worst than 7/8th of the optimum count.
This algorithm hinges on probability and the description is pretty long. Basically for every n variable you pretend it is true and calculate the average number of true clauses and then pretend it is false and calculate the average number of true clauses. Pick the one with the higher average - so it's a greedy algo.
# We will represent clause as a list of literals,
# A -ve sign in front of the literal means that the
# corresponding variable is negated.
# Eg., (x2 or not x4 or x5) --> [2, -4, 5]
# IMPORTANT: we assume clauses are not empty and a variable cannot appear twice in the same clause.
# given a list of clauses and a truth assignment as a list of true/false, return number of clauses satisfied
def num_clauses_satisfied(lst_of_clauses, truth_assign):
# truth_assign is simply a list of booleans.
# The truth assignment for variable # i is given by truth_assign[i-1]
n = len(truth_assign)
m = len(lst_of_clauses)
num_satisfied = 0
for clause in lst_of_clauses:
is_clause_satisfied = any([truth_assign[j-1] if j > 0 else not truth_assign[-j-1] for j in clause])
num_satisfied += 1 if is_clause_satisfied else 0
return num_satisfied
# Given a list of clauses, calculate the expectation
# Assume that the same variable does not repeat in a given clause.
# This calculation will be incorrect if this assumption does not hold
def expectation(lst_of_clauses):
expect = 0
for clause in lst_of_clauses: # go through the clauses
expect += 1.0 - 1.0/(2.0**len(clause)) # add to the expectation based on the probability clause is true.
return expect
# Assign and Simplify the clause by assigning variable var to boolean value val
def assign_and_simplify(lst_of_clauses, var, val):
new_list_of_clauses = []
num_satisfied = 0 # counter for number of clauses we satisfy due to this assignment
num_falsified = 0 # counter for number of clauses we falsify due to this assignment
for clause in lst_of_clauses: # go through each clause
var_in_clause = var in clause # is the variable `var` part of our clause
neg_var_in_clause = (-var) in clause #is the literate not (var) part of our clause
if (var_in_clause and val) or (neg_var_in_clause and not val):
# entire clause is true
num_satisfied += 1
elif (var_in_clause and not val) or (neg_var_in_clause and val):
# remove var from clause
new_clause = [j for j in clause if (j != var and j != -var)]
if len(new_clause) == 0: # did the clause become empty as a result?
num_falsified += 1
else:
new_list_of_clauses.append(new_clause) # append new clause
else:
new_list_of_clauses.append(clause)
assert len(lst_of_clauses) ==len(new_list_of_clauses) + num_satisfied + num_falsified
return (num_satisfied, num_falsified, new_list_of_clauses)
def approx_max_sat(n, lst_of_clauses):
# make sure that the clauses has literals -i or i for i in range 1 to n inclusive
assert all( all( 1 <= lj <= n or -n <= lj <= -1 for lj in clause) for clause in lst_of_clauses)
clauses = lst_of_clauses
assign = []
num_sat = 0
num_fals = 0
for i in range(1, n+1): # go through each variable
(n1, f1, phi1) = assign_and_simplify(clauses, i, True) # create formula by assigning xi -> True
(n2, f2, phi2) = assign_and_simplify(clauses, i, False)# xi -> False
e1 = expectation(phi1) + n1 # calculate expectations but also account for number of satisfied clauses
e2 = expectation(phi2) + n2
if (e1 >= e2):
assign.append(True) # assign xi to true
num_sat += n1
num_fals += f1
clauses = phi1 # replace the formula with phi1
else:
assign.append(False) # assign xi to false
num_sat += n2
num_fals += f2
clauses = phi2 # replace the forumula with phi2
return (assign, num_sat)