最大团问题的各种算法和源代码
- 格式:doc
- 大小:985.50 KB
- 文档页数:20
c++最大团问题分支限界法
C++最大团问题分支限界法
最大团问题是一类典型的计算机视觉算法,是一种经典的NP难问题。
它的出处可以追溯到1960年代的军事合作活动,它是一个试图确定由一系列正交关系中的最大团的问题。
为了解决该问题,具有一组称为“分支限界法”(Branch and Bound)的方法已经开发出来,它是一种基于回溯的搜索算法。
分支限界法主要是通过组合所有可能的顶点组合来求解最大团问题。
该算法从一个指定顶点顺序表开始,依次检查每个顶点,组合每个子团,直到它找到最大的团为止。
算法步骤如下:
(1)将所有顶点按照一定的顺序排序,然后将它们放入一个队列中。
(2)移除队列中的第一个顶点并将其加入当前团中。
(3)继续循环,检查剩余顶点是否需要加入当前团,如果确定它们满足条件,则将它们加入当前团。
(4)检查团大小是否达到最大值。
(5)如果当前团的大小小于最大值,则回到步骤2,继续检查剩余顶点是否可以加入团中。
(6)重复步骤2到5,直到找到最大团为止。
该算法通过在搜索空间中枚举所有可能的节点组合,来求解最大团问题。
尽管该算法可以有效地解决最大团问题,但它也存在一些弊端,例如,该算法的时间复杂度非常高(O(n^2)),因为它需要搜索所有可能的节点组合。
这就是该算法的局限性。
用迭代法求集合中最大、最小元问题迭代法是一种通过不断重复迭代过程来逐步逼近问题解的方法。
在求解集合中的最大、最小元问题时,可以使用迭代法来逐步查找并比较集合中的元素,以找到最大、最小元。
首先,我们需要定义一个集合,可以是有序集合(如数组)或无序集合(如链表)。
然后我们使用迭代的方式来遍历集合中的每个元素,逐一比较它们,并更新最大、最小元。
以下是使用迭代法求解集合中最大、最小元的示例代码:```// 求解集合中的最大、最小元int findMax(const vector<int>& nums) {// 假设集合不为空int maxNum = nums[0];for (int i = 1; i < nums.size(); i++) {if (nums[i] > maxNum) {maxNum = nums[i];}}return maxNum;}int findMin(const vector<int>& nums) {// 假设集合不为空int minNum = nums[0];for (int i = 1; i < nums.size(); i++) {if (nums[i] < minNum) {minNum = nums[i];}}return minNum;}```上述代码中,我们分别定义了两个函数`findMax`和`findMin`来求解集合中的最大、最小元。
这两个函数均接受一个参数`nums`,表示输入的集合。
假设集合不为空,我们初始化最大、最小元为集合中的第一个元素,然后通过遍历集合中的其余元素,逐一与最大、最小元进行比较,找到实际的最大、最小元。
在上述代码中,我们使用了一个循环来遍历集合中的元素。
该循环从索引1开始,因为我们已经将最大、最小元初始化为集合中的第一个元素。
在循环中,我们使用一个条件判断语句来比较当前元素与最大、最小元,如果当前元素大于最大元,我们更新最大元为当前元素;如果当前元素小于最小元,我们更新最小元为当前元素。
计算机算法设计与分析最大团问题研究报告目录1. MCP问题描述 (1)1.1 MCP问题基本概念 (1)1.2 MCP问题数学描述 (1)2. MCP问题应用背景 (2)3. 求解MCP问题的常用算法 (2)3.1 顺序贪婪启发式算法 (2)3.2 局部搜索启发式算法 (2)3.3 智能搜索启发式算法 (3)3.3.1 遗传算法 (3)3.3.2 模拟退火算法 (3)3.3.3 禁忌算法 (4)3.3.4 神经网络算法 (4)3.4 改进蚁群算法-AntMCP (4)3.5 其它启发式算法 (5)3.6 回溯法 (6)3.6.1 算法基本思想 (6)3.6.2 算法设计思想 (6)3.6.3 实例分析 (7)3.6.4 程序设计及测试 (8)3.7 分支限界法 (11)3.7.1 算法描述 (11)3.7.2 算法求解流程 (12)3.7.3 优先队列式分支限界法求解MCP问题 (12)3.7.4 实例分析 (13)3.7.5 程序设计及测试 (13)4. 回溯法与分支限界法比较 (18)最大团问题及其求解算法研究最大团问题(Maximum Clique Problem, MCP )是图论中一个经典的组合优化问题,也是一类NP 完全问题,在国际上已有广泛的研究,而国内对MCP 问题的研究则还处于起步阶段,因此,研究最大团问题具有较高的理论价值和现实意义。
最大团问题又称为最大独立集问题(Maximum Independent Set Problem ),在市场分析、方案选择、信号传输、计算机视觉、故障诊断等领域具有非常广泛的应用。
目前,求解MCP 问题的算法主要分为两类:确定性算法和启发式算法。
确定性算法有回溯法、分支限界法等,启发式算法蚁群算法、顺序贪婪算法、DLS-MC 算法和智能搜索算法等。
不管哪种算法,都要求在多项式时间内求得MCP 问题的最优解或近似解。
图分为有向图和无向图,本文主要研究确定性算法求解无向图最大团问题。
基于最大团问题的两种解法作者:李源来源:《数字技术与应用》2011年第09期摘要:最大团问题(maximum clique problem, MCP)是图论中的一个经典组合优化问题,也是一类NP( Non-deterministic Polynomial)完全问题, ,也被称为最大独立集树问题。
给出了最大团问题的基本定义和其数学描述;分析求解该问题的典型启发式算法,即回溯算法和优先队列分支限界算法,本文主要阐述算法的介绍、算法求解最大团问题的基本思路、特点及性能;最后介绍了测试这些启发式算法性能的测试基准图。
关键词:最大团问题回溯算法优先队列分支限界算法图中图分类号: TP312 文献标识码:A 文章编号:1007-9416(2011)09-0132-021、基本概念最大团的定义:给定无向图G=(V,E)。
如果UV,且对任意u,vU有(u,v)E,则称U是G 的完全子图。
G的完全子图U是G的团当且仅当U不包含在G的更大的完全子图中。
G的最大团是指G中所含顶点数最多的团。
如果UV且对任意u,vU有(u,v)E,则称U是G的空子图。
G的空子图U是G的独立集当且仅当U不包含在G的更大的空子图中。
G的最大独立集是G中所含顶点数最多的独立集。
对于任一无向图G=(V,E)其补图G=(V1,E1)定义为:V1=V,且(u,v)E1当且仅当(u,v)E。
2、最大团问题算法设计思想2.1 最大团问题的回溯算法2.1.1 回溯算法设计思想设当前扩展结点Z位于解空间树的第i层。
在进入左子树前,必须确认从顶点i到已选入的顶点集中每一个顶点都有边相连。
在进入右子树前,必须确认还有足够多的可选顶点使得算法有可能在右子树中找到更大的团。
在具体实现时,用邻接矩阵表示图G.。
整型树组V返回所找到的最大团。
V[i]=1,当且仅当顶点i属于找到最大团。
无向图G用邻接矩阵存储如图1。
2.1.2 回溯算法程序设计最大团问题的回溯解法,部分代码如下。
随笔- 218 文章- 0 评论- 22 最大团问题一、定义一个无向图G=(V,E),V 是点集,E 是边集。
取V 的一个子集U,若对于U 中任意两个点u 和v,有边(u,v)∈E,那么称U 是G 的一个完全子图。
U 是一个团当且仅当U 不被包含在一个更大的完全子图中。
G的最大团指的是定点数最多的一个团。
二、常用做法1、顺序贪婪启发式搜索算法2、局部搜索启发式算法3、智能搜索启发式算法4、遗传算法5、模拟退火算法6、禁忌算法7、神经网络算法8、改进蚁群算法-AntMCP如果你想看上面的东西,百度百科中有一些简略的介绍,百度百科传送门:最大团问题下面说说常用的一种搜索算法当然,这种算法很不高效,所以当图中有100 个点以上时,请慎用先看看一个显而易见的DFS :初始化:从一个点u 开始,把这个点加入到一个集合中,设为U。
遍历一遍所有和他相连的点,把他们放入另一个集合S1 中,接下来进行第一遍DFS 第一遍 DFS :从S1 中选择一个点u1,这个点肯定和集合U 中的任何一个点相连。
把集合S1 中u1 能访问到的点加入到集合 S2 中,并把u1 加入到集合U 中,进行第二遍DFS第二遍 DFS :从S2 中选择一个点u2,这个点肯定和集合U 中的任何一个点相连。
把集合S2 中u2 能访问到的点加入到集合S3 中,并把u2 加入到集合U 中,进行第三遍DFS第三遍 DFS :从S3 中选择一个点u3,这个点肯定和集合U 中的任何一个点相连。
把集合S3 中u3 能访问到的点加入到集合S4 中,并把u3 加入到集合U 中,进行第四遍DFS......最底层的 DFS :当某个S 集合为空集的时候,DFS 结束,这时候我们就找到了一个完全子图,用这个完全子图更新我们的最大团。
退出当前的DFS,返回上层DFS,接着找下一个完全子图,直到找完所有的完全子图按照上面介绍的DFS 方法,肯定能够得到一个最大团,因为该DFS 把所有的完全子图都枚举了一遍。
c语言最大数组合(实用版)目录1.C 语言中的整数类型2.最大数组合的求解方法3.示例代码及解析正文一、C 语言中的整数类型C 语言中,整数类型包括有符号整数和无符号整数两种,分别用 int 和 unsigned int 表示。
其中,有符号整数的取值范围为 -2^31 到2^31-1,无符号整数的取值范围为 0 到 2^32-1。
在实际编程中,为了避免溢出等问题,我们通常使用无符号整数来表示整数。
二、最大数组合的求解方法最大数组合问题可以理解为给定一组整数,求出它们的组合中,哪一组整数的和最大。
对于 n 个整数,它们的组合数为 2^n,因此暴力枚举所有的组合数是不现实的。
因此,我们需要采用一种高效的算法来求解最大数组合。
一种常见的求解方法是贪心算法。
具体来说,我们可以从第一个整数开始,依次将它与后面的整数进行比较,如果后面的整数比当前整数大,则将它与当前整数的和更新为后面的整数与当前整数的和,以此类推。
直到遍历完所有的整数,得到的就是最大数组合。
三、示例代码及解析下面是一个求解最大数组合的 C 语言示例代码:```c#include <stdio.h>#include <stdlib.h>int main(){int n, max_sum = 0, temp_sum = 0;for (int i = 0; i < n; i++){for (int j = i + 1; j < n; j++){temp_sum += a[j];if (temp_sum > max_sum){max_sum = temp_sum;}}}printf("最大数组合为:%d, %d", a[0], a[1]);return 0;}```这段代码中,我们使用了双重循环来枚举所有的组合。
其中,外层循环遍历的是当前整数的后缀,内层循环遍历的是当前整数的前缀。
在内层循环中,我们将当前整数与后面的整数相加,如果它们的和大于当前的最大和,则更新最大和。
# include <iostream># include <queue># include <conio.h>using namespace std;typedef struct{int v; //无向图G的顶点int e; //无向图G的边int a[50][50]; //定义图G的邻接矩阵int bestx[50]; //最优解}MCP;void Creat(MCP &G){int i,j;cout<<"请输入定点数:"<<endl;cin>>G.v;cout<<"请输入无向图矩阵:"<<endl;for( i=1;i<=G.v;i++)for(int j=1;j<=G.v;j++)cin>>G.a[i][j];for(i=1;i<=G.v;i++) //初始化{G.bestx[i]=0;}}struct BBNode{BBNode *parent; //指向父结点的指针bool LChild; //左儿子结点标志};struct CliqueNode //定义优先队列类型为CliqueNode{int cn; //当前团的顶点数int un; //当前团最大顶点数的上界int level; //结点在子集空间树种所处的层次BBNode *p; //指向活结点在子集树中相应结点的指针bool operator<(const CliqueNode& b) const //<号重载建立大根堆{if(b.un>un) return true;if(b.un==un && >cn) return true;else return false;}};void BBMaxClique(MCP &G){BBNode *E=new(BBNode); //定义B代表记录的队列情况//初始化int j,i=1;int cn=0,bestn=0;int OK=1;priority_queue<CliqueNode> Q; //定义优先队列QE->LChild=false; //初始化E->parent=NULL;while(i!=G.v+1)//非叶结点{//检查顶点i与当前团中其它顶点之间是否有边相连OK=1;BBNode *B=E; //把当前点的数据给B,B为中间变量for(j=i-1;j>0;B=B->parent,j--)if(B->LChild && G.a[i][j]==0) //如果不满足就停止{OK=0;break;}if(OK) //满足条件,即左儿子结点为可行结点{CliqueNode *D=new(CliqueNode); //定义一个节点DD->p=new(BBNode);if(cn+1>bestn) bestn=cn+1;D->cn=cn+1;D->level=i+1;D->p->LChild=true;D->p->parent=E;D->un=cn+1+G.v-i;Q.push(*D); //进队列}if(cn+G.v-i>bestn ) //不满足条件但是还是可能有最优解{CliqueNode *D=new(CliqueNode); //定义一个节点DD->p=new(BBNode);D->cn=cn;D->level=i+1;D->p->LChild=false;D->p->parent=E;D->un=cn+G.v-i;Q.push(*D); //进队列}CliqueNode N;N=Q.top(); //取队顶元素,最大堆Q.pop(); //删除队顶元素E=N.p; //记录当前团的信息cn=; //记录当前团的顶点数i=N.level; //所在的层次}for(j=G.v;j>0;j--) //保存最优解{G.bestx[j]=E->LChild;E=E->parent;bestn=cn;}}void main(){MCP G;Creat(G);BBMaxClique(G);cout<<"最大团方案为:( ";for(int i=G.v;i>0;i--)if(G.bestx[i]==1){cout<<i<<" ";}cout<<")"<<endl;getch();}。
1321、基本概念最大团的定义:给定无向图G=(V,E)。
如果UV,且对任意u,vU有(u,v)E,则称U是G的完全子图。
G的完全子图U是G的团当且仅当U不包含在G的更大的完全子图中。
G的最大团是指G中所含顶点数最多的团。
如果UV且对任意u,vU有(u,v)E,则称U是G的空子图。
G的空子图U是G的独立集当且仅当U不包含在G的更大的空子图中。
G的最大独立集是G中所含顶点数最多的独立集。
对于任一无向图G=(V,E)其补图G=(V1,E1)定义为:V1=V,且(u,v)E1当且仅当(u,v)E。
2、最大团问题算法设计思想2.1 最大团问题的回溯算法2.1.1 回溯算法设计思想设当前扩展结点Z位于解空间树的第i层。
在进入左子树前,必须确认从顶点i到已选入的顶点集中每一个顶点都有边相连。
在进入右子树前,必须确认还有足够多的可选顶点使得算法有可能在右子树中找到更大的团。
在具体实现时,用邻接矩阵表示图G.。
整型树组V返回所找到的最大团。
V[i]=1,当且仅当顶点i属于找到最大团。
无向图G用邻接矩阵存储如图1。
图12.1.2 回溯算法程序设计 最大团问题的回溯解法,部分代码如下。
class MaxClique {static int[] x; static int n; static int cn ; static int bestn; static int[] bestx; static boolean [][] a; ……//省略 } public class Fac5_7{ public static void main(String args[]) {MaxClique abc=new MaxClique(); int n1=5; int ak[]=new int[n1+1]; boolean [][] aw=new boolean[n1+1][n1+1]; aw[1][1]=false;aw[1][2]=true; aw[1][3]=false; aw[1][4]=true; aw[1][5]=true; aw[2][1]=true; aw[2][2]=false;aw[2][3]=true; aw[2][4]=false;aw[2][5]=true; aw[3][1]=false;aw[3][2]=true; aw[3][3]=false; aw[3][4]=false;aw[3][5]=true; aw[4][1]=true; aw[4][2]=false;aw[4][3]=false; aw[4][4]=false;aw[4][5]=true; aw[5][1]=true; aw[5][2]=true; aw[5][3]=true; aw[5][4]=true; aw[5][5]=false; abc.a=aw; abc.n=n1; System.out.println("最大团顶点数为 " + abc.maxClique(ak)); for(int i=1;i<=n1;i++) System.out.print(" "+abc.bestx[i]); System.out.println(); } }2.1.3 运行结果,如图2图22.2 最大团的问题的优先队列分支限界算法2.2.1 优先分支限界算法思想在扩展内部结点时,首先考察左子树结点,在左子树结点i 加入到当前团中,并检查该顶点与当前团中其他顶点之间是否有边相连。
这是一个很典型的NP问题.很长一段时间一直在想如何解决它,直到那天看了一位前人推荐的文档,得到一些启发才顺利的解决了这个困扰我多天的问题.典型描述:给定一个图G,要求G的最大团(团是指G的一个完全子图,该子图不包含在任何其他的完全子图当中。
最大团指其中包含顶点最多的团).该命题可以有很多变种,例如1002,1654的放机器人,实质上都是求最大团的问题,当然,由于问题的特殊性,他们或许还可以用其他更高效的算法来解.毕竟,问题抽象,解法一般后其实现难度和复杂度也会增大.解决该问题的一般算法该是搜索.设其包含顶点a1,a2,a3,a4,a5·····an。
从a1开始搜索,依次搜索所有与其相邻的点······典型的指数时间搜索。
那天看到一篇文章,专门论述了这个问题。
它不是采用我们惯用的从a1开始搜索的方法,而是反了过来,从an开始走,当然,走的时候还是只向前走(a5开始,就只搜a6,a7······),这样做有什么好处呢?实际上类似于动态规划,这样我们每做的一次搜索都可以为后面的搜索所用,而我们先前的搜索方法则基本上每一次搜索都会从头开始去寻找最有解。
并且,我们注意到这末一个结论:如果max【i】表示搜索a1得到的解,则max【i-1】=max【i】+1或者max【i】。
这个结论是很明显的。
如果ai~an的点可能形成的最大完全图含Max【i】个接点,则a(i-1)~an能形成的完全子图最多比前一个多1个顶点。
这些给我们的搜索提供了一个很强的约束条件。
这里提供ZJU1492的解答源代码加以具体说明(TLE 2次,AC一次1.61s):#i nclude<stdio.h>#i nclude<string.h>int joint[50][50];int Size;int MAX;int DP[50];bool find;int reachEnd(int start,int sets[])//返回-1表示邻接顶点集已经为空集,否则返回第一个公共顶点。
回溯法求解最大团问题python一、什么是最大团问题?最大团问题是一个经典的NP完全问题,其目标是在一个无向图中找到一个完全子图,使得该子图中的任意两个节点都有一条边相连,并且该子图的节点数最多。
换句话说,就是在一个无向图中找到一个最大的、完全的子图。
最大团问题在实际应用中有很多重要的应用,如社交网络分析、生物信息学等。
二、回溯法求解最大团问题回溯法是一种搜索算法,它通过不断地试探和回溯来寻找问题的解。
回溯法通常适用于求解组合优化问题,如旅行商问题、背包问题等。
对于最大团问题来说,回溯法也是一种可行的求解方法。
1. 回溯法基本思想回溯法通过深度优先搜索来遍历所有可能的解空间,并通过剪枝技术来减少搜索空间。
在搜索过程中,每当发现当前状态不满足要求时,就会回溯到之前的状态并尝试其他可能性。
直到找到一个满足要求的解或者遍历了所有可能性为止。
2. 最大团问题的回溯算法实现对于一个无向图G(V,E),其中V表示节点集合,E表示边集合。
我们可以通过一个布尔类型的矩阵A来表示该图的邻接矩阵。
其中,A(i,j) = True表示节点i和节点j之间有一条边相连,反之则为False。
在回溯算法中,我们需要定义一个函数max_clique来实现最大团问题的求解。
该函数需要接受三个参数:G表示无向图的邻接矩阵;k表示当前正在考虑第k个节点;C表示当前已经选定的节点集合。
算法流程如下:1. 如果已经遍历了所有的节点,则输出当前选定的节点集合C,并返回。
2. 否则,对于当前考虑的节点k,分别判断是否与当前已经选定的节点集合C中所有元素都相连。
如果是,则将该节点加入到C中,并继续递归求解下一层问题;否则跳过该节点并继续考虑下一个节点。
3. 在递归求解下一层问题后,如果发现当前选定的节点集合C中元素个数比之前找到的最优解还要多,则更新最优解。
4. 回溯到上一层状态,并尝试其他可能性。
代码实现如下:```pythondef max_clique(G, k, C, best):if k == len(G):if len(C) > len(best):best.clear()best.extend(C)returnif all(G[k][i] for i in C):C.append(k)max_clique(G, k+1, C, best)C.pop()if len(C) + len(G) - k > len(best):max_clique(G, k+1, C, best)# 示例代码G = [[False, True, True, False],[True, False, True, True],[True, True, False, True],[False, True, True, False]]best = []max_clique(G, 0, [], best)print(best)```运行结果为:[1, 2, 3]三、总结回溯法是一种基于深度优先搜索的算法,它通过不断地试探和回溯来寻找问题的解。
最大团问题的回溯法一、引言最大团问题是图论中的一个经典问题,其目的是在给定无向图中找到一个最大的完全子图,即该子图中任意两个顶点之间都有边相连。
最大团问题在计算机科学、网络分析等领域具有广泛的应用。
本文将介绍使用回溯法来解决最大团问题的方法。
回溯法是一种基于深度优先搜索的算法,它通过遍历所有可能的解空间来找到问题的解。
在本文中,我们将首先介绍回溯法的基本思想和实现方法,然后详细讨论如何使用回溯法来求解最大团问题。
二、回溯法基本思想和实现方法1. 基本思想回溯法是一种基于深度优先搜索的算法,其基本思想是在搜索过程中不断地试探和撤销选择,直到找到问题的解或者确定无解。
具体来说,回溯法从起始状态开始,每次选择一个可能的扩展状态,并进入下一层搜索;如果该状态不能满足条件,则撤销上一步选择并尝试其他可能性。
2. 实现方法为了实现回溯法,我们需要定义以下几个关键函数:(1)is_valid:判断当前状态是否满足问题的约束条件。
(2)is_complete:判断当前状态是否是问题的解。
(3)backtrack:回溯函数,用于搜索所有可能的解空间。
在实现回溯法时,我们通常使用递归函数来实现回溯过程。
具体来说,backtrack 函数会从起始状态开始搜索所有可能的解空间,并在满足约束条件和找到问题的解时返回结果。
在每一层递归中,backtrack函数会选择一个可能的扩展状态,并检查该状态是否满足约束条件;如果满足,则继续向下搜索;否则撤销上一步选择并尝试其他可能性。
三、使用回溯法求解最大团问题1. 问题描述最大团问题是在给定无向图中找到一个最大的完全子图,即该子图中任意两个顶点之间都有边相连。
具体来说,给定一个无向图G=(V,E),其中 V 是节点集合,E 是边集合。
最大团问题就是要求出 G 的一个最大团 C=(V',E'),其中 V' 是 C 的节点集合,E' 是 C 的边集合。
实验报告12课程数据构造与算法实验名称回溯法第页班级11计本学号姓名风律澈实验日期:2022年5月27日报告退发(订正、重做)一、实验目的掌握回溯法的原理和应用。
二、实验环境1、微型计算机一台2、WINDOWS操作系统,Java SDK,Eclipse开发环境三、实验内容必做题:1、编写程序,采用回溯法求解最大团问题。
2、编写程序,采用回溯法实现m着色问题。
四、实验步骤和结果(附上代码和程序运行结果截图)1最大团public class Maxclique {/*** @param args*/static graph a;static int n;//顶点数目static int nowanswer[];//当前解static int nowpointn;//当前顶点数量static int nowmostpointn;//当前最多顶点子图static int nowbestanswer[];//当前可能最优解public static void main(String[] args) {// TODO Auto-generated method stubint x[][]={{1,1,0,1,1},{1,1,1,0,1},{0,1,1,0,1},{1,0,0,1,1},{1,1,1,1,1}};//初始化变量n=5;a=new graph(x,n);nowanswer=new int[n];nowpointn=0;nowmostpointn=0;nowbestanswer=new int[n];nowbestanswer=a.getpoint(1).getreach();nowanswer[0]=1;//进入算法backtrack(0);//输出解System.out.println(nowmostpointn);for(int i=0;i<nowanswer.length;i++){System.out.print(nowanswer[i]+" ");}}private static void backtrack(int i) {// TODO Auto-generated method stubif(i>n-1){for(int j=0;j<n;j++)nowbestanswer[j]=nowanswer[j];nowmostpointn=nowpointn;return;}int ok=a.check(i,nowanswer);if(ok==1) {nowanswer[i]=1;nowpointn++;backtrack(i+1);nowpointn--;}else if(nowpointn+n-i>nowmostpointn){nowanswer[i]=0;backtrack(i+1);}}} ——————————————————————————————————public class graph {private point p[];public graph(int x[][],int n){p=new point[x.length];for(int i=0;i<n;i++){p[i]=new point(x[i]);}}public int returnpointnumber(){return p.length;}public int check(int i, int[] x) {// TODO Auto-generated method stubint ok = 1;for(int j=0;j<i;j++){if(x[j]==1&&(p[i].reachornot(j)!=1)){ok=0;break;}}return ok;}public point getpoint(int i){return this.p[i];}} ———————————————————————————————————————public class point {private int reach[];public point(int x[]){this.reach=x;};public int reachornot(int j){return reach[j];}public int[] getreach(){return reach;}} ———————————————————————————————————————2,m着色问题public class Coloring {/*** @param args*/static int n,m;static int [][]a;static int []x;static long sum;public static long mColoring(int mm){ m=mm;sum=0;backtrack(1);return sum;}private static void backtrack(int t) { // TODO Auto-generated method stubif(t>n){sum++;for(int i=1;i<=n;i++)System.out.print(x[i]+" ");System.out.println();}elsefor(int i=1;i<=m;i++){x[t]=i;if(check(t)==1){backtrack(t+1);x[t]=0;}}}private static int check(int k) {// TODO Auto-generated method stubfor(int j=1;j<=n;j++)if(a[k][j]==1&&(x[j]==x[k]))return 0;return 1;}public static void main(String[] args) { // TODO Auto-generated method stuba=new int[][]{{0,0,0,0,0,0},{0,0,1,1,1,0},{0,1,0,1,1,1},{0,1,1,0,1,0},{0,1,1,1,0,1},{0,0,1,0,1,0}};m=4;n=5;x=new int[6];long count;count=mColoring(m);System.out.println(count);}}五、实验总结(本次实验完成的情况,心得体会)。
实验报告14课程数据结构与算法实验名称分支限界法第页班级11计本学号105032011130 姓名风律澈实验日期:2013年6月8日报告退发(订正、重做)一、实验目的掌握分支限界法的原理和应用。
二、实验环境1、微型计算机一台2、WINDOWS操作系统,Java SDK,Eclipse开发环境三、实验内容必做题:1、编写程序,采用问题。
2、编写程序,采用分支限界法求解旅行售货员问题。
四、实验步骤和结果(附上代码和程序运行结果截图)1、分支限界法最大团package分支限界最大团;public class main {/*** @param args*/public static void main(String[] args) {// TODO Auto-generated method stubint [][]a={{1,1,0,1,1},{1,1,1,0,1},{0,1,1,0,1},{1,0,0,1,1},{1,1,1,1,1}};graph g=new graph(a);g.find_answer();g.show();}}----------------------------------------------------------------------------------------------------------------------package分支限界最大团;import java.util.ArrayList;import java.util.PriorityQueue;public class graph {private ArrayList<point> points;private ArrayList<point> answer_point;public graph(int [][]a){this.points=new ArrayList<>();this.answer_point=new ArrayList<>();int n=a.length;for(int i=0;i<n;i++)this.points.add(new point());for(int i=0;i<n;i++)for(int j=i+1;j<n;j++)if(a[i][j]==1){point pi=this.points.get(i);point pj=this.points.get(j);pi.add_nearpoint(pj);pj.add_nearpoint(pi);}}public void find_answer() {// TODO Auto-generated method stubint total_points=this.points.size();PriorityQueue<Node> enode=new PriorityQueue<>();int node_in=0;point node_point=this.points.get(0);ArrayList<point> nowanswer=new ArrayList<>();int maxnum_point=this.points.size();Node startnode=new Node(node_in,node_point,nowanswer,maxnum_point);enode.offer(startnode);Node node=null;while(true){if(enode.isEmpty())break;node=enode.poll();if(node.get_node_in()==maxnum_point)break;if(node.ifleft()){int new_node_in=node.get_node_in()+1;int new_answer=node.get_maxnum_point();point newpoint;if(new_node_in==total_points)newpoint=null;elsenewpoint=this.points.get(new_node_in);ArrayList<point> new_answer_point=new ArrayList<point>(node.get_answer_point());new_answer_point.add(node.get_node_point());Node new_node=new Node(new_node_in,newpoint,new_answer_point,new_answer);enode.offer(new_node);if(new_node.get_answer_point().size()>this.answer_point.size())this.answer_point=newArrayList<>(new_node.get_answer_point());}if(node.ifright(this.points.size(),this.answer_point.size())){ArrayList<point>new_node_answer_points=node.get_answer_point();int new_node_in=node.get_node_in()+1;int new_node_max=node.get_maxnum_point()-1;point p=this.points.get(new_node_in);Node newnode=new Node(new_node_in,p,new_node_answer_points,new_node_max);enode.add(newnode);}}}public void show(){System.out.println(this.answer_point);}}---------------------------------------------------------------------------------------------------------------------- package分支限界最大团;import java.util.ArrayList;public class point {private static int ids=1;private int id;private ArrayList<point> nearpoints;public point(){super();this.id=ids++;this.nearpoints=new ArrayList<point>();}public String toString(){return"顶点"+this.id;}public void add_nearpoint(point p){this.nearpoints.add(p);}public boolean check_if_nearpoint(point p){return this.nearpoints.contains(p);}}---------------------------------------------------------------------------------------------------------------------- package分支限界最大团;import java.util.ArrayList;public class Node implements Comparable<Node> {private int node_in;private point node_point;private ArrayList<point> answer_points;private int maxnum_point;public Node(int n,point p,ArrayList<point> a,int m){super();this.node_in=n;this.node_point=p;this.answer_points=a;this.maxnum_point=m;}public int compareTo(Node o) {// TODO Auto-generated method stubif(this.maxnum_point>o.maxnum_point) return -1;if(this.maxnum_point<o.maxnum_point) return 1;return 0;}public int get_node_in(){return this.node_in;}public ArrayList<point> get_answer_point(){return this.answer_points;}public boolean ifleft(){for(point p:this.answer_points)if(!p.check_if_nearpoint(this.node_point))return false;return true;}public boolean ifright(int total_point,int nowanswer){int leftpoint=total_point-this.node_in;if(this.answer_points.size()+leftpoint>nowanswer)return true;return false;}public int get_maxnum_point(){return this.maxnum_point;}public point get_node_point(){return this.node_point;}}-----------------------------------------------------------------------2、分支限界法求旅行售货员package旅行售货员问题;public class main {/*** @param args*/public static void main(String[] args) {int[][]a={{0,30,6,4},{30,0,5,10},{6,5,0,20},{4,10,20,0},};graph g=new graph(a);g.findanswer();g.show();}}------------------------------------------------------------------------------- package 旅行售货员问题;import java.util.ArrayList;import java.util.Collections;import java.util.PriorityQueue;public class graph {private ArrayList<point> points;private int[][] a;private ArrayList<point> answer_points;private int answerpathlong;public graph(int[][] b) {this.points = new ArrayList<>();int n = b.length;for (int i = 0; i < n; i++)this.points.add(new point());this.a = b;this.answerpathlong = Integer.MAX_VALUE;this.answer_points = null;this.set_point_short_long();}public int get_value(point px, point py) {int p1id = px.getid();int p2id = py.getid();return this.a[p1id][p2id];}public void findanswer(){PriorityQueue<Node>enode=new PriorityQueue<>();ArrayList<point> root_list=this.points;int root_answer=0;for(point p:this.points)root_answer+=p.getshortestlong();int root_in=0;int root_path_long=0;Node root_node=new Node(root_in, root_list, root_path_long, root_answer);enode.offer(root_node);while(true){if(enode.isEmpty())break;Node old_node=enode.poll();int total_point=this.points.size();if(old_node.if_end(total_point)){this.answer_points=new ArrayList<>(old_node.get_now_sort());this.answerpathlong=old_node.get_now_path_long();break;}if(old_node.if_end_father(total_point)){int new_node_in=old_node.get_node_in()+1;ArrayList<point> new_node_set=old_node.get_now_sort();point old_point_node=old_node.get_node_point();point new_point_node=new_node_set.get(new_node_in);point startpoint=this.points.get(0);intnew_node_nowpath=old_node.get_now_path_long()+this.get_value(old_point_node, new_point_node)+this.get_value(new_point_node, startpoint);int maybe_the_best=new_node_nowpath;Node new_node=new Node(new_node_in, new_node_set, new_node_nowpath, maybe_the_best);if(new_node.get_now_path_long()<this.answerpathlong){this.answerpathlong=new_node.get_now_path_long();enode.offer(new_node);}continue;}for(int i=old_node.get_node_in()+1;i<total_point;i++){//尝试原节点的各个分支if(old_node.if_in_i(i, this.a, this.answerpathlong)){int new_node_in=old_node.get_node_in()+1;ArrayList<point> newnode_nowset=new ArrayList<>(old_node.get_now_sort());int j=new_node_in;this.changeij(newnode_nowset, i, j);point old_node_to_point=old_node.get_node_point();point new_node_to_point=newnode_nowset.get(new_node_in);intnew_maybe_the_best=old_node.get_node_answer()+this.get_value(old_node_to_point, new_node_to_point)-old_node_to_point.getshortestlong();intnew_node_path_long=old_node.get_now_path_long()+this.get_value(old_node_to_poin t, new_node_to_point);Node new_node=new Node(new_node_in, newnode_nowset, new_node_path_long, new_maybe_the_best);enode.offer(new_node);}}}}private void changeij(ArrayList<point> points,int i, int j) {// TODO Auto-generated method stubCollections.swap(points, i, j);}public void show(){System.out.println(this.answer_points);System.out.println("最优路径长度为:"+this.answerpathlong);}private void set_point_short_long(){point p;int[]value;for(int i=0;i<this.points.size();i++){p=this.points.get(i);value=this.a[i];for(int x:value){if(x==0)continue;if(x<p.getshortestlong())p.setshortestlong(x);}}}}------------------------------------------------------------------------------- package旅行售货员问题;public class point {private static int ids=0;private int id;private int shortestlong;public point() {super();this.id=point.ids++;this.shortestlong=Integer.MAX_VALUE;}public int getid() {return id;}@Overridepublic String toString() {return"顶点:"+this.id+";";}public int getshortestlong() {return shortestlong;}public void setshortestlong(int s) {this.shortestlong = s;}------------------------------------------------------------------------------- package旅行售货员问题;import java.util.ArrayList;public class Node implements Comparable<Node>{private int node_in;private point node_point;private ArrayList<point>now_sort;private int now_path_long;private int node_answer;public Node(int in, ArrayList<point> set, int p_l,int n_a) { super();this.node_in = in;this.now_sort = set;this.now_path_long = p_l;this.node_answer = n_a;this.node_point=this.now_sort.get(in);}public boolean if_in_i(int i,int[][]a,int now_long){point now_point=now_sort.get(node_in);point pointi=now_sort.get(i);int now_id=now_point.getid();int pointi_id=pointi.getid();int value=a[now_id][pointi_id];if(value==Integer.MAX_VALUE)return false;intpointi_answer=this.node_answer+value-now_point.getshortestlong();if(pointi_answer>now_long)return false;return true;}public boolean if_end(int point_sum){if(this.node_in==point_sum-1)return true;return false;}public boolean if_end_father(int point_number){if(this.node_in==point_number-2)return true;return false;}public int get_now_path_long() {return now_path_long;public ArrayList<point> get_now_sort() {return now_sort;}public int get_node_in() {return node_in;}public int get_node_answer() {return node_answer;}public point get_node_point() {return node_point;}@Overridepublic int compareTo(Node o) {if(this.node_answer>o.node_answer)return 1;if(this.node_answer<o.node_answer)return -1;return 0;}}-----------------------------------------------------------------------五、实验总结(本次实验完成的情况,心得体会)。
计算机算法设计与分析最大团问题研究报告目录1. MCP问题描述 (1)1.1 MCP问题基本概念 (1)1.2 MCP问题数学描述 (1)2. MCP问题应用背景 (2)3. 求解MCP问题的常用算法 (2)3.1 顺序贪婪启发式算法 (2)3.2 局部搜索启发式算法 (2)3.3 智能搜索启发式算法 (3)3.3.1 遗传算法 (3)3.3.2 模拟退火算法 (3)3.3.3 禁忌算法 (4)3.3.4 神经网络算法 (4)3.4 改进蚁群算法-AntMCP (4)3.5 其它启发式算法 (5)3.6 回溯法 (6)3.6.1 算法基本思想 (6)3.6.2 算法设计思想 (6)3.6.3 实例分析 (7)3.6.4 程序设计及测试 (8)3.7 分支限界法 (11)3.7.1 算法描述 (11)3.7.2 算法求解流程 (12)3.7.3 优先队列式分支限界法求解MCP问题 (12)3.7.4 实例分析 (13)3.7.5 程序设计及测试 (13)4. 回溯法与分支限界法比较 (18)最大团问题及其求解算法研究最大团问题(Maximum Clique Problem, MCP )是图论中一个经典的组合优化问题,也是一类NP 完全问题,在国际上已有广泛的研究,而国内对MCP 问题的研究则还处于起步阶段,因此,研究最大团问题具有较高的理论价值和现实意义。
最大团问题又称为最大独立集问题(Maximum Independent Set Problem ),在市场分析、方案选择、信号传输、计算机视觉、故障诊断等领域具有非常广泛的应用。
目前,求解MCP 问题的算法主要分为两类:确定性算法和启发式算法。
确定性算法有回溯法、分支限界法等,启发式算法蚁群算法、顺序贪婪算法、DLS-MC 算法和智能搜索算法等。
不管哪种算法,都要求在多项式时间内求得MCP 问题的最优解或近似解。
图分为有向图和无向图,本文主要研究确定性算法求解无向图最大团问题。
最大团问题MaxCliqueC++源码// TSP.htemplate class Traveling{public:Traveling(int,T**,T,T);~Traveling();void Backtrack(inti);void print();private:void swap(T&a,T&b);int N,*X,*BestX;T**A,CC,BestC,NoEdge;};templateTraveling::Traveling(intn,T**a,Tc,T e) {N=n;A=a;NoEdge=e;X=new int[N+1];BestX=new int[N+1];CC=c;BestC=e;for(inti=0;i<=N;i++)X[i]=i;}templateTraveling::~Traveling(){delete[]X;delete[]BestX;}templatevoid Traveling::swap(T&a,T&b) {T temp=a;a=b;b=temp;}templatevoid Traveling::print(){if(BestC!=NoEdge){cout<<"最优巡回路线是:"<<endl;< p=""> cout<<bestx[1]<<endl;< p="">for(inti=2;i<=N;i++){cout<<"->"<<bestx[i]<<endl;< p="">}cout<<endl;< p="">cout<<"所需费用为:"<<bestc<<endl;< p=""> }elsecout<<"问题无解!"<<endl;< p="">}templatevoid Traveling::Backtrack(inti){if(i==N){if(A[X[N-1]][X[N]]!=NoEdge&&A[X[N]][1]!=NoEdge&&(CC+A[X[N-1]][X[N]]+A[X[N]][1]{for(int j=1;j<=N;j++){BestX[j]=X[j];}BestC=CC+A[X[N-1]][X[N]]+A[X[N]][1]; }}else{for(int j=i;j<=N;j++){//是否可进入x[j]子树?if(A[X[i-1]][X[j]]!=NoEdge&&(CC+A[X[i-1]][X[i]]<bestc||< p=""> BestC==NoEdge)){//搜索子树swap(X[i],X[j]);CC+=A[X[i-1]][X[i]];Backtrack(i+1);CC-=A[X[i-1]][X[i]];swap(X[i],X[j]);}}}}// make2db.h#ifndef Make2DArray_#define Make2DArray_templatevoid Make1DArray(T * &x, int cols){x = new T[cols];}templatevoid Make2DArray(T ** &x, int rows, int cols){x = new T * [rows];for (inti = 0; i< rows; i++)x[i] = new T[cols];}templatevoid Make3DArray(T *** &x, int plan, int rows, int cols){ x = new T ** [plan];for (inti = 0; i< plan; i++)x[i] = new T* [rows];for (inti = 0; i< plan; i++)for (int j = 0; j < rows; j++)x[i][j] = new T[cols];}templatevoid remove1DArray(T * &x){delete [] x;}templatevoid remove2DArray(T ** &x, int rows){for (inti = 0; i< rows; i++)delete [] x[i];delete [] x;}templatevoid remove3DArray(T *** &x, int plan, int rows){ for (inti = 0; i< plan; i++)for (int j = 0; j < rows; j++)delete [] x[i][j];for (inti = 0; i< rows; i++)delete [] x[i];delete [] x;}#endif// cclique.hclass clique{public:clique(int **,int [],int);~clique();void backtrack(inti);void print();private:int **a;//图g的邻接矩阵int n;//图g顶点数int *x;//当前解int *bestx;//当前最优解intcn;//当前顶点数intbestn;//当前最大顶点数int **A;int N;int *X;};clique::clique(int **a,int v[],int n) {cout<<"*"<<endl;< p="">A=a;N=n;X=new int[N+1];cn=0;bestx=new int[N+1];v=new int[N+1];/*for(inti=0;i<=N;i++){X[i]=1;v[i]=0;bestx[i]=v[i];}*/cout<<"*"<<endl;< p="">}clique::~clique(){delete []x;delete []bestx;}void clique::print(){if(bestx!=0){cout<<"最大团是:"<<endl;< p="">cout<<bestx[1]<<endl;< p="">for(inti=2;i<=N;i++){cout<<bestx[i]<<' '<<endl;<="" p="">}cout<<endl;< p="">}elsecout<<"问题无解!"<<endl;< p="">}void clique::backtrack(inti)//回朔方程{if(i>N)//判断是否有必要继续进行回朔{for(int j=1;j<=N;j++)//如果i小于总顶点的个数就执行循环bestx[j]=X[j];//并且将当前解赋值给当前最优解bestn=cn;//并且将当前顶点个数记为最大顶点个数return;}int ok=1;for(int j=1;j<="">if(X[j]&&A[i][j]==0)//如果i与j是不相连的{ok=0;//将ok变为假break;//并且跳出循环}if(ok)//如果ok为真{X[i]=1; //把相对的数组的位置变为真cn++;//并且当前点数增加一backtrack(i+1);//再马上递归调用原函数继续判断下个个数的顶点数是否适合组成最大团X[i]=0;//要是得出的数组相对应的位置为假cn--;//当前点减小一}if(cn+N-i>bestn)//如果当前点加上总共点的个数减去参数的值比当前最大的点数大当出现不符合组成最大团的点的时候就是用这个函数将其踢走{X[i]=0;//相对应数组的位置变成假backtrack(i+1);//然后调用下个点的个数}}// Maxclique.cpp#include#include#include"cclique.h"#include"Make2db.h"#include"stdlib.h"void main(void){int n,**a,*v;ifstream fin("data.txt");if(!fin){cerr<<"不能打开文件:"<<"data.txt"<<endl;< p="">exit(1);}fin>>n;Make2DArray(a,n+1,n+1); for(inti=1;i<n;i++)< p=""> for(int j=1;j<=n;j++) fin>>a[i][j];clique cli(a,v,n);cli.backtrack(1);cli.print();//TravelingTra(n,a,0,-1);//Tra.Backtrack(2);//Tra.print();remove2DArray(a,n+1);}</n;i++)<></endl;<></endl;<></endl;<></bestx[i]<<'></bestx[1]<<endl;<></endl;<></endl;<></endl;<></bestc||<></endl;<></bestc<<endl;<></endl;<></bestx[i]<<endl;<></bestx[1]<<endl;<></endl;<>。
最大团问题最大团问题251实例最大团:{ 1, 3, 4, 5 },问题:无向图G =<V ,E >, 求G 的最大团. G 的子图:G'= <V ',E '>, 其中V '⊆V , E '⊆E , G 的补图:=<V , E '>, E '是E 关于完全图边集的补集G G 中的团:G 的完全子图G 的最大团:顶点数最多的团独立集与团G 的点独立集:G 的顶点子集A ,且∀u , v ∈A , {u,v }∉E .最大点独立集:顶点最多的点独立集35G 的最大团:U ={ 1, 3, 6 }补图G 命题:U 是G 的最大团当且仅当U 是的最大点独立集. G应用编码, 故障诊断, 计算机视觉, 聚类分析,经济学, 移动通信,VLSI电路设计,...例子: 噪音使信道传输字符发生混淆混淆图G=<V,E>,V为有穷字符集,{u,v}∈E⇔u和v易混淆ijtn编码设计ac ad ae bc bd be G 与H的正规积G cde Hxy 与uv 混淆⇔x 与u 混淆且y 与v 混淆∨x=u 且y 与v 混淆∨x 与u 混淆且y=v为减少噪音干扰,设计代码应该找混淆图中的最大点独立集最大团问题问题:给定无向图G = < V, E >, 其中顶点集V= { 1, 2, … , n},边集为E . 求G 中的最大团., x2, … , x n>为0-1向量,解:< x1x k=1当且仅当顶点k属于最大团. 蛮力算法:对每个顶点子集,检查是否构成团,即其中每对顶点之间是否都有边. 有2n 个子集,至少需要指数时间.分支限界算法设计搜索树为子集树., x2, … , x k> 的含义:结点< x1=1表示已检索顶点1, 2, … , k, 其中xi顶点i在当前的团内, i= 1, 2, … , k约束条件:该顶点与当前团内每个顶点都有边相连界:当前已检索到的极大团的顶点数代价函数代价函数:目前的团可能扩张为极大团的顶点数上界F = C n+ n −k为目前团的顶点数(初始为0)其中Cnk 为结点层数最坏情况下时间:O( n2n )实例顶点编号顺序为1, 2, 3, 4, 5,对应x 1, x 2, x 3, x 4, x 5,x i = 1 当且仅当i 在团内分支规定左子树为1,右子树为0.B 为界,F 为代价函数值.251搜索树a : 极大团{1,2,4},顶点数为3, 界B =3=4B =3B =4=3输出最大团{1,3,4,5}, 顶点数为4. 251b :代价函数值F =3, c : 极大团{1,3,4,5}, 顶点数为4,修改界B =4;d : F =3,不必搜索;e : F =4, 不必搜索.小结•最大团问题的定义与点独立集的关系•分支限界算法的设计树的结构:子集树分支约束条件代价函数与界的设定•最坏情况的时间复杂度:O(n2n)11。
最大团问题目录1. MCP问题描述 (1)1.1 MCP问题基本概念 (1)1.2 MCP问题数学描述 (1)2. MCP问题应用背景 (2)3. 求解MCP问题的常用算法 (2)3.1 顺序贪婪启发式算法 (2)3.2 局部搜索启发式算法 (2)3.3 智能搜索启发式算法 (3)3.3.1 遗传算法 (3)3.3.2 模拟退火算法 (3)3.3.3 禁忌算法 (4)3.3.4 神经网络算法 (4)3.4 改进蚁群算法-AntMCP (4)3.5 其它启发式算法 (5)3.6 回溯法 (6)3.6.1 算法基本思想 (6)3.6.2 算法设计思想 (6)3.6.3 实例分析 (7)3.6.4 程序设计及测试 (8)3.7 分支限界法 (11)3.7.1 算法描述 (11)3.7.2 算法求解流程 (12)3.7.3 优先队列式分支限界法求解MCP问题 (12)3.7.4 实例分析 (13)3.7.5 程序设计及测试 (13)4. 回溯法与分支限界法比较 (18)最大团问题及其求解算法研究最大团问题(Maximum Clique Problem, MCP )是图论中一个经典的组合优化问题,也是一类NP 完全问题,在国际上已有广泛的研究,而国内对MCP 问题的研究则还处于起步阶段,因此,研究最大团问题具有较高的理论价值和现实意义。
最大团问题又称为最大独立集问题(Maximum Independent Set Problem ),在市场分析、方案选择、信号传输、计算机视觉、故障诊断等领域具有非常广泛的应用。
目前,求解MCP 问题的算法主要分为两类:确定性算法和启发式算法。
确定性算法有回溯法、分支限界法等,启发式算法蚁群算法、顺序贪婪算法、DLS-MC 算法和智能搜索算法等。
不管哪种算法,都要求在多项式时间内求得MCP 问题的最优解或近似解。
图分为有向图和无向图,本文主要研究确定性算法求解无向图最大团问题。
1. MCP 问题描述1.1 MCP 问题基本概念给定无向图G =(V , E ),其中V 是非空集合,称为顶点集;E 是V 中元素构成的无序二元组的集合,称为边集,无向图中的边均是顶点的无序对,无序对常用圆括号“( )”表示。
如果U ⊆V ,且对任意两个顶点u ,v ∈U 有(u , v )∈E ,则称U 是G 的完全子图。
G 的完全子图U 是G 的团当且仅当U 不包含在G 的更大的完全子图中。
G 的最大团是指G 中所含顶点数最多的团。
如果U ⊆V 且对任意u ,v ∈U 有(u , v )∉E ,则称U 是G 的空子图。
G 的空子图U 是G 的独立集当且仅当U 不包含在G 的更大的空子图中。
G 的最大独立集是G 中所含顶点数最多的独立集。
对于任一无向图G =(V , E ),其补图G '=(V ', E ')定义为:V '=V ,且(u , v )∈E '当且仅当(u , v )∉E 。
如果U 是G 的完全子图,则它也是G '的空子图,反之亦然。
因此,G 的团与G '的独立集之间存在一一对应的关系。
特殊地,U 是G 的最大团当且仅当U 是G '的最大独立集。
1.2 MCP 问题数学描述最大团问题作为一个整数规划问题有许多等价的描述,整数规划问题描述如下:设t : (0,1)n →2v ,t (x )={i ∈V : x i =1},∀x ∈{0,1}n ,∀S ∈2v ,则x =t -1(S )={x i : i =1,2,…,n },其中⎩⎨⎧∉∈=S i S i x i ,0,1,n 为图的顶点数。
∑=-=ni i x x f 1)(min (1)s.t. nj i x E j i x x }1,0{,),(,1∈'∈∀≤+。
如果x*是式(1)的最优解,则集合C =t (x*)是图G 的一个最大团,且|C |=-f (x*)。
由于x i , x j ∈{0,1},x i +x j ≤1,∀(i , j )∈E '当且仅当x i x j =0,有x I A x x x x x f G T j i E j i j i n i i )(2)(,),(1-=+-='>'∈=∑∑,其中G A '为图G 的补图G'的邻接矩阵。
MCP 问题等价于下面的全局二次0/1问题:Ax x x f T =)(min (2)s.t. x ∈{0,1}n其中A =A G'-I 。
如果x*是式(2)的最优解,则集合C =t (x*)是图G 的一个最大团,且|C |=-f (x*)。
2. MCP 问题应用背景MCP 问题是现实世界中一类真实问题,在市场分析、方案选择、信号传输、计算机视觉、故障诊断等领域具有非常广泛的应用。
自1957年Hararv 和Ross 首次提出求解最大团问题的确定性算法以来,研究者们已提出了多种确定性算法来求解最大团问题。
但随着问题规模的增大(顶点增多和边密度变大),求解问题的时间复杂度越来越高,确定性算法显得无能为力,不能有效解决这些NP 完全问题。
20世纪80年代末,研究者们开始尝试采用启发式算法求解最大团问题,提出了各种各样的启发式算法,如顺序贪婪启发式算法、遗传算法、模拟退火算法、禁忌搜索算法、神经网络算法等,并且取得了令人满意的效果。
在时间上,由于采用了启发式信息,启发式算法的运算时间与确定性算法的运算时间之间的比值会随着图的顶点、边密度的增加而变得越来越小。
唯一的缺点就是不一定能找到最优值,有时只能找到近优值。
近年来研究表明,单独使用一种启发式算法求解最大团问题,算法性能往往并不是很好,因此,常借鉴算法之间优势互补策略,形成新的混合启发式算法来求解最大团问题。
当前求解该问题最好的启发式算法有反作用禁忌搜索(Reactive Tabu Search, RTS )算法、基于遗传算法的简单启发式算法(Simple Heuristic Based Genetic Algorithm, HGA )、DLS-MC 算法等。
3. 求解MCP 问题的常用算法本节首先对求解最大团问题的常用算法进行简要的阐述,然后重点对回溯法和分支限界法求解最大团问题进行着重分析,并用C++语言在Visual Studio 2008环境下编程实现。
3.1 顺序贪婪启发式算法顺序贪婪启发式算法是最早的求解最大团的启发式算法。
这类算法通过给一个团重复进行加点操作得到一个极大团或者对一组并不是团的子图重复进行删除顶点操作以得到一个团。
1987年,Kopf 和Ruhe 把这类型算法分为Best in 和Worst out 两类。
(1) Best in 方法的基本思路:由一个团出发,和这个团中顶点相连的顶点组成候选集;然后以一定的启发式信息,从中选择顶点加入团中,以后反复进行,直到最后得到一个极大团。
(2) Worst out 方法的基本思路:从整个顶点集开始,然后按一定的启发式信息,从中反复进行删除顶点操作,直到最后得到一个团。
顺序贪婪启发式算法有很大不足,该算法一旦找见一个极大团,搜索就停止,因此找到最大团的概率相对较低。
3.2 局部搜索启发式算法假设S G 为图的所有极大团的集合,由于顺序贪婪启发式算法仅能找见S G 中的一个极大团,因此,为了提高解的质量,应当扩大在S G 的搜索区域,比如,可以在极大团S 的邻居中继续进行搜索,以扩大搜索区域,进而提高解的质量。
在局部搜索启发式算法中,如果搜索S 的邻居越多,提高解的质量的机会就越大。
依赖不同的邻居定义,局部搜索启发式算法可以得到不同的解。
在局部搜索启发式算法中,比较有名的算法是K-interchange 启发式算法,它是一种基于K-neighbor邻居实现的,在解集S的K邻居中进行局部搜索的方法。
分析可知,局部搜索启发式算法存在一个问题,即仅能够找见一个局部最优值。
所以为了提高求解的质量,常把该算法和其它算法相混合,从而得到求解MCP问题的新的算法。
Wayne Pullan和Holger H.Hoos基于这一思想提出了求解最大团问题的DLS-MC算法,该算法是plateau search局部搜索启发式和算法迭代改善法相混合得到的,算法性能非常好,在该方法中引入了顶点惩罚函数,该函数在算法的求解过程中能够动态改变;在算法执行过程中迭代改善法和plateau search算法轮流执行来提高解的质量。
在基准图上对该算法进行了测试,性能非常好。
3.3 智能搜索启发式算法智能搜索算法主要有遗传算法、禁忌算法、模拟退火算法、神经网络等。
3.3.1 遗传算法遗传算法(Genetic Algorithm, GA)是一种基于自然选择和群体遗传机理的搜索算法,它模拟了自然选择和自然遗传过程中发生的复制、交叉和变异现象。
1993年,Carter和Park首次提出使用遗传算法求解最大团问题,但由于所求解的质量差,计算复杂度高,因此,他们认为遗传算法并不适合求解最大团问题。
与此同时,Bäck和Khuri致力于最大独立集问题的求解,却得到了完全相反的结论,通过选用合适的适应度函数,取得了很好的效果。
因此在使用GA来解决最大团问题时,适应度函数起着非常关键的作用。
此后,基于遗传算法求解最大团问题的方法逐渐增多,但在提高解的质量,降低算法复杂度上方面却没有大幅度的提高。
l998年,Marchiori提出了一种基于遗传算法的简单启发式算法(simple heuristic based genetic algorithm, HGA)。
算法由两部分组成:简单遗传算法(simple genetic algorithm, SGA)和简单的贪婪启发式局部搜索算法(simple greedy heuristic local search algorithm, SGHLSA)。
在基准图上对算法HGA的性能进行测试,证明了该算法在解的质量和计算速度方面都优于基于遗传算法的其它算法。
因此,单纯使用遗传算法(改动变异、杂交、选择等算子)求解最大团问题时,算法的性能是比较差;要提高算法性能,遗传算法最好能和局部搜索算法相结合。
3.3.2 模拟退火算法模拟退火(Simulated Annealing, SA)算法是N. Metropolis在1953年提出的一种基于物质退火过程的随机搜索算法,是一种迭代求解的启发式随机搜索算法。
首先在高温下较快地进行搜索,使系统进入“热平衡”状态,大致地找到系统的低能区域。
随着温度的逐渐降低,搜索精度不断提高,可逐渐准确地找到最低能量的基态。
作为局部搜索算法的扩展,当邻域的一次操作使当前解的质量提高时,接受这个改进解作为新的当前解;反之,以一定的概率接受相对质量比较差的解作为新的当前解。