从零开始编写SAT求解器(三)

从零开始编写SAT求解器(三)

上一篇

核心算法:DPLL

//DPLL.cpp
#include "DPLL.h"

bool DPLL::check_sat() {
    // TODO: your code here, or in the header file
    std::unordered_map<int,int> atomStatus;//记录节点状态0,1,2
    int clause_num = phi.clauses.size();//子句数量
    int atomNum = phi.num_variable;//变量数量
    for(int i=1;i<=atomNum;i++)
        result.insert(std::make_pair(i,true));
    int* nodeStatus = new int[atomNum];
    for(int i=0;i<atomNum;i++)
        nodeStatus[i]=0;
    int ptr = 0;//指向当前节点
    while(true){
        if(nodeStatus[ptr]==2)
            break;
        else if(nodeStatus[ptr]==0) {
            nodeStatus[ptr]++;
            result[ptr + 1] = false;
        }
        else {
            nodeStatus[ptr]++;
            result[ptr + 1] = true;
        }
        int solveStatus = 2;//0 肯定不是解,1 肯定是解,2 不确定
        //检查是否是解
        bool wholeValue = true;//整个式子的真值
        for(int i=0;i<clause_num;i++) //每个子句
        {
            bool currValue=false;//这个子句是不是假的
            bool any_notsure=false;//有没有不确定的literature
            int len = phi.clauses[i].size();
            for(int j=0;j<len;j++)
            {
                int currvar = phi.clauses[i][j];
                if(VAR(currvar)<=ptr+1)
                {
                    if((POSITIVE(currvar)&&result[currvar])||(NEGATIVE(currvar)&&!result[VAR(currvar)])){//有一个为真,子句为真
                        currValue=true;
                        break;
                    }
                }
                else{
                    any_notsure=true;
                }
            }
            wholeValue=wholeValue&&currValue;
            if(currValue||any_notsure){
                continue;
            }
            else{
                solveStatus=0;
                break;
            }
        }
        if(wholeValue)
            solveStatus=1;
        //检查完毕
        if(solveStatus==0)//肯定不是解,回溯
        {
            while(ptr>0&&nodeStatus[ptr]==2)
                ptr--;
            for(int i=ptr+1;i<atomNum;i++)
                nodeStatus[i]=0;
        }
        else if(solveStatus==1)
            return true;
        else ptr++;
    }
    return false;
}

model DPLL::get_model() {
    // TODO: your code here, or in the header file
    return this->result;
}

其基本流程我举个例子:
假设五个变量A,B,C,D,E
我先假定A取真,其他的不确定,然后我检查输入的CNF是否为真
如果是真,那太好了,返回退出
如果不确定,那我再假定B取真,再检查
如果是假,那么回溯/取另外一个值
怎么回溯:
再举个例子:
假如ABCDE分别是1,1,1,1,null,这时发现CNF为假!
现在指针指向的是D,所以从D取另外的值,此时ABCDE分别为1,1,1,0,null
发现还是不行,CNF为假,这时就要回溯,回溯到哪?递归的搜索当前节点的父亲,直到找到这样一个节点,它还有没有取到的值(也就是说它没“脏”),或者到根节点(此时如果根节点为“脏”,证明所有情况搜索完毕,输入的CNF是不可满足的)。
回溯完成后,注意,在当前节点以下的所有节点,它们的状态都被重新标记为“干净”,也就是它们既没有取过真值,也没有取过假值,因为它们的父节点状态发生了变化,相当于它们即使与之前取同样的布尔值,ABCDE作为一个整体,这五个布尔值的组合也与之前不同。

其他文件

common.h:

#include <vector>
#include <unordered_map>
#include <string>
#include <sstream>

#ifndef DPLL_COMMON_H
#define DPLL_COMMON_H

// A literal is a atomic formula (that contains a variable). Like in dimacs,
// + positive numbers denote the corresponding variables;
// - negative numbers denote the negations of the corresponding variables.
// Variables are numbered from 1.
typedef int literal;
#define POSITIVE(x) ((x) > 0)
#define NEGATIVE(x) ((x) < 0)
#define VAR(x) (((x) > 0) ? (x) : (-(x)))

// A clause is a list of literals (meaning their disjunction).
typedef std::vector<literal> clause;

// A formula is a list of clauses (meaning their conjunction).
// We also specify the total number of variables, as some of them may not occur in any clause!
struct formula {
    int num_variable;
    std::vector<clause> clauses;
     formula(int n, const std::vector<clause>& clauses): num_variable(n), clauses(clauses) {}
};

// A satisfying model (interpretation).
// e.g. `model[i] = false` means variable `i` is assigned to false.
typedef std::unordered_map<int, bool> model;

#endif //DPLL_COMMON_H

DPLL.h:

#ifndef DPLL_DPLL_H
#define DPLL_DPLL_H

#include "common.h"

class DPLL {
public:
	DPLL(const formula &phi) : phi(phi) {}
	bool check_sat();
	model get_model();
private:
    formula phi;
    model result;
};
#endif //DPLL_DPLL_H

CMakeLists.txt:

# cmake_minimum_required(VERSION <specify CMake version here>)
cmake_minimum_required(VERSION 3.15)
project(dpll)

set(CMAKE_CXX_STANDARD 17)

set(CMAKE_BUILD_TYPE "Debug")
set(CMAKE_CXX_FLAGS_DEBUG "$ENV{CXXFLAGS} -O0 -Wall -g -ggdb")
set(CMAKE_CXX_FLAGS_RELEASE "$ENV{CXXFLAGS} -O2 -Wall")

add_executable(dpll main.cpp DimacsParser.h common.h DPLL.cpp DPLL.h common.h DPLL.h DimacsParser.h)

测试效果

输入:

c Generated with `cnfgen`
c (C) 2012-2016 Massimo Lauria <lauria.massimo@gmail.com>
c https://massimolauria.github.io/cnfgen
c
p cnf 12 32
1 -2 0
-1 2 0
1 3 4 0
1 -3 -4 0
-1 3 -4 0
-1 -3 4 0
3 -5 0
-3 5 0
2 6 -7 0
2 -6 7 0
-2 6 7 0
-2 -6 -7 0
4 6 8 -9 0
4 6 -8 9 0
4 -6 8 9 0
4 -6 -8 -9 0
-4 6 8 9 0
-4 6 -8 -9 0
-4 -6 8 -9 0
-4 -6 -8 9 0
5 8 -10 0
5 -8 10 0
-5 8 10 0
-5 -8 -10 0
7 11 0
-7 -11 0
9 11 -12 0
9 -11 12 0
-9 11 12 0
-9 -11 -12 0
10 -12 0
-10 12 0

输出:
在这里插入图片描述

发布了94 篇原创文章 · 获赞 69 · 访问量 11万+

猜你喜欢

转载自blog.csdn.net/swy_swy_swy/article/details/105019684