Thus, Problem of 2-Satisfiability can be stated as: Given CNF with each clause having only 2 terms, is it possible to assign such values to the variables so that the CNF is TRUE?
For the CNF value to come TRUE, value of every clause should be TRUE.
Let one of the clause be ($A\lor B$) = True
if A=0, B must be 1, ($\bar{A} \Rightarrow B $)
if B=0, A must be 1, ($\bar{B} \Rightarrow A $)
Then we create an Implication Graph which has 2 edges for every clause of the CNF.
($A\lor B$) expressed in Graph as $edge(\bar{A} \Rightarrow B )$ , $edge(\bar{B} \Rightarrow A )$
So for a Boolean formula with m clauses, we have 2 edges for every clause in Graph. So 2m edges in total.
Case:
Case 1: If $edge(X\rightarrow \bar{X})$ exists in the graph
It means if X=True, $\bar{X}$ = True
But if X = False, can not decide $\bar{X}$
Case 2: If $edge(\bar{X}\rightarrow X)$ exists in the graph
It means if $\bar{X}$ =True, X = True
But if $\bar{X}$ = False, can not decide X
Case 3: If $edge(\bar{X}\rightarrow X) \space and \space edge(X\rightarrow \bar{X})$ exists in the graph at same time
One edge requires X to be TRUE and the other one requires X to be FALSE.
Thus, there is no possible assignment in such a case.
CONCLUSION: If any two variables $X$ and $\bar{X}$ are on a cycle i.e. $edge(\bar{X}\rightarrow X) \space and \space edge(X\rightarrow \bar{X})$ both exists, then the CNF is unsatisfiable. Otherwise, there is a possible assignment and the CNF is satisfiable.
CONCLUSION FROM IMPLEMENTATION POINT OF VIEW: If both $X$ and $\bar{X}$ lie in the same SCC (Strongly Connected Component), the CNF is unsatisfiable.
Strongly Connected Component:
A Strongly Connected Component of a directed graph has nodes such that every node can be reach from every another node in that SCC.
3 SCCs here
5. Code for solve 2-SAT problem implement by Python
test_data = [ ] withopen('2satBIG.cnf', 'r') as f: for line in f.readlines(): test_data.append(line.strip()) # 把末尾的'\n'删掉
A = [] B = [] n = 0 m = 0 for rec in test_data: rec=rec.strip() junk_list = [] substrs = rec.split() if substrs[0] == 'p'and substrs[1] == 'cnf': n=int(substrs[2]) m=int(substrs[3]) ## record has to start with a number or a minus sign if (rec[0].isdigit()) or (rec[0].startswith("-")): substrs = rec.split() try: iflen(substrs) == 3: for num inrange(len(substrs)): num_int = int(substrs[num]) if num == 0: A.append(num_int) elif num ==1: B.append(num_int) eliflen(substrs) == 2: for num inrange(len(substrs)): num_int = int(substrs[num]) if num == 0: A.append(num_int) B.append(num_int) except: print("error converting", rec)
MAX = 1000000
adj = [] adjInv = [] for i inrange(0, MAX + 1): adj.append([]) adjInv.append([]) visited = [False] * MAX visitedInv = [False] * MAX s = []
scc = [0] * MAX
count = 1
defadd_edge(a, b): adj[a].append(b)
defadd_edge_inv(a, b): adjInv[b].append(a)
defdfs_first(u): if visited[u]: return visited[u] = True for i inrange(0, len(adj[u])): dfs_first(adj[u][i]) s.append(u) # print("stack: " + str(s))
defdfs_second(u): if visitedInv[u]: return visitedInv[u] = True for i inrange(0, len(adjInv[u])): dfs_second(adjInv[u][i]) scc[u] = count
defis_2_sat(n, m, a, b): global count for i inrange(0, m): if a[i] > 0and b[i] > 0: add_edge(a[i] + n, b[i]) add_edge_inv(a[i] + n, b[i]) add_edge(b[i] + n, a[i]) add_edge_inv(b[i] + n, a[i])
elif a[i] > 0 > b[i]: add_edge(a[i] + n, n - b[i]) add_edge_inv(a[i] + n, n - b[i]) add_edge(-b[i], a[i]) add_edge_inv(-b[i], a[i])
elif a[i] < 0 < b[i]: add_edge(-a[i], b[i]) add_edge_inv(-a[i], b[i]) add_edge(b[i] + n, n - a[i]) add_edge_inv(b[i] + n, n - a[i])
else: add_edge(-a[i], n - b[i]) add_edge_inv(-a[i], n - b[i]) add_edge(-b[i], n - a[i]) add_edge_inv(-b[i], n - a[i])
for i inrange(1, 2 * n + 1): ifnot visited[i]: dfs_first(i)
whilelen(s) != 0: top = s.pop()
ifnot visitedInv[top]: dfs_second(top) count += 1
# print("scc: " + str(scc[0:20]))
for i inrange(1, n + 1): if scc[i] == scc[i + n]: print("The given expression is unsatisfiable") return
print("The given expression is satisfiable") for i inrange(1, n + 1): if scc[i] > scc[i + n]: pass # print("x" + str(i) + ": True") else: pass # print("x" + str(i) + ": False")
if __name__ == '__main__': start = time.time() # for i in range(100): is_2_sat(n, m, A, B) end = time.time() print(end-start)
test_data = [ ] withopen('2satBIG.cnf', 'r') as f: for line in f.readlines(): test_data.append(line.strip()) # 把末尾的'\n'删掉
A = [] B = [] n = 0 m = 0 for rec in test_data: rec=rec.strip() junk_list = [] substrs = rec.split() if substrs[0] == 'p'and substrs[1] == 'cnf': n=int(substrs[2]) m=int(substrs[3]) ## record has to start with a number or a minus sign if (rec[0].isdigit()) or (rec[0].startswith("-")): substrs = rec.split() try: iflen(substrs) == 3: for num inrange(len(substrs)): num_int = int(substrs[num]) if num == 0: A.append(num_int) elif num ==1: B.append(num_int) eliflen(substrs) == 2: for num inrange(len(substrs)): num_int = int(substrs[num]) if num == 0: A.append(num_int) B.append(num_int) except: print("error converting", rec)
adj = [] adjInv = [] for i inrange(0, MAX + 1): adj.append([]) adjInv.append([]) visited = [False] * MAX visitedInv = [False] * MAX s = []
scc = [0] * MAX
count = 1
defadd_edge(a, b): adj[a].append(b)
defadd_edge_inv(a, b): adjInv[b].append(a)
defdfs_first(u): if visited[u]: return visited[u] = True for i inrange(0, len(adj[u])): dfs_first(adj[u][i]) s.append(u)
defdfs_second(u): if visitedInv[u]: return visitedInv[u] = True for i inrange(0, len(adjInv[u])): dfs_second(adjInv[u][i]) scc[u] = count
# m number of clauses # n number of terms # A all terms in first position # B all terms in second position defis_2_sat(n, m, a, b): global count for i inrange(0, m): if a[i] > 0and b[i] > 0: add_edge(a[i] + n, b[i]) add_edge_inv(a[i] + n, b[i]) add_edge(b[i] + n, a[i]) add_edge_inv(b[i] + n, a[i])
elif a[i] > 0 > b[i]: add_edge(a[i] + n, n - b[i]) add_edge_inv(a[i] + n, n - b[i]) add_edge(-b[i], a[i]) add_edge_inv(-b[i], a[i])
elif a[i] < 0 < b[i]: add_edge(-a[i], b[i]) add_edge_inv(-a[i], b[i]) add_edge(b[i] + n, n - a[i]) add_edge_inv(b[i] + n, n - a[i])
else: add_edge(-a[i], n - b[i]) add_edge_inv(-a[i], n - b[i]) add_edge(-b[i], n - a[i]) add_edge_inv(-b[i], n - a[i])
for i inrange(1, 2 * n + 1): ifnot visited[i]: dfs_first(i)
for i inrange(0, m): if a[i] > 0and b[i] > 0: add_edge(a[i] + n, b[i]) add_edge_inv(a[i] + n, b[i]) add_edge(b[i] + n, a[i]) add_edge_inv(b[i] + n, a[i])
elif a[i] > 0 > b[i]: add_edge(a[i] + n, n - b[i]) add_edge_inv(a[i] + n, n - b[i]) add_edge(-b[i], a[i]) add_edge_inv(-b[i], a[i])
elif a[i] < 0 < b[i]: add_edge(-a[i], b[i]) add_edge_inv(-a[i], b[i]) add_edge(b[i] + n, n - a[i]) add_edge_inv(b[i] + n, n - a[i])
else: add_edge(-a[i], n - b[i]) add_edge_inv(-a[i], n - b[i]) add_edge(-b[i], n - a[i]) add_edge_inv(-b[i], n - a[i])
Step 4:
execute the DFS
changing the count number
1 2 3 4 5 6 7 8 9
for i inrange(1, 2 * n + 1): ifnot visited[i]: dfs_first(i)
Reprint policy:
All articles in this blog are used except for special statements
CC BY 4.0
reprint policy. If reproduced, please indicate source
Liang Junyi
!