目 录
任 务 书 3
设计内容 3
设计要求 3
参考文献 4
1 引言 6
1.1 课题背景与意义 6
1.1.1 课程目标 6
1.1.2 课程任务 7
1.2 国内外研究现状 7
1.3 课程设计的主要研究工作 9
2 系统需求分析与总体设计 10
2.1 系统需求分析 10
2.2 系统总体设计 10
3 系统详细设计 12
3.1 有关数据结构的定义 12
3.2主要算法设计 13
3.2.1 CNF文件处理 13
3.2.2 DPLL算法处理 14
3.2.3 DPLL算法优化方案 17
3.2.4 二进制数独处理 17
4 系统实现与测试 20
5.总结与展望 41
5.1 全文总结 41
5.2工作展望 41
6. 体会 43
参考文献 44
附录 45
1.3 课程设计的主要研究工作
本设计要求精心设计问题中变元、文字、子句、公式等有效的物理存储结构,基于DPLL过程实现一个高效SAT求解器,对于给定的中小规模算例进行求解,输出求解结果,统计求解时间。在此基础上,通过改进算法、使用不同的选择策略和物理存储结构来优化求解过程,使求解器能更快地求解。完成基于此算法的数独游戏,通过将数独归约成SAT问题进行求解。
2 系统需求分析与总体设计
2.1 系统需求分析
(1)基于DPLL算法实现一个高效的SAT求解器,对于给定的中小规模测试算例进行求解,能输出求解结果和求解时间。
a)能够读取文件, 输出执行时间和执行结果
b)能够保存文件结果
c)能够对计算出的结果进行正确性验证
d)能够输出读取的文件
(2)数独游戏。将数独游戏转化为SAT问题进行求解,并具有一定的可玩性和交互性,要求如下:
a)能够生成数独格局
b)能将数独转化为SAT文件并导出CNF文件
c)能随机生成数独格局
d)能将求解结果化为数独的解
2.2 系统总体设计
本系统主要有3个模块,如下:
(1)SAT求解模块
a)读取CNF文件并将其转化为设计的数据结构
b)可以查看并输出读入的CNF文件及相关信息(子句数,变元数)
c)保存求解结果
d)根据DPLL算法输出求解结果
(2)输出模块
a)将SAT求解结果和求解时间输出到控制台(包括是否可解)
b)将最终结果以文件形式保存
(3)数独模块
a)随机生成数独布局
b)能够选择特定空位置输入游玩
c)将数独转化为SAT问题求解
d)使用Qt实现的GUI界面,可玩性较强
http://www.biyezuopin.vip/onews.asp?id=16994
流程图如下所示:

图2-2 系统模块流程图
#include "define.h"
int main(void)
{
for(i=0;i<2000;i++)
{
nums[i] = 0;
}
char filename[200], copy_filename[200]; //cnf文件读取 与结果保存
char sudo_filename[20]; //数独文件
int op = 1, SAT = 1, SuDoKu = 1; //用于switch分支
int flag_cnf = 0; //用于判断cnf文件是否读取成功
int flag_sudo = 0; //用于判断数独文件是否读取成功
int way_sudo = 0; //sudo处理方式
struct Clause * root;
while(op)
{
printf("|--------------------Choose SAT or BinaryPuzzle--------------------|\n");
printf("| |\n");
printf("| 1.SAT 2.BinaryPuzzle |\n");
printf("| |\n");
printf("| 0.Exit |\n");
printf("| |\n");
printf("| CopyRight @CS1906 LX |\n");
printf("| |\n");
printf("|------------------------------------------------------------------|\n");
printf("| |\n");
printf("| Make your choice ( 0 - 2 ) |\n");
printf("| |\n");
printf("|------------------------------------------------------------------|\n");
scanf("%d",&op);
switch(op)
{
case 1:
while(SAT){
printf("|-------------------------- Menu For SAT --------------------------|\n");
printf("| |\n");
printf("| 1.Readfile 2.Savefile |\n");
printf("| |\n");
printf("| 3.Traversefile 4.Dpll |\n");
printf("| |\n");
printf("| 0.Exit |\n");
printf("| |\n");
printf("| CopyRight @CS1906 LX |\n");
printf("| |\n");
printf("|------------------------------------------------------------------|\n");
printf("| |\n");
printf("| Make your choice ( 0 - 4 ) |\n");
printf("| |\n");
printf("|------------------------------------------------------------------|\n");
scanf("%d",&SAT);
switch(SAT) {
case 1:
for(i=0;i<2000;i++)
{
nums[i] = 0;
}
printf("请输入读取的cnf文件名:\n");
scanf("%s", filename);
strcpy(copy_filename, filename);
root = Readfile(filename);
if (root != NULL) {
printf("文件读取成功\n");
flag_cnf = 1;
} else {
printf("文件读取失败!\n");
}
getchar();
printf("请输入c继续:\n");
getchar();
break;
case 2: {
if (flag_cnf) {
if (Savefile(root)) {
printf("文件保存成功!\n");
} else {
printf("文件保存失败!\n");
}
} else printf("文件还未读取!\n");
getchar();
printf("请输入c继续:\n");
getchar();
break;
}
case 3:
{
if(flag_cnf) Traversefile(root);
else printf("文件还未读取!\n");
getchar();
printf("请输入c继续:\n");
getchar();
break;
}
case 4:
{
if(flag_cnf)
{
printf("请选取求解方式:\n");
printf("1.优化前 2.优化后\n");
scanf("%d",&Way);
clock_t start,end;
start = clock();
if(dpll(root)==OK)
{
end = clock();
printf("文件有解!\n");
printf(" \n");
printf("求解时间:%d ms\n",(end-start)*1000/CLOCKS_PER_SEC);
for(i=1;i<=word_number;i++)
{
temp = (variable[i] ==0 ?-1:1)*i;
printf("%d ",temp);
if(i%10==0) putchar('\n');
}
putchar('\n');
writeResult(copy_filename,1,(end-start)*1000/CLOCKS_PER_SEC);
}
else
{
end = clock();
printf("文件无解!\n");
printf(" \n");
printf("求解时间:%d ms\n",(end-start)*1000/CLOCKS_PER_SEC);
putchar('\n');
writeResult(copy_filename,0,(end-start)*1000/CLOCKS_PER_SEC);
}
flag_cnf = 0;
//destroyClauses(root);
}
else{
printf("文件还未读取!\n");
}
getchar();
printf("请输入c继续:\n");
getchar();
break;
}
case 0:{
break;
}
default:
{
printf("输入错误!\n");
getchar();
printf("请输入c继续:\n");
getchar();
break;
}
}
}
getchar();
printf("请输入c继续:\n");
getchar();
break;
case 2:
while(SuDoKu)
{
//system("cls");
printf("|--------------------- Menu For BinaryPuzzle --------------------|\n");
printf("| |\n");
printf("| 1.ReadSudoKuFile 2.Play |\n");
printf("| |\n");
printf("| 3.SetCNFfile 4.Dpll For Answer Check |\n");
printf("| |\n");
printf("| 5.Randomly Generate 0.Exit |\n");
printf("| |\n");
printf("| CopyRight @CS1906 LX |\n");
printf("| |\n");
printf("|------------------------------------------------------------------|\n");
printf("| |\n");
printf("| Make your choice ( 0 - 4 ) |\n");
printf("| |\n");
printf("|------------------------------------------------------------------|\n");
scanf("%d",&SuDoKu);
switch(SuDoKu)
{
case 1:
printf("请输入要读取的数独棋盘文件名:\n");
scanf("%s",sudo_filename);
Readqipan(sudo_filename);
Showqipan(qipan);
flag_sudo = 1;
getchar();
printf("请输入c继续:\n");
getchar();
break;
case 2:
if(flag_sudo) PlaySuDoKu(qipan);
else printf("数独文件还未读取!\n");
getchar();
printf("请输入c继续:\n");
getchar();
break;
case 3:
if(flag_sudo)
{
printf("请选择:\n1.直接求解 2.检验Play后的结果!\n");
scanf("%d",&way_sudo);
if(way_sudo==1)
{
Resetqipan(qipan);
SetCNFfile(qipan);
}
else
{
if(Checkqipan(qipan)) SetCNFfile(qipan);
else printf("棋盘还未填充完整,请填充完毕!\n");
}
}
else printf("数独文件还未读取!\n");
getchar();
printf("请输入c继续:\n");
getchar();
break;
case 4:
if(flag_sudo){
char filename_sudocopy[15];
strcpy(filename_sudocopy,filename_setcnf);
struct Clause * root_sudo = Readfile(filename_sudocopy);
clock_t t1,t2;
t1 = clock();
if(dpll(root_sudo) == OK){
t2 = clock();
char file_sudoresult[15];
strcpy(file_sudoresult,filename_sudocopy);
if(way_sudo == 1){
printf("Dpll求解成功!结果为:\n");
for(i = 0 ; i<36; i++)
{
printf("%d ", variable[i+1]);
if((i+1) % 6 == 0) putchar('\n');
}
}
else
{
printf("Dpll求解成功,结果正确!\n");
writeResult(file_sudoresult,1,t2-t1);
//destroyClauses(root_sudo);
}
}
else printf("Dpll求解失败,结果错误!\n可继续尝试!\n");
}
else printf("数独文件还未读取!\n");
getchar();
printf("请输入c继续:\n");
getchar();
break;
case 5: {
char *random_filename[]={"StanSudo_1.txt","StanSudo_2.txt","StanSudo_3.txt","StanSudo_4.txt","StanSudo_5.txt"};
srand((unsigned)time(NULL));
int z =rand()%5;
RandGener(random_filename[z]);
Showqipan(qipan);
flag_sudo = 1;
getchar();
printf("请输入c继续:\n");
getchar();
break;
}
case 0:
break;
default:
printf("输入错误!\n");
getchar();
printf("请输入c继续:\n");
getchar();
break;
}
}
case 0:
break;
default:
printf("输入错误!\n");
getchar();
printf("请输入c继续:\n");
getchar();
break;
}
}
printf("下次再见!\n");
return 0;
}



















