First we try, then we trust

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

自从《华容道与数据结构》系列文章写完后,有很多热心的朋友提出了宝贵的意见,使得华容道程序的运行效率不断提升。在这里特别感谢"智能算法爱好者"与sumtec,给程序提出了很多建设性意见。

在《华容道与数据结构(续)》这部分内容中,我将从两个方面对原有程序进行改造,"以内存换效率",进一步提升华容道程序的执行效率。方法包括两方面:1、将原有4字节棋局表示变为8字节表示以省去原有程序中的排序操作。2、用HashTable取代原有的AVLTree,提升检索效率。在本部分内容中,先说说通过8字节棋局表示提高程序执行效率。

64位棋局表示,AVLTree版代码下载:http://www2.cnblogs.com/Files/zhenyulu/HRD_64_AVLTree.rar
64位棋局表示,HashTable版代码下载:http://www2.cnblogs.com/Files/zhenyulu/HRD_64_HashTable.rar


一、 8字节棋局表示

在前面的华容道设计中,我一直使用4字节棋局表示以减少内存空间占用。但这么做也牺牲了程序的执行效率。为了计算得到4字节棋局表示,需要对现有棋子执行"排序"操作(可以参考《华容道与数据结构 (7)》),即使使用快速排序法,但面对检索的几万节点仍然消耗了不少的CPU时间。提高效率的方法之一就是消除排序操作,这就意味着放弃4字节棋局表示,设计另外一个棋局表示方法。

新的设计使用8字节表示一个棋盘布局,共占用64个二进制位。在.net环境下可以使用一个Int64进行存储。棋盘布局的存储方式为:将棋盘分成4×5=20个单元格,分别标上序号0~19,每个棋子使用3个二进制位来描述。

public enum ChessmanType
{
  Blank 
= 0,
  General 
= 1,
  VChessman 
= 2,
  HChessman 
= 3,
  Solider 
= 4
}

例如,一个Gereral放在了序号为1的棋盘格上,那么它的物理存储为:
00000000-00000000-00000000-00000000-00000000-00000000-00000000-00001000

又如,两个棋子,一个Gereral放在了序号为1的棋盘格上,一个VChessman放在了序号为0的棋盘格上,那么它的物理存储为:
00000000-00000000-00000000-00000000-00000000-00000000-00000000-00001010

Int64 layout = 0;
Int64 chessman 
= 1;   // 二进制的 001,对应General
int positionIndex = 1;   // 在棋盘上放在序号为 1 的单元格中
layout += chessman << (positionIndex * 3);

左移量决定着棋子的坐标,而001决定棋子类型。程序自动忽略值为000的棋子。

下面的棋盘布局如果转换成Int64的化,就是:

 

Int64: 577606461035643914
0000100000000100000100100000010000011010000000000000010000001010
Pos:|19|18|17|16|15|14|13|12|11|10| 9| 8| 7| 6| 5| 4| 3| 2| 1| 0

这个棋盘布局的表述方法不需要预先对棋子排序,只需要根据棋子坐标左移合适的位移,然后相加就可以了。下面是Layout类中ToInt64的方法实现:

//===========================================================
// 将当前棋局转换为一整数
//===========================================================
public Int64 ToInt64()
{
  Int64 result 
= 0;
  Int64 typeCode, positionCode;
      
  
for(int i=0; i<10; i++)
  
{
    typeCode 
= (int)_chessmen[i].chessmanType;
    positionCode 
= _chessmen[i].position.y * 4 + _chessmen[i].position.x;

    result 
+= typeCode << ((int)(3 * positionCode));
  }


  
return result;
}

尽管8字节棋局表示比起4字节棋局表示多占用了一倍的内存空间,但我们可以省略了复杂的排序操作,更可以大幅度简化代码,因此牺牲一些内存空间还是很值得的。

修改完Layout类后,其它相关类也需要一并修改以支持64位棋局表示。主要包括:ChessStep类、AVLTree类、Mediator类等,由于修改工作比较简单,在这里就不再单独说明了。大家可以参考具体的程序代码。


二、 对解题效率的提升

经过修改后,程序的执行效率得到比较大的提升。原来程序中的第四个布局的解题时间从4字节表示的1.55秒缩短到了1秒,但内存的占用也进一步提升,毕竟一个棋盘布局使用8字节来描述。

程序优化到这里还有没有可能再进一步呢?答案当然是"有"。Sumtec指出:"AVLTree是否可以用HashTable来替代,毕竟你的目的不是要排序,而是要检验是否容易产生重复。从纯粹的理论上来说,HashTable在这方面的性能消耗应该比AVLTree要好一些,因为AVLTree的复杂度至少是O(Log2(x))级别的,而HashTable则接近于O(C)级别的。"引入HashTable将进一步降低检索重复布局时所需要的时间,进而使程序执行效率进一步提升。经过实验,使用HashTable检索重复布局比AVLTree确实要快。在我的机器上,程序中的第四个布局的解题时间可以从使用AVLTree的1秒钟时间再次缩短到使用HashTable的0.92秒。关于HashTable及其在华容道程序中的实现将在下一部分介绍。

 

posted on 2005-02-22 14:16  吕震宇  阅读(6117)  评论(21编辑  收藏  举报