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

转载请注明出处:http://blog.csdn.net/zhoubin1992/article/details/46440389

近期梳理,翻出了当年高级算法课程做的题目。禁忌搜索算法解决3SAT问题。

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

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 : 定义控制台应用程序的入口点。

//
/*********************************
-----------------------------------
禁忌搜索算法解决3SAT问题(C++代码实现)
-----------------------------------
Author:牧之丶  Date:2014年
Email:[email protected]
**********************************/
#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;
}

測试全部给出的例子,执行20次可得结果例如以下:


CNF(l=3)


平均时间


成功/失败次数


N        m


TS


TS


30       129


0.8200


20/0


40       172


0.9500


20/0


50       215


0.1500


20/0


100      430


0.2600


20/0

測试用例(1.txt):http://download.csdn.net/detail/zhoubin1992/8794893

參考文献

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

时间: 2024-10-18 13:39:49

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

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

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

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

最近梳理,翻出了当年高级算法课程做的题目,禁忌搜索. 吐槽:数学符号如何在编辑器里打出来啊,为了保留符号,我直接截图了. 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 遗传算法 遗传算法类似于自然进化,通过作用于染色体上的基因寻找好的染色体来求解问题.与自然界相似,遗传算法对求解问题的本身一无所知,它所需要的仅是对

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

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

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.1 说明 本篇为<挑战程序设计竞赛(第2版)>读书笔记系列,旨在: 梳理算法逻辑 探索优化思路 深入代码细节 1.2 目录 原文首发于个人博客Jennica.Space,按算法难度划分为初中高三个级别,详细目录及链接如下: 初级篇 穷竭搜索 贪心 动态规划 数据结构 图论 数论 中级篇 二分搜索 常用技巧 数据结构(二) 动态规划(二) 网络流 计算几何 高级篇 数论(二) 博弈论 图论(二) 常用技巧(二) 智慧搜索 分治 字符串 1.3 题解 配套习题及详解同步发

Freecodecamp 高级算法(个人向)

freecodecamp 高级算法地址戳这里. freecodecamp的初级和中级算法,基本给个思路就能完成,而高级算法稍微麻烦了一点,所以我会把自己的解答思路写清楚,如果有错误或者更好的解法,欢迎留言. Validate US Telephone Numbers 如果传入字符串是一个有效的美国电话号码,则返回 true. 简单来说,美国号码的规则就是,国家代码(必须为1),然后就是3,3,4的数字组合,前三个数字可以用括号包起来.另外就是间隔使用空格或者"-". 因为输入值肯定是字

js 高级算法 - 动态规划

主要是看了<数据结构与算法>有所感悟,虽然这本书被挺多人诟病的,说这有漏洞那有漏洞,但并不妨碍我们从中学习知识. 其实像在我们前端的开发中,用到的高级算法并不多,大部分情况if语句,for语句,swith语句等等,就可以解决了.稍微复杂的,可能会想到用递归去的解决. 但要注意的是递归写起来简洁,但实际上执行的效率并不高. 我们再看看动态规划的算法: 动态规划解决方案从底部开始解决问题, 将所有小问题解决掉, 然后合并成一个整体解决方案, 从而解决掉整个大问题 . 实例举例  (计算斐波那契数列