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 refer vector< 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 to int scc[MAX]; // counter maintains the number of the SCC int counter = 1; // adds edges to form the original graph void addEdges( int a, int b) { adj[a].push_back(b); } // add edges to form the inverse graph void addEdgesInverse( int a, int b) { adjInv[b].push_back(a); } // for STEP 1 of Kosaraju"s Algorithm void 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 Algorithm void 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-Satisfiability 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.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 functions int 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 Algorithm import 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 to static int [] scc = new int [MAX]; // counter maintains the number of the SCC static int counter = 1 ; // Adds edges to form the original graph void static void addEdges( int a, int b) { adj.get(a).add(b); } // Add edges to form the inverse graph static void addEdgesInverse( int a, int b) { adjInv.get(b).add(a); } // For STEP 1 of Kosaraju"s Algorithm static 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 Algorithm static 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-Satisfiability static 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 code public 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>()); }
|