用oo方法求解24点牌问题

  1. 算法思路 
  2. 计算24点,可以抽象描述为:求代数系统<Real;+,-,*,/>的子系统<SInteger;+,-,*,/>的所有运算结果为24的运算。一般情况下<SInteger;+,-,*,/>有很多性质,如交换律,i+j=j+i,结合律,i+(j+k)=(i+j)+k等等,为了使我这个惰人写代码方便,我去掉所有规律(这样使运算量加几倍了,呵呵,相信可以用来烤机了),并加上一个一元运算符~,~I=I I属于SInteger,再加上一个似乎破坏了数学完美性的规则,SInteger中的元素在一个运算中只能用一次,如果不加这个"所有运算"将会是一个无限集.
  3. 第三代语言不能直接操作集合,所以要作一下转化,想办法把作用在代数系统<{a,b,c};+,-,*,/>的所有运算(a+b*c,a-b*c,a+b/c,(a+b)*c........)用别的方法表示。
  4. 下面我用O表示二元运算符,Oi表示第i个二元运算符。用[<{{a},{b,c}};Oi>]表示{aO1b,aO2b,aO3b,aO1c,aO2c,aO3c....},也就是返回作用在集合{a},{b,c}笛卡尔积上的运算结果。
  5. [<{a};~>]表示{a},也就是返回作用在集合上的运算结果。记得上面我去掉了很多性质吗,所以可能a+b不等于b+a,因此我要把a+b+c与c+a+b都要计算一次,而不是推出来.下面是用[<,; >]描述"作用在代数系统<{a,b,c};+,-,*,/>的所有运算".
  6. [<[<{a};~>],[<[<{b};~>],[<{c};~>];Oi>];Oi>]
  7. [<[<{a};~>],[<[<{c};~>],[<{b};~>];Oi>];Oi>]
  8. [<[<{b};~>],[<[<{a};~>],[<{c};~>];Oi>];Oi>]
  9. [<[<{b};~>],[<[<{c};~>],[<{a};~>];Oi>];Oi>]
  10. .........
  11. [<[<{a};~>],[<[<{b};~>],[<{c};~>];+,->];+,->]=
  12. [<[<{a};~>],[<{b},{c};+,->];+,->]=
  13. [<[<{a};~>],{(b+c),(b-c)};+,->]=
  14. [<{a},{(b+c),(b-c)};+,->]=
  15. {a+(b+c),a+(b-c),a-(b+c),a-(b-c)}
  16. .......................
  17. 是不是发现很多重复,可能我上面说的运算量加几倍了太保守了,最起码也是个代数级数式的增加.
  18. 经过一轮对数学世界的破坏,终于使问题对计算机来说简化了。
  19. 记得上一次上数学课都是两年前了,不知上面的内容表达得是否正确。
  20. 绘制类图 
  21. 上面很多地方提到集合,第三代语言操作集合的最有效方法当然是使用Itertor模式,所以先定义一个TIterator基类,以后的集合都是它的子类。
  22. 观察[<,; >],它由三部分组成(一元运算只有二部分),逗号左边,逗号右边,分号右边,其中逗号左右都是一个[<,; >],分号右边是运算符,现在把[<,; >]转化为一个类TNode.分号右边是一系列运算符,因此使用TOperatorIterator表示。
  23. 我是用[<,; >]描述"作用在代数系统<{a,b,c};+,-,*,/>的所有运算".观察上面,“代数系统<{a,b,c};+,-,*,/>的所有运算”是由多个[<,; >]的并集表示,因此又需要一个TIterator,命名为TTreeIterator.下面就是用ModelMaker 6.2绘制的类图
  24. 其中的TTreeBuilder是用来构建TNode的,如创建一个:[<[<{a};~>],[<[<{b};~>],[<{c};~>];Oi>];Oi>]
  25. IClientInterface是一个最小化的界面,大部分二次开发的人员都不会想与复杂的内部结构打交道。
  26. 绘制顺序图 
  27. 做到这里时我就想描述内部的工作流,活动图是一种不错的方法,考虑到RUP的开发方式(主要是我只会一点点UML),所以决定还是先画个顺序图,可是画完顺序图后,代码就写出来了,可能这个问题过于简单,有机会我想找个复杂的问题。顺序图中我用了一些不太标准的表示方法,用来描述函数的返回。
  28. (为了整体布局,我把图缩小了,有点失真,请用看图工具打开这张图)
  29. 这是一个取[<,; >]中所有计算结果的顺序图。Actor首先通知TNode重新开始(Resume),跟着取第一个运算的运算结果(Eventuate,{a+(b+c),a+(b-c),a-(b+c),a-(b-c)}中的a+(b+c) ),再用Evaluate评估是否还有运算式(当还有运算符时,代表还有运算式,当然首先要检查逗号两边的[<,; >],如果它们有表达式,哪一定就是有表达式存在,没必要看当前是否还有运算符),如果有评估为真,哪继续用Eventuate取结果。“继续”写起来容易,但画就难了,我在ModelMaker中找不到相应循环符号,所以用了一条竖线表示范围,用文本表示退出条件。
  30. 下面是我对着这个图写出来的怪怪代码:
  31. function TNode.Evaluate: Boolean;
  32. var
  33. LeftBool, RightBool: Boolean;
  34. begin
  35. Result:=false;
  36. if (FLeftChild=nil) And (FRightChild=nilthen
  37. begin
  38. Result:=not FOperatorIterator.IsDone;
  39. if Result then
  40. FOperatorIterator.Next;
  41. Exit;
  42. end;
  43. LeftBool:=FLeftChild.Evaluate;
  44. if LeftBool then
  45. begin
  46. Result:=true;
  47. Exit;
  48. end;
  49. RightBool:=FRightChild.Evaluate;
  50. if Rightbool then
  51. begin
  52. FLeftChild.Resume;
  53. Result:=true;
  54. Exit;
  55. end;
  56. if not FOperatorIterator.IsDone then
  57. begin
  58. FOperatorIterator.Next;
  59. FLeftChild.Resume;
  60. FRightChild.Resume;
  61. Result:=true;
  62. Exit;
  63. end;
  64. end;
  65. 是不是感觉怪怪的。
  66. (FLeftChild=nil) And (FRightChild=nil)这句检测这是否一个只有一元运算符的[<; >]。
  67. 看到这里,大家应该明白它们之间的对应关系了吧
  68. "作用在代数系统<{a,b,c};+,-,*,/>的所有运算(别忘了加上一个破坏规则,)"是一个很大的集合,
  69. 分成多个子集,分别为
  70. +,-,*,/作用有元组(a,b,c)上
  71. +,-,*,/作用有元组(a,c,b)上
  72. +,-,*,/作用有元组(b,a,c)上
  73. .............
  74. 再从这些子集中把每个元素提取出来。
  75. +,-,*,/作用有元组(a,b,c)上用TNode表示,哪全集用TTreeIterator表示,下面就是与它相关的顺序图
  76. function TClientInterface.GetAnswer(aNumArr:IntArray): TStringList;
  77. var
  78. FResult:TStringList;
  79. TI: TTreeIterator;
  80. ND: TNode;
  81. Num:Double;
  82. function GetResult:TStringList;
  83. begin
  84. if not Assigned(FResult) then
  85. FResult:=TStringList.Create;
  86. Result:=FResult;
  87. end;
  88. begin
  89. { TODO -cMM : Interface wizard: Implement interface method }
  90. FResult:=nil;
  91. TI:=TTreeIterator.Create(aNumArr);
  92. TI.First;
  93. while true do
  94. begin
  95. ND:=TI.CurrentItem;
  96. ND.Resume;
  97. repeat
  98. try
  99. Num:=ND.Eventuate;
  100. if TCheck.Check(Num) then
  101. GetResult.Add(ND.Print+'='+FloatToStr(Num));
  102. except
  103. //捕捉零除异常 
  104. end;
  105. until (not ND.Evaluate);
  106. if TI.IsDone then Break;
  107. TI.Next ;
  108. end;
  109. Result:=FResult;
  110. end;
  111. Actor从TTreeIterator中取一个子集,再取子集的所有元素,取完元素后,再取一个子集一直循环下去。
  112. 可能大家看完一本UML书后,也找不到几个资源释放的字样,这应该是技术发展的走势,就像今天没人会考虑640的限制,所以我在代码里也没写这部分,我希望学习的是未来的技术。
  113. 使用ModelMaker的一些经验 
  114. 更改了源代码后一定要按以保持与Model的同步,如果忘记了可能会见到这个对话框 
  115. 这时最好按NO,如是按了YES,可能你在Delphi中辛苦写的代码就被删了。如果觉得经常用Refresh in Model很烦,可以在下面这里保持同步
  116. 2.这应该是一个BUG
  117. ModelMaker中的组合与聚合关系竟然是一样的线,要注意一下,别理解错误了.
  118. 3.又是一个BUG
  119. ModelMaker中的Singleton模式竟然产生错误代码,我本来想在TMonitor使用这个模式。
  120. class function TForm1.AccessInstance(Request: Integer): TForm1;
  121. const FInstance: TForm1 = nil;
  122. begin
  123. case Request of
  124. 0 : ;
  125. 1 : if not Assigned(FInstance) then FInstance := CreateInstance;
  126. 2 : FInstance := nil;
  127. else
  128. raise Exception.CreateFmt('Illegal request %d in AccessInstance'
  129. [Request]);
  130. end;
  131. Result := FInstance;
  132. end;
  133. ModelMaker带的设计模式不多,也是一大缺陷。Pascal语言似乎没意增加<Template>,操作符重载这两个有用的特性,哪设计模式的使用量相信将会增加,ModelMake应该增强这一点。
  134. 文章很多地方带有个人观点,有些描述也不知是否标准化,作者也找不到标准化的例子,希望大家只须了解作者的思想,不必要管哪些表示方法。
  135. 源码下载
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值