当前位置:文档之家› 回溯法

回溯法

回溯法
1 一般方法 2 n-皇后 3 子集和数
1 一般方法
1.1 基本概念
规 定 每 个 xi 取 值 的 约 束 条 件 称 为 显 式 约 束 (explicit constraint)。 对给定的一个问题实例,显式约束规定了所有 可能的元组,它们组成问题的候选解集,被称为 该问题实例的解空间(solution space)。 隐式约束(implicit constraint)给出了判定一 个候选解是否为可行解的条件。
【程序3】 蒙特卡罗算法 int Estimate(SType* x) { int k=0,m=1,r=1; do { SetType S={ x[k]| x[k]T(x[1],,x[k1]) && Bk(x[1],,x[k]==true)}; if (!Size(S)) return m; r=r*Size(S);m=m+r; x[k]=Choose(S);k++; }while(1); }
皇后问题的状态历排列树的时间为(n!)。
2.3 n-皇后算法
【程序8-4】 n-皇后问题的回溯算法 bool Place(int k, int i,int* x) {//判定两个皇后是否在同一列或在同一斜线上 for (int j=0;j<k;j++) if ((x[j]==i) || (abs(x[j]-i)==abs(j-k))) return false; return true; } void NQueens(int n,int *x) { NQueens(0,n,x); }
2.4 时间分析
可通过计算得到这5次试验中实际需要生成的结 点数的平均值为1625。8-皇后问题状态空间树的结 j 7 点总数是:
1 ( 8 i ) 109601
j 0 i 0
因此,需要实际生成的结点数目大约占总结点数 的1.55%。
3 子集和数
3.1 问题描述
已知n个不同正数wi ,0in-1,的集合,求该集合 的所有满足条件的子集,使得每个子集中的正数之 和等于另一个给定的正数M。 例8-2 设有n=4个正数的集合 W={w0,w1,w2,w3}=(11,13,24,7)和整数M=31,求W的 所有满足条件的子集,使得子集中的正数之和等于M。
1.3 回溯法的效率分析
回溯算法的最坏情况时间复杂度可达O(p(n)n!) (或O(p(n)2n)或O(p(n)nn)),这里p(n)是n的多项 式,是生成一个结点所需的时间。
蒙特卡罗方法(Monte Carlo)。这种估计方法的 基本思想是在状态空间树中随机选择一条路径(x0, x1,…, xn1)。设X是这条随机路径上,代表部分向量 (x0, x1,…, xk1)的结点,如果在X处不受限制的孩子 数目是mk,则认为与X同层的其他结点不受限制的 孩子数目也都是mk。 整个状态空间树上将实际生成的结点数估计为 m = 1+m0+m0m1+m0m1m2+
k 1
3.3 子集和数算法
【程序8-5】子集和数的回溯算法 void SumOfSub (float s,int k,float r,int* x,float m,float* w) { x[k]=1; if (s+w[k]==m) { for (int j=0;j<=k;j++) cout<<x[j]<< ' '; cout<<endl; }
目标函数,也称代价函数(cost function),用来 衡量每个可行解的优劣。使目标函数取最大(或最 小)值的可行解为问题的最优解。 状态空间树(state space)是描述问题解空间的树 形结构。树中每个结点称为一个问题状态 (problem state)。如果从根到树中某个状态的路 径代表一个作为候选解的元组,则称该状态为解状 态(solution state)。如果从根到某个解状态的路 径代表一个作为可行解的元组,则称该解状态为答 案状态(answer state)。
void NQueens(int k,int n,int *x) { for (int i=0; i<n;i++) { if (Place(k,i,x)) { x[k]=i; if (k==n-1) { for(i=0;i<n;i++)cout<<x[i]<<" "; cout<<endl; } else NQueens(k+1,n,x); } } }
例 设有n=6个正数的集合W=(5,10,12,13,15,18)和整数 M=30,求W的所有元素之和为M的子集。
使用剪枝函数的深度优先生成状态空间树中结点 的求解方法称为回溯法(backtracking);广度优 先生成结点,并使用剪枝函数的方法称为分枝限界 法(branch-and-bound)。
【程序1】递归回溯法 Void RBacktrack(int k) {//应以Rbacktrack(0)调用本函数 for (每个x[k],使得 x[k]T(x[0],,x[k-1]) &&(Bk(x[0],,x[k])){ if ( (x[0],x[1],,x[k])是一个可行解) 输出 (x[0],x[1],,x[k]); RBacktrack(k+1); } }
2 n-皇后
2.1 问题描述
n-皇后问题要求在一个nn的棋盘上放置n个皇后, 使得它们彼此不受“攻击”。 n-皇后问题要求寻找 在棋盘上放置这n个皇后的方案,使得它们中任何两
个都不在同一行、同一列或同一斜线上。
2.2 回溯法求解
皇后问题 可用n-元组(x0, x1,…, xn1)表示n-皇后问题的解, 其中xi表示第i行的皇后所处的列号(0≤xi<n)。 显式约束的一种观点是:Si={0, 1, …, n1},0≤i<n。 相应的隐式约束为:对任意0≤i, j<n,当ij时,xixj 且。此时的解空间大小为nn。 另一种显式约束的观点是:Si={0, 1, …, n1},0≤i <n,且xi xj(0≤i, j<n, ij)。相应的隐式约束为: 对任意0≤i, j<n,当ij时,。与此相对应的解空间 大小为n!。
3.2 回溯法求解
解结构形式:可变长度元组和固定长度元组。 可变长度元组(x0,,xk1,xk ),0k<n。元组的 每个分量的取值可以是元素值,也可以是选入子集 的正数的下标。 固定长度n-元组(x0,x1,,xn1),xi{0,1}, 0i<n。 xi=0,表示wi未选入子集,xi=1,表示wi入选子集。
【程序2】 迭代回溯法 Void IBacktrack(int n) { int k=0; while (k>=0){ if (还剩下尚未检测的x[k],使得x[k] T(x[0],,x[k-1]) && Bk(x[0],,x[k]){ if ( (x[0],x[1],,x[k])是一个可行解) 输出(x[0],x[1],,x[k]); k++; } else k--; } }
else if (s+w[k]+w[k+1]<=m) SumOfSub(s+w[k],k+1,r-w[k],x,m,w); if ((s+r-w[k]>=m)&&(s+w[k+1]<=m)) { x[k]=0; SumOfSub(s,k+1,r-w[k],x,m,w); } } void SumOfSub (int* x,int n,float m,float* w) { float r=0; for(int i=0;i<n;i++) r=r+w[i]; if(r>=m && w[0]<=m) SumOfSub(0,0,r,x,m,w); }
称这种从n个元素的集合中找出满足某些性质的 子集的状态空间树为子集树。子集树有2n个解状态, 遍历子集树的时间为Ω(2n)。
约束函数: Bk(x0,x1,,xk) 为true,当且仅当
k 1
wi xi wi M
i 0 i k
n1
且 wi xi wk M
i 0
1.2剪枝函数和回溯法
为了提高搜索效率,在搜索过程中使用约束函数 (constraint function),可以避免无谓地搜索那些 已知不含答案状态的子树。如果是最优化问题,还 可使用限界函数(bound function)剪去那些不可 能包含最优答案结点的子树。约束函数和限界函数 的目的是相同的,都为了剪去不必要搜索的子树, 减少问题求解所需实际生成的状态结点数,它们统 称为剪枝函数(pruning function)。
相关主题