禁忌搜索算法解决3SAT问题(C++代码实现)

最近梳理,翻出了当年高级算法课程做的题目,禁忌搜索。

吐槽:数学符号如何在编辑器里打出来啊,为了保留符号,我直接截图了。

1 SAT问题描述

定理4.4.1:

赋值v为使CNF可满足的充要条件是f(x1,x2,…,xm)达到最小值0。

2  禁忌搜索算法

禁忌搜索算法是在局部搜索的过程中引进了贪心选择机制,并利用禁忌表修改邻域,通过构造的候选邻域来控制解得选择和接受过程。在搜索的过程中,禁忌搜索算法从上一步计算解的候选邻域里选择一个最好的解,即使这个解比上一步得到的解还差,也接受它,同时修改禁忌表,以避免该解在禁忌期限内再次被选择。

思路分析如下:

1 初始赋值

随机初始化变元值

2 候选邻域的构造:

对于当前的赋值X,从每一个非零子句中选出一个变元,所有选出的变元构成一个子变元集SVS。从SVS里选择一个变元,改变它的值,其他的变元值保持不变,得到的解为X的一个邻解。所有邻解的集合,就构成了候选邻域。减少搜索空间,提高了搜索效率。

3 禁忌表:

禁忌表记录着在最近L次迭代内扰动过得变元,这些变元在当前迭代范围内禁忌扰动。

禁忌表用数组iteration_age[i],i=1,2,…m来表示,iteration_age[i]的值为变元xi被扰动时的迭代序数

变元xi是不是被禁忌:

iteration_age[i]+L>=iteration

禁忌搜索算法解决3SAT问题的伪代码:

算法伪代码:
  initcnf();initialiteration_age[]  //初始化CNF,禁忌表
  iteration    =  1;flips =     1     //迭代次数和扰动次数初始化
  while(v_cnf(variable)!=0&&iteration< itera_max) //停止准则
  SVS[]  //从每一个非零子句中选出一个变元
  flag    = 1;i  =     0
  while(i<|SVS|&&flag==1)do
       for j   i+1 to |SVS| do
      if((candidate(j)-v_cnf(variable))<(candidate(s)-v_cnf(variable)))    //从SVS选择f'最小的变元  选择策略
  then swap SVS[i] andSVS[j]
       if(iteration_age[SVS[i]]+L>=iteration)  //如果变元禁忌                               if(candidate(i)-v_cnf(variable)<0)  //吸引准则
  candidate(i) isflipped    //接受该变元的扰动
  modify iteration_age[]  //修改禁忌表
  flag=0  flips++
           else
  i++
       else
  candidate(i) isflipped    //接受该变元的扰动
  modify iteration_age[]  //修改禁忌表
  flag=0  flips++
  iteration++;

C++实现代码:

// TS3SAT.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include "stdafx.h"
#include <string>
#include <time.h>
#include <fstream>
#include <iostream>
#include <iterator>
using namespace std;

const int n=129; //子句个数
const int l=3;
const int m=30; //变元个数
const int L=20;		//禁忌表长度
const int N=1000;
int clause[n+5][l+5];       //下标数组
int sign[l*n+1];			//CNF变元符号
int variable[m+1];	//变元数组
//int neighbour[n];	//邻域
int SVS[N];			//子变元集
int vclause[n+5];		//子句的值
int itera_max = 500000;
int iteration_age[m];	//禁忌表
int t;
//int v;	//f(x)目标函数

void initcnf()		//CNF初始赋值
{
	printf("\n");
	ifstream in("1.txt");
   for(int i =0;i<n+5;i++)
   {
	   for(int j=0;j<=3;j++)
	   {
		   clause[i][j]=1;
	   }
   }
    for (int i = 1; i <= n; i++)
    {
        in >> clause[i][1] >> clause[i][2] >> clause[i][3] >> t;
    }
	//下标变元随机赋值
	/*for(int i=0;i<n;i++)
	{
		for(int j=0;j<l;j++)
		{
			clause[i][j]=rand()%m+1;	//1到m
		}
	}*/
	//各变元符号 0为反 1为正
	for(int i=1; i<=n; i++)
		for(int j=1; j <= l; j++)
	{
		//sign[i] == clause[i][j]/abs(clause[i][j]);
		if(clause[i][j]/abs(clause[i][j]) == 1)
			sign[i]=1;
		else
			sign[i]=0;
	}

	for(int i=1;i<=m;i++)
	{
		iteration_age[i]=0;
	}
	for(int i=0;i<=N;i++)
	{
		SVS[i]=0;
	}
}

int v_cnf(int var[])	//f(x)的值
{
	int v=0;
	for(int i=1;i<=n;i++)
	{
		vclause[i]=1;
	}
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=l;j++)
		{
			vclause[i] *= (sign[3*(i-1)+j]^var[abs(clause[i][j])]);	//各个子句的值
		}
		v+=vclause[i];
	}
	return v;
}

int candidate(int a)		//邻解
{	int var1[m+1];
     //memcpy(var1,variable,m+1);
	for (int t = 0; t < m+1; t++)
		var1[t] = variable[t];
	int v=0;
	//v=v_cnf();
	var1[SVS[a]]=1-var1[SVS[a]];
	v=v_cnf(var1);
	return v;
}
void tssat()	//禁忌搜索
{

	srand(double(time(NULL)));
	for(int i=1;i<=m;i++)	//变元赋值
	{
		variable[i]=rand()%2;	//0到1
	}
	printf("变元初始赋值为:");
	for(int i=1;i<=m;i++)
	{
		printf("%d ",variable[i]);
	}
	initcnf();
	int iteration=1;
	int flips=1;
	int c=v_cnf(variable);
	printf("初始f(X)=%d ",c);
	printf("\n");

	while(v_cnf(variable)!=0&&iteration < itera_max)
	{	int a=0;
		for(int i=0;i<n;i++)	//从每一个非零子句中选出一个变元
		{
			if(vclause[i]==1)
			{	int svs=abs(clause[i][rand()%l]);
				SVS[a]=svs;	//选出变元的下标
				int pos = 1;
				for(int i=0;i<a;i++)
			{
				if(SVS[a]==SVS[i])
				{
					pos = 0;
					break;
				}
			}
				if (pos == 1)
				{
				a++;
				}
			}

		int flag=1;
		int s=0;
		while(s<a&&flag==1)
		{
			for(int j=s+1;j<a;j++)
			{
				if((candidate(j)-v_cnf(variable))<(candidate(s)-v_cnf(variable)))	//选择f'最小的变元
				{
					/*int temp=candidate(i);
					candidate(i)=candidate(j);
					candidate(j)=temp;*/
					int temp=SVS[s];
					SVS[s]=SVS[j];
					SVS[j]=temp;
				}
			}
			if(iteration_age[SVS[s]]+L>=iteration)	//变元是否禁忌
			{
				if(candidate(s)-v_cnf(variable)<0)	//吸引准则
				{
					variable[SVS[s]]=1-variable[SVS[s]];
					iteration_age[SVS[s]]=iteration;
					flag=0;
					flips++;
				}
				else
				{

					//flag=0;
					s++;
				}
			}
			else
			{
				variable[SVS[s]]=1-variable[SVS[s]];
				iteration_age[SVS[s]]=iteration;
				flips++;
				flag=0;
			}
		}
		iteration++;
	}
	printf("扰动次数为:%d ",flips);
	printf("\n");
	printf("变元最终取值为:");
	for(int i=0;i<m;i++)
	{
		printf("%d ",variable[i]);
	}
	printf("\n");
	int v=v_cnf(variable);
	printf("最终f(X)=%d\n ",v);
	}
}

int _tmain(int argc, _TCHAR* argv[])
{	time_t start,end;
	start = clock();
	tssat();
	end = clock();

	printf("\n");
	printf("运行时间为:%f\n",double(end - start)/(CLOCKS_PER_SEC));
	system("pause");
	return 0;
	return 0;
}

参考文献

[1]  张德富.算法设计与分析(高级教程)[M].国防工业出版社,2007.

时间: 2024-11-07 03:09:21

禁忌搜索算法解决3SAT问题(C++代码实现)的相关文章

【高级算法】禁忌搜索算法解决3SAT问题(C++实现)

转载请注明出处:http://blog.csdn.net/zhoubin1992/article/details/46440389 近期梳理,翻出了当年高级算法课程做的题目.禁忌搜索算法解决3SAT问题. 吐槽:数学符号怎样在编辑器里打出来啊,为了保留符号,我直接截图了. 1 SAT问题描写叙述 定理4.4.1: 赋值v为使CNF可满足的充要条件是f(x1,x2,-,xm)达到最小值0. 2  禁忌搜索算法 禁忌搜索算法是在局部搜索的过程中引进了贪心选择机制.并利用禁忌表改动邻域,通过构造的候选

遗传算法解决3SAT问题(C++实现代码)

1 SAT问题描述 命题逻辑中合取范式 (CNF) 的可满足性问题 (SAT)是当代理论计算机科学的核心问题, 是一典型的NP 完全问题.在定义可满足性问题SAT之前,先引进一些逻辑符号. 一个 SAT 问题是指: 对于给定的 CNF 是否存在一组关于命题变元的真值指派使A为真. 显然,如A为真,则CNF的每个子句中必有一个命题变元为1(真). 2 遗传算法 遗传算法类似于自然进化,通过作用于染色体上的基因寻找好的染色体来求解问题.与自然界相似,遗传算法对求解问题的本身一无所知,它所需要的仅是对

【高级算法】遗传算法解决3SAT问题(C++实现)

转载请注明出处:http://blog.csdn.net/zhoubin1992/article/details/46910079 1 SAT问题描写叙述 命题逻辑中合取范式 (CNF) 的可满足性问题 (SAT)是当代理论计算机科学的核心问题, 是一典型的NP 全然问题.在定义可满足性问题SAT之前,先引进一些逻辑符号. 一个 SAT 问题是指: 对于给定的 CNF 是否存在一组关于命题变元的真值指派使A为真. 显然,如A为真,则CNF的每一个子句中必有一个命题变元为1(真). 2 遗传算法

Lasvegas+回溯算法解决3SAT问题(C++实现代码)

转载请注明出处:http://blog.csdn.net/zhoubin1992/article/details/46507919 1.SAT问题描述 命题逻辑中合取范式 (CNF) 的可满足性问题 (SAT)是当代理论计算机科学的核心问题, 是一典型的NP 完全问题.在定义可满足性问题SAT之前,先引进一些逻辑符号. 一个 SAT 问题是指: 对于给定的 CNF 是否存在一组关于命题变元的真值指派使得A 为真. 显然, 如果A 为真, 则 CNF 的每个子句中必有一个命题变元为 1 (真) .

【算法】禁忌搜索算法(Tabu Search,TS)超详细通俗解析附C++代码实例

01 什么是禁忌搜索算法? 1.1 先从爬山算法说起 爬山算法从当前的节点开始,和周围的邻居节点的值进行比较. 如果当前节点是最大的,那么返回当前节点,作为最大值 (既山峰最高点):反之就用最高的邻居节点来,替换当前节点,从而实现向山峰的高处攀爬的目的.如此循环直到达到最高点.因为不是全面搜索,所以结果可能不是最佳. 1.2 再到局部搜索算法 局部搜索算法是从爬山法改进而来的.局部搜索算法的基本思想:在搜索过程中,始终选择当前点的邻居中与离目标最近者的方向搜索.同样,局部搜索得到的解不一定是最优

现代优化算法 之 禁忌搜索算法

这次是首次接触这个算法,看了一些资料,总结一下. 禁忌搜索算法简介 禁忌搜索算法是组合优化算法的一种,是局部搜索算法的扩展.禁忌搜索算法是人工智能在组合优化算法中的一个成功应用.禁忌搜索算法的特点是采用了禁忌技术.所谓禁忌就是禁止重复前面的工作.禁忌搜索算法用一个禁忌表记录下已经到达过的局部最优点,在下一次搜索中,利用禁忌表中的信息不再或有选择地搜索这些点. 禁忌搜索算法实现的技术问题是算法的关键.禁忌搜索算法涉及侯选集合.禁忌对象.评价函数.特赦规则.记忆频率信息等概念. (1)邻域 在组合优

解决Eclipse中C++代码显示Symbol &amp;#39;std&amp;#39; could not be resolved的问题

第一次在Eclipse中写C++代码,写了一个简单的hello world程序,还没有等我编译.就报出了各种错误,但是这么简单的代码.怎么可能这么多错误.于是没有理会.编译执行后,能够正常输出!!!Hello World!!!,但是我的代码中还是有非常多红叉,把鼠标放在上面显示:Symbol 'std' could not be resolved 这种信息. 于是问题来了.怎样解决? 方法:写完库函数以后立刻保存.这样写之后的代码就不会报错了 比如:-->首先写#include <iostre

解决Eclipse编写Python代码出现SyntaxError: Non-ASCII character问题

在用Eclipse编写Python代码时,如果代码或注释中包含中文,运行程序会出现SyntaxError: Non-ASCII character问题. 解决方法:在代码的第一行添加 # coding=utf-8 即可解决问题.

解决Eclipse中C++代码显示Symbol &#39;std&#39; could not be resolved的问题

第一次在Eclipse中写C++代码,写了一个简单的hello world程序,还没有等我编译,就报出了各种错误,可是这么简单的代码,怎么可能这么多错误,于是没有理会,编译运行后,可以正常输出!!!Hello World!!!,但是我的代码中还是有很多红叉,把鼠标放在上面显示:Symbol 'std' could not be resolved 这样的信息. 于是问题来了,如何解决? 方法:写完库函数以后立马保存,这样写之后的代码就不会报错了 例如:-->首先写#include <iostre