您的位置:首页 > 运维架构

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

2015-09-13 12:29 225 查看

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

一、引入

先介绍一道简单的题目:

给你两个同样长度的序列,请你判断通过一个映射,是否可以将第一个序列转化成第二个序列。序列中的每个元素都是一个大写字母。例如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)变为了O(n+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
;
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

可以看到,效率有了很大的提高。但是仍然不堪大用,可以看到这有陷入局部极值次数太多的原因。如果采用模拟退火算法,可以有更好的效果。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  代码 初学者 算法