算法思想:维护而非重新计算

本文介绍了一种优化算法的设计思想——维护而非重新计算,并通过两道实例题目展示了这一思想的应用,分别是字符串映射匹配问题及N皇后问题的首选梯度上升法优化。

算法思想:维护而非重新计算

一、引入

先介绍一道简单的题目:

给你两个同样长度的序列,请你判断通过一个映射,是否可以将第一个序列转化成第二个序列。序列中的每个元素都是一个大写字母。例如ADDDA可以以A->C,D->F的映射转化为CFFFC;但是ABC无论如何也转化不成ENE

看到这道题,直觉算法是这样的:对序列1中的元素遍历,找到序列1中该元素后面与该元素一样的所有位置,测试序列2中同样位置的元素是不是也都相同。

这样的算法毫无疑问是可以完成任务的,但是其复杂度为O(n^2),有很多元素都被重复计算过多遍。于是我们应该怎样改进算法呢?这就要用到维护而非重新计算的思想了。当经过序列1中某个元素第一次出现的位置时,我们能知道这时序列1中的元素为X,序列2中的元素为Y。如果这两个序列之间存在映射变换关系的话,X->Y是必定存在的映射,于是我们把这个映射记录下来。遇到序列1中又一次出现X时,我们对X进行映射,看序列2中该位置是不是Y。如果是,则继续;如果不是,则该序列必不能匹配。

bool check(char* fir,char* sec,int size)
{
int map[26];
for(int i=0;i<26;++i)
	map[i]=INT_MIN;
for(int i=0;i<size;++i)
{
	if(map[sec[i]-'A']==INT_MIN)
		map[sec[i]-'A']=fir[i]-sec[i];
else
if(fir[i]-sec[i]- map[sec[i]-'A']!=0)
			return false;
}
return true;
}

这样算法的复杂度就成了O(n),是一个很大的突破。


二、对首选梯度上升法的改进

为什么要介绍这种算法思想呢?因为它对上一章讲过的梯度上升法可以有很大的改进作用。上一章提到evaluate函数是算法的瓶颈之一,而我们就用维护而非重新计算的思想改进它。

calculate函数中,为了找到更优化的棋局,我们对每一个改变过的皇后调用一次evaluate函数,而每次evaluate函数都是重新计算冲突数,这样就浪费了很多信息。假设我们已知第i列的皇后在位置j时,评估值为N,有没有办法利用这些信息,更快地计算出这个皇后移到位置k时的评估值M呢?有办法的。

我们先计算出皇后i在位置j时,与这个皇后冲突的其它皇后的数量f(i,j),这需要对所有皇后的位置过一遍,时间复杂度为O(n)。在总的评估值中减去这个数,即为这个列没有皇后时的冲突数。同理可得,计算皇后i在位置k时由它引起的冲突数f(i,k),即为这个列没有皇后时的冲突数。因此,M=N-f(i,j)+f(i,k),这样我们就得到了新的评估值。而这个evaluate操作的时间复杂度也就从O(n*n)变为了On+n)。

以下是改进过的首选梯度上升法:

#include<iostream>
#include<ctime>
using namespace std;
class queen
{
private:
	int size;
	int eva_now;
	int *list;
	void init()
	{
		for(int i=0;i<size;++i)
			list[i]=rand()%size;
		eva_now=evaluate(list);
	}
	int eva_by_x(int *li,int minx,int miny)
	{
		int eva=0;
		for(int x=0;x<size;++x)
		{
			if(x==minx)
				continue;
			else if(li[x]==miny)
				eva++;
			else if(abs(miny-li[x])==abs(minx-x))
				eva++;
		}
		return eva;
	}
	int evaluate(int *li)
	{
		int eva=0,minx,miny,x;
		for(minx=0;minx<size;++minx)
		{
			miny=li[minx];
			for(x=minx+1;x<size;++x)
			{
				if(x==minx)
					continue;
				else if(li[x]==miny)
					eva++;
				else if(abs(miny-li[x])==abs(minx-x))
					eva++;
			}
		}
		return eva;
	}
	bool calculate()
	{
		if(eva_now==0)
			return true;
		int x,y,recoardy,recoardeva=eva_now;
		int tmp;
		int course_x=0;
		bool change=false;
		for(x=0;x<size;++x)
		{
			recoardy=list[x];
			course_x=eva_by_x(list,x,list[x]);
			for(y=0;y<size;++y)
			{
				if(y==recoardy)
					continue;
				else 
				{
					tmp=eva_by_x(list,x,y);
					if(tmp<course_x)
					{
						list[x]=y;
						eva_now=eva_now-course_x+tmp;
						return true;
					}
				}
			}
		}
		return false;
	}
public:
	void output()
	{
		for(int i=0;i<size;++i)
			cout<<list[i]<<endl;
	}
	bool start()
	{
		if(size==-1)
			return false;
		for(int i=0;i<1000000;++i)
		{
			if(eva_now==0)
				return true;
			else
			{
				if(calculate())
					continue;
				else
					init();
			}
		}
		return false;
	}
	bool start(int &stp,int &res)
	{
		stp=0;
		res=0;
		if(size==-1)
			return false;
		for(int i=0;i<100000;++i)
		{
			stp++;
			if(eva_now==0)
				return true;
			else
			{
				if(calculate())
					continue;
				else
				{
					init();
					res++;
				}
			}	
		}
		return false;
	}
	queen()
	{
		srand(clock());
		size=-1;
		list=NULL;
	}
	bool setsize(int n)
	{
		if(size!=-1)
			delete[] list;
		list=new int[n];
		if(!list)
			return false;
		size=n;
		init();
	}
	queen(int _size):size(_size)
	{
		srand(clock());
		list=new int[size];
		init();

	}
	~queen()
	{
		if(size!=-1)
			delete[] list;
	}
};

以下是这个算法在我的破笔记本电脑上的运行结果:

10 queens : 0ms , used 12 steps, reset 0 times
11 queens : 4ms , used 128 steps, reset 13 times
12 queens : 1ms , used 23 steps, reset 2 times
13 queens : 4ms , used 70 steps, reset 6 times
14 queens : 4ms , used 63 steps, reset 4 times
15 queens : 14ms , used 210 steps, reset 17 times
16 queens : 91ms , used 1035 steps, reset 73 times
17 queens : 6ms , used 62 steps, reset 3 times
18 queens : 171ms , used 1656 steps, reset 96 times
19 queens : 272ms , used 2401 steps, reset 141 times
20 queens : 232ms , used 1765 steps, reset 97 times
21 queens : 238ms , used 1513 steps, reset 82 times
22 queens : 75ms , used 428 steps, reset 21 times
23 queens : 75ms , used 379 steps, reset 16 times
24 queens : 5ms , used 20 steps, reset 0 times
25 queens : 160ms , used 645 steps, reset 26 times
26 queens : 317ms , used 1072 steps, reset 42 times
27 queens : 52ms , used 153 steps, reset 5 times
28 queens : 186ms , used 498 steps, reset 18 times
29 queens : 1305ms , used 3287 steps, reset 120 times
30 queens : 186ms , used 404 steps, reset 13 times
31 queens : 360ms , used 679 steps, reset 23 times
32 queens : 2173ms , used 5210 steps, reset 176 times
33 queens : 1241ms , used 3015 steps, reset 97 times
34 queens : 220ms , used 383 steps, reset 11 times
35 queens : 2176ms , used 3308 steps, reset 100 times
36 queens : 1363ms , used 2037 steps, reset 61 times
37 queens : 745ms , used 1015 steps, reset 27 times
38 queens : 8228ms , used 10018 steps, reset 280 times
39 queens : 282ms , used 315 steps, reset 7 times
40 queens : 651ms , used 742 steps, reset 18 times
41 queens : 1600ms , used 1518 steps, reset 38 times
42 queens : 675ms , used 628 steps, reset 14 times
43 queens : 6719ms , used 5810 steps, reset 142 times
44 queens : 4395ms , used 3569 steps, reset 85 times
45 queens : 3283ms , used 2452 steps, reset 55 times
46 queens : 11724ms , used 8237 steps, reset 186 times
47 queens : 3918ms , used 2589 steps, reset 58 times
48 queens : 1404ms , used 849 steps, reset 17 times
49 queens : 5317ms , used 3187 steps, reset 66 times
50 queens : 3786ms , used 2059 steps, reset 41 times
51 queens : 8266ms , used 4349 steps, reset 87 times
52 queens : 1950ms , used 954 steps, reset 18 times
53 queens : 23956ms , used 11443 steps, reset 222 times
54 queens : 2028ms , used 1078 steps, reset 19 times
55 queens : 705ms , used 386 steps, reset 6 times
56 queens : 8508ms , used 3983 steps, reset 72 times
57 queens : 1305ms , used 489 steps, reset 8 times
58 queens : 12473ms , used 4565 steps, reset 80 times
59 queens : 1989ms , used 681 steps, reset 11 times
60 queens : 91447ms , used 30809 steps, reset 525 times
61 queens : 5207ms , used 2178 steps, reset 35 times
62 queens : 32519ms , used 10533 steps, reset 172 times
63 queens : 7421ms , used 2106 steps, reset 32 times
64 queens : 18309ms , used 5395 steps, reset 84 times
65 queens : 35677ms , used 9269 steps, reset 147 times
66 queens : 10190ms , used 2745 steps, reset 41 times
67 queens : 15633ms , used 3731 steps, reset 56 times
68 queens : 2703ms , used 612 steps, reset 8 times
69 queens : 15742ms , used 3464 steps, reset 50 times
70 queens : 2843ms , used 633 steps, reset 8 times
71 queens : 16286ms , used 3328 steps, reset 46 times
72 queens : 31098ms , used 6537 steps, reset 92 times
73 queens : 184579ms , used 36221 steps, reset 500 times
74 queens : 78013ms , used 14455 steps, reset 197 times
75 queens : 27006ms , used 5446 steps, reset 71 times
76 queens : 29666ms , used 6900 steps, reset 90 times
77 queens : 20713ms , used 3276 steps, reset 41 times
78 queens : 6867ms , used 1419 steps, reset 17 times
79 queens : 23483ms , used 3491 steps, reset 43 times
80 queens : 24183ms , used 3454 steps, reset 42 times
81 queens : 10245ms , used 1395 steps, reset 17 times
82 queens : 144705ms , used 20220 steps, reset 246 times
83 queens : 98812ms , used 12632 steps, reset 151 times
84 queens : 71014ms , used 8723 steps, reset 102 times
85 queens : 6961ms , used 829 steps, reset 9 times
86 queens : 5553ms , used 607 steps, reset 6 times
87 queens : 33738ms , used 3781 steps, reset 42 times
88 queens : 40079ms , used 4293 steps, reset 47 times
89 queens : 91678ms , used 10334 steps, reset 115 times
90 queens : 204185ms , used 22628 steps, reset 250 times
91 queens : 101065ms , used 10678 steps, reset 117 times
92 queens : 153650ms , used 14632 steps, reset 159 times
93 queens : 74092ms , used 7643 steps, reset 81 times
94 queens : 128694ms , used 12199 steps, reset 130 times
95 queens : 301043ms , used 26924 steps, reset 280 times
96 queens : 158951ms , used 13989 steps, reset 145 times
97 queens : 195779ms , used 15854 steps, reset 163 times
98 queens : 132589ms , used 11876 steps, reset 120 times
99 queens : 147190ms , used 12100 steps, reset 121 times
100 queens : 10394ms , used 851 steps, reset 7 times


可以看到,效率有了很大的提高。但是仍然不堪大用,可以看到这有陷入局部极值次数太多的原因。如果采用模拟退火算法,可以有更好的效果。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值