標籤:進階演算法 禁忌搜尋 c++ 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 : 定義控制台應用程式的進入點。//#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.
禁忌搜尋演算法解決3SAT問題(C++代碼實現)