2-удовлетворительная (2-SAT) проблема
Проблема логической выполнимости
Boolean Satisfiability или просто SAT - это проблема определения, является ли логическая формула выполнимой или невыполнимой.
- Выполнимость: если булевым переменным можно присвоить такие значения, что формула оказывается ИСТИННОЙ, то мы говорим, что формула выполнима.
- Неудовлетворительно: если невозможно присвоить такие значения, мы говорим, что формула невыполнима.
Примеры:
, выполнимо, потому что A = TRUE и B = FALSE делает F = TRUE.
, является неудовлетворительным, потому что:
![]() | ![]() | ![]() |
|---|---|---|
| ПРАВДА | ЛОЖНЫЙ | ЛОЖНЫЙ |
| ЛОЖНЫЙ | ПРАВДА | ЛОЖНЫЙ |
Примечание: проблема булевой выполнимости является NP-полной (см. Теорему Кука).

Что такое 2-SAT Problem
2-SAT is a special case of Boolean Satisfiability Problem and can be solved
in polynomial time.
Чтобы лучше понять это, сначала давайте посмотрим, что такое конъюнктивная нормальная форма (CNF) или также известная как произведение сумм (POS).
CNF: CNF - это соединение (И) предложений, где каждое предложение является дизъюнкцией (ИЛИ).
Теперь 2-SAT ограничивает проблему SAT только теми логическими формулами, которые выражаются как CNF, где каждое предложение имеет только 2 члена (также называемое 2-CNF ).
Пример: 
Таким образом, Проблему 2-удовлетворенности можно сформулировать как:
Учитывая CNF с каждым предложением, имеющим только 2 члена, возможно ли присвоить такие значения переменным, чтобы CNF была ИСТИНА?
Примеры:
Вход :Вывод: данное выражение выполнимо. (если x1 = ЛОЖЬ, x2 = ИСТИНА) Вход :
Вывод: данное выражение невыполнимо. (для всех возможных комбинаций x1 и x2)
Подход к задаче 2-SAT
Чтобы значение CNF стало ИСТИНА, значение каждого предложения должно быть ИСТИНА. Пусть один из пунктов будет
.
*** QuickLaTeX не может составить формулу: *** Сообщение об ошибке: Ошибка: ничего не отображается, формула пуста
= ИСТИНА
- Если A = 0, B должно быть 1, т.е.

- Если B = 0, A должно быть 1, т.е.

Таким образом,
= ИСТИНА эквивалентно
Теперь мы можем выразить CNF как следствие. Итак, мы создаем граф импликации, который имеет 2 ребра для каждого предложения CNF.
выражается в графе импликации как 
Таким образом, для булевой формулы с предложениями 'm' мы создаем график импликации с:
- 2 ребра для каждого пункта, т.е. ребра «2 м».
- 1 узел для каждой логической переменной, входящей в логическую формулу.
Давайте посмотрим на один пример графа импликации.
*** QuickLaTeX cannot compile formula: *** Error message: Error: Nothing to show, formula is empty

Примечание: импликация (если A, то B) эквивалентна ее контрапозитиву (если
тогда
).
Теперь рассмотрим следующие случаи:
СЛУЧАЙ 1: Если[Tex] существует в графе [/ Tex] Это означает
Если X = ИСТИНА,
= ИСТИНА, противоречие. Но если X = FALSE, импликационных ограничений нет. Таким образом, X = FALSE
СЛУЧАЙ 2: Если[Tex] существует в графе [/ Tex] Это означает
Если
= ИСТИНА, X = ИСТИНА, противоречие. Но если
= FALSE, импликационных ограничений нет. Таким образом,
= ЛОЖЬ, т.е. X = ИСТИНА
СЛУЧАЙ 3: Если[Tex] оба существуют в графе [/ Tex] Одно ребро требует, чтобы X было ИСТИННО, а другое требует, чтобы X было ЛОЖЬ. Таким образом, в таком случае переуступка невозможна.
CONCLUSION: If any two variables
and
are on a cycle i.e.
both exists, then the CNF is unsatisfiable. Otherwise, there is a possible assignment and the CNF is satisfiable.
Note here that, we use path due to the following property of implication:
If we have 
Thus, if we have a path in the Implication Graph, that is pretty much same as having a direct edge.
CONCLUSION FROM IMPLEMENTATION POINT OF VIEW:
If both X and
lie in the same SCC (Strongly Connected Component), the CNF is unsatisfiable.
A Strongly Connected Component of a directed graph has nodes such that every node can be reach from every another node in that SCC.
Now, if X and
lie on the same SCC, we will definitely have
present and hence the conclusion.
Checking of the SCC can be done in O(E+V) using the Kosaraju’s Algorithm
C++
// C++ implementation to find if the given// expression is satisfiable using the// Kosaraju"s Algorithm#include <bits/stdc++.h>using namespace std;const int MAX = 100000;// data structures used to implement Kosaraju"s// Algorithm. Please refervector<int> adj[MAX];vector<int> adjInv[MAX];bool visited[MAX];bool visitedInv[MAX];stack<int> s;// this array will store the SCC that the// particular node belongs toint scc[MAX];// counter maintains the number of the SCCint counter = 1;// adds edges to form the original graphvoid addEdges(int a, int b){ adj[a].push_back(b);}// add edges to form the inverse graphvoid addEdgesInverse(int a, int b){ adjInv[b].push_back(a);}// for STEP 1 of Kosaraju"s Algorithmvoid dfsFirst(int u){ if(visited[u]) return; visited[u] = 1; for (int i=0;i<adj[u].size();i++) dfsFirst(adj[u][i]); s.push(u);}// for STEP 2 of Kosaraju"s Algorithmvoid dfsSecond(int u){ if(visitedInv[u]) return; visitedInv[u] = 1; for (int i=0;i<adjInv[u].size();i++) dfsSecond(adjInv[u][i]); scc[u] = counter;}// function to check 2-Satisfiabilityvoid is2Satisfiable(int n, int m, int a[], int b[]){ // adding edges to the graph for(int i=0;i<m;i++) { // variable x is mapped to x // variable -x is mapped to n+x = n-(-x) // for a[i] or b[i], addEdges -a[i] -> b[i] // AND -b[i] -> a[i] if (a[i]>0 && b[i]>0) { addEdges(a[i]+n, b[i]); addEdgesInverse(a[i]+n, b[i]); addEdges(b[i]+n, a[i]); addEdgesInverse(b[i]+n, a[i]); } else if (a[i]>0 && b[i]<0) { addEdges(a[i]+n, n-b[i]); addEdgesInverse(a[i]+n, n-b[i]); addEdges(-b[i], a[i]); addEdgesInverse(-b[i], a[i]); } else if (a[i]<0 && b[i]>0) { addEdges(-a[i], b[i]); addEdgesInverse(-a[i], b[i]); addEdges(b[i]+n, n-a[i]); addEdgesInverse(b[i]+n, n-a[i]); } else { addEdges(-a[i], n-b[i]); addEdgesInverse(-a[i], n-b[i]); addEdges(-b[i], n-a[i]); addEdgesInverse(-b[i], n-a[i]); } } // STEP 1 of Kosaraju"s Algorithm which // traverses the original graph for (int i=1;i<=2*n;i++) if (!visited[i]) dfsFirst(i); // STEP 2 pf Kosaraju"s Algorithm which // traverses the inverse graph. After this, // array scc[] stores the corresponding value while (!s.empty()) { int n = s.top(); s.pop(); if (!visitedInv[n]) { dfsSecond(n); counter++; } } for (int i=1;i<=n;i++) { // for any 2 vairable x and -x lie in // same SCC if(scc[i]==scc[i+n]) { cout << "The given expression " "is unsatisfiable." << endl; return; } } // no such variables x and -x exist which lie // in same SCC cout << "The given expression is satisfiable." << endl; return;}// Driver function to test above functionsint main(){ // n is the number of variables // 2n is the total number of nodes // m is the number of clauses int n = 5, m = 7; // each clause is of the form a or b // for m clauses, we have a[m], b[m] // representing a[i] or b[i] // Note: // 1 <= x <= N for an uncomplemented variable x // -N <= x <= -1 for a complemented variable x // -x is the complement of a variable x // The CNF being handled is: // "+" implies "OR" and "*" implies "AND" // (x1+x2)*(x2’+x3)*(x1’+x2’)*(x3+x4)*(x3’+x5)* // (x4’+x5’)*(x3’+x4) int a[] = {1, -2, -1, 3, -3, -4, -3}; int b[] = {2, 3, -2, 4, 5, -5, 4}; // We have considered the same example for which // Implication Graph was made is2Satisfiable(n, m, a, b); return 0;} |
Java
// Java implementation to find if the given// expression is satisfiable using the// Kosaraju"s Algorithmimport java.io.*;import java.util.*;class GFG{static final int MAX = 100000;// Data structures used to implement Kosaraju"s// Algorithm. Please refer@SuppressWarnings("unchecked")static List<List<Integer> > adj = new ArrayList();@SuppressWarnings("unchecked")static List<List<Integer> > adjInv = new ArrayList();static boolean[] visited = new boolean[MAX];static boolean[] visitedInv = new boolean[MAX];static Stack<Integer> s = new Stack<Integer>();// This array will store the SCC that the// particular node belongs tostatic int[] scc = new int[MAX];// counter maintains the number of the SCCstatic int counter = 1;// Adds edges to form the original graph voidstatic void addEdges(int a, int b){ adj.get(a).add(b);}// Add edges to form the inverse graphstatic void addEdgesInverse(int a, int b){ adjInv.get(b).add(a);}// For STEP 1 of Kosaraju"s Algorithmstatic void dfsFirst(int u){ if (visited[u]) return; visited[u] = true; for(int i = 0; i < adj.get(u).size(); i++) dfsFirst(adj.get(u).get(i)); s.push(u);}// For STEP 2 of Kosaraju"s Algorithmstatic void dfsSecond(int u){ if (visitedInv[u]) return; visitedInv[u] = true; for(int i = 0; i < adjInv.get(u).size(); i++) dfsSecond(adjInv.get(u).get(i)); scc[u] = counter;}// Function to check 2-Satisfiabilitystatic void is2Satisfiable(int n, int m, int a[], int b[]){ // Adding edges to the graph for(int i = 0; i < m; i++) { // variable x is mapped to x // variable -x is mapped to n+x = n-(-x) // for a[i] or b[i], addEdges -a[i] -> b[i] // AND -b[i] -> a[i] if (a[i] > 0 && b[i] > 0) { addEdges(a[i] + n, b[i]); addEdgesInverse(a[i] + n, b[i]); addEdges(b[i] + n, a[i]); addEdgesInverse(b[i] + n, a[i]); } else if (a[i] > 0 && b[i] < 0) { addEdges(a[i] + n, n - b[i]); addEdgesInverse(a[i] + n, n - b[i]); addEdges(-b[i], a[i]); addEdgesInverse(-b[i], a[i]); } else if (a[i] < 0 && b[i] > 0) { addEdges(-a[i], b[i]); addEdgesInverse(-a[i], b[i]); addEdges(b[i] + n, n - a[i]); addEdgesInverse(b[i] + n, n - a[i]); } else { addEdges(-a[i], n - b[i]); addEdgesInverse(-a[i], n - b[i]); addEdges(-b[i], n - a[i]); addEdgesInverse(-b[i], n - a[i]); } } // STEP 1 of Kosaraju"s Algorithm which // traverses the original graph for(int i = 1; i <= 2 * n; i++) if (!visited[i]) dfsFirst(i); // STEP 2 pf Kosaraju"s Algorithm which // traverses the inverse graph. After this, // array scc[] stores the corresponding value while (!s.isEmpty()) { int top = s.peek(); s.pop(); if (!visitedInv[top]) { dfsSecond(top); counter++; } } for(int i = 1; i <= n; i++) { // For any 2 vairable x and -x lie in // same SCC if (scc[i] == scc[i + n]) { System.out.println("The given expression" + "is unsatisfiable."); return; } } // No such variables x and -x exist which lie // in same SCC System.out.println("The given expression " + "is satisfiable.");}// Driver codepublic static void main(String[] args){ // n is the number of variables // 2n is the total number of nodes // m is the number of clauses int n = 5, m = 7; for(int i = 0; i < MAX; i++) { adj.add(new ArrayList<Integer>()); adjInv.add(new ArrayList<Integer>()); }
|



Вывод: данное выражение выполнимо.
(если x1 = ЛОЖЬ, x2 = ИСТИНА)
Вход :
Вывод: данное выражение невыполнимо.
(для всех возможных комбинаций x1 и x2)
= ИСТИНА эквивалентно 
[Tex] существует в графе [/ Tex]
Это означает
Если X = ИСТИНА,
= ИСТИНА, противоречие.
Но если X = FALSE, импликационных ограничений нет.
Таким образом, X = FALSE
[Tex] существует в графе [/ Tex]
Это означает
Если
[Tex] оба существуют в графе [/ Tex]
Одно ребро требует, чтобы X было ИСТИННО, а другое требует, чтобы X было ЛОЖЬ.
Таким образом, в таком случае переуступка невозможна.