递归算法与两个经典问题:汉诺塔问题和八皇后问题

函数在其函数体里又包含对其自身的调用,称为递归。例如阶乘函数f(n)就可以用递归表示为

int f(int n){
    if(n==0){
        return 1;
    }
    else{
        return n*f(n-1)
    }
}

而其非递归表示为

int g(int n){
    int s=1; 
    if(n==0){
        return 1;
    }
    else{
        for(int i=1;i<=n,i++){
            s=s*i;
        }
        return s;
    }
}

可见非递归表示要复杂一些,对于更为复杂的问题,则更是如此,如以下所述的汉诺塔问题。

传说在世界中心贝拿勒斯(在印度北部)的圣庙里,一块黄铜板上插着三根宝石针。印度教的主神梵天在创造世界的时候,在其中一根针上从下到上地穿好了由大到小的64片金片,这就是所谓的汉诺塔。不论白天黑夜,总有一个僧侣在按照下面的法则移动这些金片:一次只移动一片,不管在哪根针上,小片必须在大片上面。僧侣们预言,当所有的金片都从梵天穿好的那根针上移到另外一根针上时,世界就将在一声霹雳中消灭。其实,如果每秒移动一块金片,移完这些金片需要5845.54亿年以上,而地球存在至今不过45亿年,太阳系的预期寿命据说也就是数百亿年。真的过了5845.54亿年,不说太阳系和银河系,至少地球上的一切生命,连同梵塔、庙宇等,都早已经灰飞烟灭。我们现在通过输入金片数m(如m=3),希望得到解决方案,三根柱子分别用A,B,C表示,其中A是原柱,C是目标柱,B是辅助柱。递归实现:

void hanoi(int n ,char x,char y,char z){
    if(n==1){
        cout<"<"<>m;
    hanoi(m,'A','B','C');
}
非递归实现(主函数与递归实现相同,这里略去)

DEFINE MAXSTACK 100;
struct{
    int n,returnadd;
    char x,y,z;
}H;
struct{
    int top;
    H item[MAXSTACK];
}HStack;
void commonhanoi(int n,char A,char B,char C){
    HStack s;
    H cur;
    int i;
    IniStack(s);//初始化栈的函数具体实现略,以下进栈、出栈、GetTop、swap函数也同样;
    cur.n=n;
    cur.x=A;
    cur.y=B;
    cur.z=C;
    cur.returnadd=1;
    push(s,cur);
    start:
    cur=GetTop(s);
    if(cur.n==1){
        cout<"<"<
可见非递归实现要复杂得多,而且我们还省略了一系列函数的具体实现。

汉诺塔问题是一个NP(nondeterministic polynomial-time,非确定型多项式时间)问题。确定型机器是指,根据其某时刻执行的指令,可以确定其接下来执行的指令,而非确定型机器——这一模型——对下一步是有选择的,如果这些后面的指令中有一条通向正确的解,那么它将总是选择这一正确的步骤。NP完全问题是NP问题的一个子集,定义是,任意NP问题A的任何实例,都可以经(多项式)变换为某一NP完全问题B的一个实例,可以通过求解B然后将答案映射回原始的解答来求解A。

旅行推销员问题是一个NP(nondeterministic polynomial-time,非确定型多项式时间)完全问题,

阿德勒曼利用DNA计算解决“旅行推销员”问题——为一个在N个城市间奔忙的推销员找到一条最优路线——使之每个城市都能到,但又不能超过一次——的步骤是:

为每一个城市创建一个小号码的DNA链;

用多聚酶链式反应(PCR)对这些DNA链进行许多次复制;

把上一步复制所得的所有DNA链放入一支试管,由于DNA连接酶的作用,这些DNA链会随机组合连接为许多条长链;

利用胞质基因“消灭”那些首尾两部分不是开头和结束两个城市的链,然后运用PCR对剩下的链进行复制;

利用酶反应消除路式中包含的城市超过N的DNA链;

利用酶反应消除路式中不包含第一个城市的DNA链,然后对第二个到第N个城市重复这一操作;

此时剩下的链就是正确答案,运用PCR技术对其进行复制,运用电泳技术“解读”正确链中的DNA顺序,其结果就是各城市的正确排序。

这一思想与遗传算法是异曲同工的。所谓遗传算法(genetic algorithm)是指如下过程:

1.开始时随机生成一组字符串(或一组程序);

2.利用这些程序进行计算,筛选出其中效用最优者;

3.将效用最优者进行配对,代代“繁衍”,直到数量达到最大(maxsize);

4.返回步骤2,重复执行。

八皇后问题是指,在8×8格的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法。递归实现如下:

#include
using namespace std;
/*说明数组Octqueen[8],Octqueen[i]表示第i行皇后要摆放的列位置,m表示方案数(正确答案为m=92)*/
static int OctQueen[8] = { 0 }, m = 0;
bool check(int x, int y){
	/*检查是否存在有多个皇后在同一行或同一“/”对角线或同一“\”对角线的情况,x和y分别表示行和列*/
	for (int i = 0; i < x; i++){
		int j = OctQueen[i];
		if ((y == j)||((i + j) == (x + y))||((i - j) == (x - y))){
			return false;
		}
	}
		return true;
}
void count(int i){
	int x;
	for (x = 0; x < 8; x++)
	{
		if (check(i, x))
		{
			OctQueen[i] = x;
			if (i==7){
				m++;
				OctQueen[i] = 0;
				return;
			}
			count(i + 1);
			OctQueen[i] = 0;
		}
	}
}
int main(int argc, char*argv[]){
	count(0);
	cout << m << endl;
	system("pause");
	return 0;
}

你可能感兴趣的:(C++)