引言
前几天看了LIVE写的两篇关于骑士巡游算法的文章(我的马的遍历代码[Teaks & xgluxv] 以及马的遍历进阶版--骑士巡游(添加了评估函数)[Teaks & xgluxv & r]),感觉其中C#版本的算法实现还有可以改进的地方,所以乘周末有空的时候,做了几个版本来验证一下C#在需要速度的时候是否真的差很多。
 
版本一
HamiltonCS:点击下载源文件
算法上在保证遍历路线不变的情况下,做了小小的优化。更主要的改进是使用了指针访问,代替了原有的数组。减少了装箱以及拆箱的操作。

版本二
KnightTour:点击下载源文件
在版本一的基础上又做了改进。棋盘遍历标志的访问直接使用指针。同时修改版本一中骑士存储的方式,改存偏移量索引为访问地址。

版本三
KnightTourPlus:点击下载源文件
在算法上做了优化,所以说在执行时间上,与原有的版本没有什么可比性。
算法优化思路如下:
作为骑士巡游路线,只要搜索到一条,那么把该路线第2点与第64点以及之间的点的次序颠倒,则必然存在另外一条巡游路线。所以在计数时,可以每次累加2。为了去除重复计数的部分,首先先获取第2点可能点的序列(即所有的NextKnight)。在判断是否是巡游路线时,只要判断结束点是否是当前遍历路线第2点以后的点即可。此算法和LIVE使用的优化一样,只是做到了局部优化,对于获取前N条路线时可以大大提高效率。

测试结果
版本
巡游次数
遍历次数
时间(毫秒)
时间比
CppHamilton(C#)

100000

1530348

146391

1

Hamilton(C#)

100000

1530348

418031

2.85

HamiltonCS(C#)

100000

1530348

157453

1.08

KnightTour(C#)

100000

1530348

95437

0.65

KnightTourPlus(C#)

100000

737861

33796

0.23


注:测试使用的版本均为Release版本。

总结
从测试结果不难看出,相同的算法,在使用C#的情况下,也可以得到比较理想的速度。但我不得不承认,为此我也大大增加了编码的时间,同时牺牲了大部分C#的特性作为代价。

我写这篇文章其实也只是为了证明C#并不是那么弱,当你需要执行效率的时候,它也可以做到这点。掌握好编码效率和执行效率的平衡点才是王道。

套用一下《霍元甲》主题曲中的一段作为结束语:“我以为,世上的武术确实没有高低之分,只有习武的人才有强弱之别,通过竞技我们可以认识一个真正的自己,因为我们真正的对手,可能就是我们自己。”