彳亍 发表于 2006-2-13 00:14:01

找到一个vb求解魔方的程序,含源码

<P><a href="http://ai.yt123.net/showbbs.asp?bd=2&amp;id=84&amp;totable=1" target="_blank" >http://ai.yt123.net/showbbs.asp?bd=2&amp;id=84&amp;totable=1</A></P>
<P><STRONG>[下载]</STRONG><a href="http://ai.yt123.net/upload/mofang.rar" target="_blank" >魔方解法VB源代码</A></P>
<P><STRONG>魔方解法VB源程序</STRONG> </P>
<P>有编译好的程序和源代码</P>
<P>使用方法<br>(1) 如果您的魔方各个面的颜色与默认值不同,可以通过单击菜单<br>        中“魔方配置”项,选择输入您魔方的6面基本颜色   <br>  (2) 单击“填充颜色”按钮,在出现的颜色条中选中颜色,然后单<br>    击需改变的小面以改变其颜色<br>  (3) 确认输入颜色无误后,单击“求解”按钮,稍后在右边的列表<br>    中将出现解法的各个步骤<br>  (4) 各步骤由旋转面和旋转方向组成,例如“黄面逆”表示将黄色<br>    (中心块颜色)面逆时针旋转一下(90度),“白面2”表 <br>    示将白色面顺时针(或逆时针)旋转两下(180度),而  <br>    “黄面顺”表示将黄色面顺时针旋转一下(90度) <br>  (5) 在步骤列表中单击某步可将魔方自动开解到相应的步骤,此功<br>    能主要是为了核对开解过程是否有误操作,也可以通过键盘的<br>       上下方向键进行操作                                <br>  (6) “随机旋转”按钮主要是为了在计算机上模拟魔方的随机旋转<br>        而设;为了进行指定面的旋转,可以选中菜单项中的“指定<br>        旋转”,利用此功能,您完全可以在计算机上玩魔方。其中    <br>        左键单击为顺时针旋转,右键单击为逆时针旋转              <br>    (7) 用鼠标指向立体的魔方,按住左键可对魔方进行各种旋转     <br>  (8) 若在开解中途您有他事要做,可选择菜单中“保存退出”项,<br>    在下次启动本程序时将恢复中断时的情景 <br>  (9) 若程序无法开解魔方,请检查颜色输入是否有误;若无误,请<br>    回忆是否有朋友拆解过您的魔方;</P>
<P>关于魔方<br>      小小方块     魔力无穷<br>  1974年,富于创造力的匈牙利37岁的<br>建筑学教授鲁毕克,为了给自己的学生增加立体<br>感,发明了一种迅速风靡全球的有趣玩具——魔<br>方。<br>  这个小小的立方体由6个可活动的面组成,<br>其变化共有4325200327448985<br>6000种,要想开解一个被打乱的魔方,绝非<br>易事。<br>           魔方解法<br>  伟大的美国化学研究员詹姆士.诺尔斯经过<br>四个半小时的努力,终于战胜了它。写成了《鲁<br>毕克魔方解法》一书。<br>  本程序便是受诺尔斯解法的启发编制而成的<br>,望各位朋友在计算机的帮助下顺利开解魔方,<br>并且通过总结规律自己开解这个小玩意。<br>  要想令它失去魔力,请你征服它!</P>

<P><a href="http://ai.yt123.net/upload/mofang.rar" target="_blank" >魔方解法VB源代码</A><br></P>
[此贴子已经被作者于2006-2-13 0:19:27编辑过]

彳亍 发表于 2006-2-13 00:23:48

<P>另外一个。</P>
<P><a href="http://coo.hsfz.net/prolog/bcpcube1.htm" target="_blank" >http://coo.hsfz.net/prolog/bcpcube1.htm</A></P>
<P>不会用prolog,转过来作为资料吧。</P>
<P>算法 、 程序都有详细介绍。</P>

彳亍 发表于 2006-2-13 00:25:45

<P><B>解决魔方问题 </B></P>
<P>本章只是就解魔方的编程方法作扼要介绍,具体的程序将在下一章进行分析。</P>
<P><B>简介</B></P>
<P>本文将介绍如何使用PROLOG语言解决魔方问题。在本程序中使用到的一些方法,将有助于解决建造专家系统时所遇到的一些问题。速度和效率是我们的所要考虑的重点。 </P>
<P>这个程序是一个经典的专家系统,他能够比人更快的解决魔方问题。然而它并不具有找到解决魔方的方法的能力。它只是简单的把人们解决魔方问题的经验汇集起来。所以编程者必须知道如何人工解决魔方问题。 </P>
<P>因为在魔方问题中状态图是相当大的,所以盲目搜索将不起作用。这时必须使用优化算法指导搜索。使用不同的优化方法,搜索空间将极大的减少,从而达到在有限时间内能够找到结果的程度。 这个问题和其它的人工智能问题一样,分为三个设计阶段。首先我们必须决定如何表示魔方的当前状态。然后还必须表示对魔方的操作。最后我们必须解决如何把这些操作运用到魔方之上。这三个方面在程序设计中都非常重要的。 </P>
<P>由于PROLOG语言的强大功能,在此我们选用它作为解决魔方问题的工具。在本文中将简要介绍编程的过程,PROLOG语言和其它语言一样也有优点和缺点,在这个例子中我们都将看到。 </P>
<P><B>魔方简介</B></P>
<P>魔方是一个看似简单的玩具,它的每个面有九个格。我们的目标是使每个面的格子的颜色都相同。每个格子实际上是小方块的一部分。小方块的每个面都可以旋转,角方块有三个面,边方块有两个面。</P>
<P>目标是把一个每面都是随机颜色的魔方还原成每面颜色都相同的魔方。问题是,有无数的方法操作魔方,然而只有极少的方法能够达到目的。使用盲目的搜索算法是不行的,即使在最大型的计算机是也要用上几年的时间。然而会玩的人在几分钟之内就可以搞定,很显然这不是使用的盲目搜索算法。解决魔方问题困难之一就是当你想移动一个方块时,不得不移动其它七个方块(中心的方块是不动的)。在求解的早期这不是大问题,但是当大部分的方块都到达正确位置时,新的旋转将会破坏已完成的部分。一些玩魔方的高手都知道许多复杂的旋转方法,这些方法能够只改变少数方块的位置而不影响其它的方块。例如本程序中一个十四步的方法能够只旋转两个角方块的方向。 </P>
<P><B>魔方的数据结构 </B></P>
<P>任何人工智能程序的核心都是如何表达知识。在这个程序中,核心就是如何表达魔方以及对它的操作。魔方本身的特点决定了有两种表达方式。我们既可以把魔方看作是54的分离的小面,也可以是26个小方块。其中6个方块只有一个面(即中间的方块),12个方块有两个面(即边方块),8个方块有3个面(即角方块)。由于本程序中的大多数智能都是基于小方块的位置和方向,所以使用小方块来表示魔方是有必要的。然而,我们的程序中使用了广度搜索,使用54个分离的小面的表示方法将能够提高搜索的速度,这是因为这种表达方法没有嵌套结构。</P>
<P>下面我们要决定是使用Prolog的复合结构表达魔方,还是使用列表来表达。如果使用列表表达,则有助于在魔方中搜寻某一个特定的小方块,但是当整体考虑时,它又会影响速度。使用复合结构虽然不容易搜寻魔方的某个部分,但是却能够有效地对魔方的整体进行操作。考虑到程序的复杂性以及效率,我们决定同时使用这两种数据结构。我们使用Prolog的复合结构来表达54个分离的小面,而用列表来表达26个方块。</P>
<P>下面是使用复合结构表达:(每个X变量表示一个小面)</P>
<BLOCKQUOTE>
<P><FONT color=#000099>cube(X1, X2, X3, X4, ........, X53, X54) </FONT></P></BLOCKQUOTE>
<P>下面是使用列表表达:(其中的X变量与上面的小面对应)</P>
<BLOCKQUOTE>
<P><FONT color=#000099> </FONT></P></BLOCKQUOTE>
<P>其中的p(...)表示一个方块。它们的参数数目有1、2、和3不等,这就是每个方块的可见面。只有一个参数的表示中心方块,2个参数的表示边方块,3个参数的则表示角方块。每一个小面都是使用的大写字母的变量表示的,这表示每个小面的数据(颜色)是不确定的。我们给每个面一个常数来定义最终的状态,在本程序中使用ghoul谓词来储存最终的魔方状态。</P>
<P><FONT color=#000099>ghoul( cube('F', 'R', 'U', 'B', ............)) </FONT></P>
<P>注意使用引号引起来的都是常数,F表示front,B表示back,如此类推。此处我们没有使用颜色,不过这并不影响解题。</P>
<P><B>对魔方的操作</B></P>
<P>当决定了使用这两种方式表达魔方后,我们必须编写这两种数据结构的转换程序。Prolog强大的联合功能可以很好的完成这个任务。下面的谓词pieces可以进行这种转换,它的编写方法很简单:</P>
<BLOCKQUOTE>
<P><FONT color=#000099>pieces( cube(X1, X2, .......X54), ). </FONT></P></BLOCKQUOTE>
<P>如果变量Z保存的是魔方的各小面的数据,下面的询问则可以轻松地转换为各小方块的数据:</P>
<BLOCKQUOTE>
<P><FONT color=#000099>?- pieces(Z,Y). </FONT></P></BLOCKQUOTE>
<P>变量Y将绑定为各小方块的数据,反过来也是一样。下面的询问可以把最终目标的方块数据表示求出来。(我们只定义了最终状态的小面数据表示)</P>
<UL><PRE><FONT color=#000099>?- ghoul(FlatState), pieces(FlatState, PieceState).
  FlatState = cube('F', 'R', 'V', 'B', .....).
  PieceState = [p('F'), p('R'), .... p('R', 'U'),
       ......p('B', 'R', 'F'), ......].
</FONT></PRE></UL>
<P>第一个目标使变量FlatState绑定为我们定义的最终状态的小面数据表示。再调用pieces把小面数据转换为小方块数据。我们同样可以使用联合来完成对魔方的基本旋转。第一个参数为旋转的名称,第二个和第三个参数则表示了某个面顺时针旋转前后的两个状态。例如:上部分的旋转可以如下表示:</P>
<UL><PRE><FONT color=#000099>mov(u, cube(X1, ...X6, X7, X8, X9, ...),
cube(X1, ...X6, X20, X19, X21, ...))
</FONT></PRE></UL>
<P>我们可以使用mov谓词来对最终的魔方进行一次旋转:</P>
<UL><PRE><FONT color=#000099>?- ghoul(State), mov(u, State, NewState).
</FONT></PRE></UL>
<P>变量NewState将绑定为魔方最终状态的上部顺时针旋转后的状态。当然逆时针旋转的功能也很容易实现,只要把NewState作为输入,则State将绑定为NewState逆时针旋转后的状态。下面的move谓词,通过调用谓词mov完成魔方的正反方向的旋转:</P>
<UL><PRE><FONT color=#000099>move(+M, OldState, NewState):-
  mov(M, OldState, NewState).
move(-M, OldState, NewState):-
  mov(M, NewState, OldState).</FONT>
</PRE></UL>
<P>到此为止我们已经编写出了魔方的基本旋转,下面来表达一些复杂的旋转序列。一般玩魔方的高手都记得许多固定的玩法,使用这些方法可以快速地解魔方。例如,有一种旋转序列可以只交换三个角方块,而其他的方块都不动。这种旋转序列使用列表是很容易表达的,下面就是只交换三个角方块的旋转序列:</P>
<UL><PRE><FONT color=#000099>seq(tc3, [+r, -u, -l, +u, -r, -u, +l, +u]).
</FONT></PRE></UL>
<P>我们使用尾递归谓词move_list来完成旋转序列的操作:</P>
<UL><PRE><FONT color=#000099>
move_list( [ ], X, X).
move_list( , X, Z):-
  move(Move, X, Y),
  move_list(T, Y, Z).
</FONT></PRE></UL>
<P>到此为止我们已经使用非常有效的方法表达了魔方以及对它的操作。下面我们需要加入指导搜索的启发式方法。</P>
<P><B>高级规则</B></P>
<P>最明显的解魔方的方法就是一步一步使各个小块到达它应该在的位置。我们在此把解魔方分为6个步骤,这6个步骤顺序从左到右来解魔方。例如其中的两个步骤是:“使左边的边方块到位”和“使右边的角方块到位”。我们把每一步需要完成的目标使用stage谓词储存起来。每个步骤都有特定的几个方块需要归位。这些信息则使用谓词pln贮存。它有两个参数,第一个是步骤的序列数,第二个是要归位的方块列表。例如步骤5是把又变得4个边方块归位,所以如下表达:</P>
<UL><PRE><FONT color=#000099>pln(5, [p('R', 'U'), p('F', 'R'),
        p('R', 'D'), p('B', 'R')]).</FONT>
</PRE></UL>
<P>而每一个步骤所使用的旋转序列是不同的。为了加快状态图的搜索,我们为每一个步骤都定义它自己所需要使用的旋转序列,在此我们使用谓词cnd来储存这些信息。</P>
<P>例如,第一阶段(左边的4个边方块)可以只使用右、上和前侧的基本旋转就可以完成。而最后的阶段(右边的4个角方块)就必须使用只交换角方块或者旋转角方块的复杂的旋转序列开能够完成。我们已经使用seq谓词为每种旋转序列起了名字。下面是这两个阶段使用的cnd子句:</P>
<UL><PRE><FONT color=#000099>
cnd(l, ).
cnd(6, ).
</FONT></PRE></UL>
<P>stage谓词则是进行每一阶段搜索的核心。它首先初始化某个阶段,包括这个阶段的目标和所使用的旋转序列。然后调用一系列的程序改进魔方的状态,直到达到目标。</P>
<P><B>优化状态</B></P>
<P>stage谓词调用搜索程序来达到目标,然后更新所有需要更新的数据。这一部分程序的核心是如何表达一个部分完成的魔方。如何让搜索程序知道某个小方块已经到达了它应该在的位置呢?本程序中,就有几个谓词专门完成在魔方的列表数据结构中搜索小方块的任务。它们能够确定某个方块的位置,或者某个位置上的方块。这些谓词在程序中的许多地方都很有用处,不过如果用它们来判断是否达到了某个阶段的目标,则会极大地影响速度。这很容易理解:因为每次都需要判断新的方块是否到位,并且已经到位的方块是否没有被移动。</P>
<P>为了解决这个问题,我们再次使用联合。迄今为止,我们的程序中已经有两个cube子句了。一个表示最终的魔方状态,一个表示当前的魔方状态。现在我们引入第三个cube子句,它是一种标准,用来确定哪些方块已经到位。</P>
<P>一开始这个标准cube的所有参数都是变量。所以标准cube可以与任何cube匹配。当小方块开始就位后,某些变量就与已经就位的方块绑定。在这种情况下,这个标准cube只能与那些有相同的小方块就位的cube匹配。例如,当程序试图归为第6个方块时,improve谓词首先把标准cube的第6个方块绑定为已归位的状态。这个时候前六个小方块应该都已经绑定为归位的状态了。而剩下的方块就使用未绑定的变量来表示。因此这个标准cube就可以与任何一个前六个方块已归位的状态匹配了。 </P>
<P><B>搜索</B></P>
<P>万事俱备,只差搜索了。本程序所使用的是广度搜索。搜索谓词为rotate:</P>
<UL><PRE><FONT color=#000066>
rotate([], State, State).
rotate(Moves, State, Crit):-
  rotate(PriorMoves, State, NextState),
  get_move(ThisMove, NextState, Crit),
  append(PriorMoves, , Moves).
</FONT></PRE></UL>
<P>变量Moves在调用时没有绑定,最后返回从State状态到Crit状态的旋转列表。State是当前的魔方状态,而Crit是某个阶段的目标状态。就是上一节中所介绍的标准Cube。因此rotate可以找到一系列的旋转方法使得在不破坏已归位的小方块的同时而把新的小方块归位。</P>
<P>这个广度搜索我们前面已经介绍过了,此处就不再多说。这里我们没有储存搜索的中间状态,也就是说会产生重复搜索的情况。不过由于搜索的深度并不深,所以这样做并不会影响速度。</P>
<P>每个阶段所可能使用的旋转方法储存在谓词cand中(真正的程序比这里的稍微复杂一下),例如阶段1的cand如下:</P>
<UL><PRE><FONT color=#000066>cand(r).
cand(u).
cand(f).
</FONT></PRE></UL>
<P>get_move谓词的功能是找到从State到Crit的旋转方法(它只寻找一步)。所以调用时Move变量是没有被绑定的。它的程序如下:</P>
<UL><PRE><FONT color=#000099>
get_move(+Move, State, Crit):-
  cand(Move),
  mov(Move, State, Crit).
get_move(-Move, State, Crit):-
  cand(Move),
  mov(Move, Crit, State).
</FONT></PRE></UL>
<P>这实际上是一种产生并校验的方法。使用cand找到可能的旋转方法,然后使用mov来判断它是否是所需要的旋转方法。由于Prolog内建的回溯功能,我们不必要关心具体的循环判断。它由两个子句构成,是因为魔方有正反两种操作方法。</P>
<P>由于广度搜索比较难于理解,所以你最好回顾一下前面介绍广度搜索的章节。</P>
<P><B>更多的优化</B></P>
<P>到此为止,我们的程序已经可以运行了。不过有几种情况会使得搜索的路径过长。这些情况破坏了程序的完美。</P>
<P>所以有必要加入更多的智能成分好让程序能够识别这些不易解的情况,并且在调用rotate之前就修正它们。在放置左边的方块时会经常遇到一个问题:如果要放置的方块在右边就很好办,可以通过几步就搞定,但是如果这些方块已经在左边,而位置不对, 就比较麻烦了,我们必须先把它们移到右边,然后再移到左边。这样搜索的路径就太长了,因此必须加入更多的优化。</P>
<P>优化程序首先分析方块,如果发现这个情况,就使用盲目的移动把方块移到右边。而后续的搜索又会把它还原到左边,并且位置也正确了。</P>
<P>为了减少搜索的工作量,我们可以引入许多优化方案。但是我们现在的程序已经能够很好的解决魔方问题了。只有当出现了搜索不出来的情况时,我们才有必要加入更多的优化,让计算机工作总比让人工作划的来吧!:-)</P>
<P>这一章只是简要地介绍了解魔方的方法,所提供的程序也是不完整的。下一章我们将来分析完整的程序。完整的程序很长,所以也只能分析主要的部分。(如果下一章不是介绍解魔方的程序,那就表示我还没有写完,请耐心等候吧)</P>

彳亍 发表于 2006-2-13 00:26:22

<P><a href="http://coo.hsfz.net/prolog/bcpcube2.htm" target="_blank" >http://coo.hsfz.net/prolog/bcpcube2.htm</A></P>
<P><B>魔方程序分析</B></P>
<P>上一章,简要地介绍了解魔方的算法。这一章里,我们将接触到完整的魔方程序,在你开始阅读本章之前,你必须确信你已经完全掌握了上一章的所有内容。在开始分析程序之前,你有必要先运行一下这个魔方程序。以了解它的大致框架。</P>
<P>首先下载prolog<a href="http://coo.hsfz.net/prolog/runner.zip" target="_blank" >运行器</A>,以及<a href="http://coo.hsfz.net/prolog/rubik.xpl" target="_blank" >魔方程序</A>, 把运行器解压后和魔方程序拷贝到同一个目录下,然后把文件yourname.exe改名为rubik.exe, 再运行它即可。</P>
<P>建议最好下载完整的Amzi! Prolog,它是在windows下运行,可以查看已经翻屏的信息,而如果使用此处下载的程序,就不能够看了。你也可以在这里下载一个<a href="http://coo.hsfz.net/prolog/a4idea.zip" target="_blank" >window运行器</A>,运行a4ideA.exe后,选择build/run,然后选择文件<a href="http://coo.hsfz.net/prolog/rubik.xpl" target="_blank" >rubik.xpl</A>即可。 </P>
<P>屏幕如下所示:</P>
<UL><PRE><FONT color=#000099>
Cube Solver II
An illustrative Prolog program from
Building Expert Systems in Prolog (Springer-Verlag) by Dennis Merritt
implemented in Amzi! Prolog

For more information contact:
Amzi! inc.
40 Samuel Prescott Dr.
Stow, MA 01775 USA
Tel 508/897-7332, FAX 508/897-2784
e-mail amzi@world.std.com

solve manual help exit
Choice: </FONT></PRE></UL>
<P>选择2则是手工玩魔方,选1则是电脑解决魔方。</P>
<P>由于Prolog没有图形功能,所以我们是使用文本方式来显示魔方的状态的,下图表示的是魔方的最终状态,其中的英文字母表示颜色,Y黄、O橙、W白、R红、G绿、B蓝。</P>
<UL><PRE><FONT color=#000099>
      Y Y Y
      Y Y Y
      Y Y Y
O O O W W W R R R
O O O W W W R R R
O O O W W W R R R
      G G G
      G G G
      G G G
      B B B
      B B B
      B B B


其中,每块对应的魔方的面如下,也就是说绿色的一面在已解状态下是对着用户的一面。


      后
    左上右
      前
      下
</FONT></PRE></UL>
<P><BR>为了使你更明白,我们把对魔方的基本旋转应用于上,请看下面的例子:</P>
<UL><PRE><FONT color=#000099>      Y Y Y
      Y Y Y
      Y Y Y
O O O W W W R R R
O O O W W W R R R
O O O W W W R R R
      G G G
      G G G
      G G G
      B B B
      B B B
      B B B
Moves: (对魔方的基本旋转操作)
u d r l f b
Rotations: (整个旋转魔方,即从不同的角度看魔方)
ru rr rf
Sequences: (一些复杂的旋转序列,每个序列都有特定的名字)
s tc1 tc1u2 tc3 ct1 ct3 ef1 ef2 et1 h g pt mr2a mr2b mr3a mr3b
Enter q. to end
Enter move
(end with period, ex. u., -l., ct1., -tc3.) : d.
(我们输入的是d.表示顺时针旋转魔方的低部的那个面)
      R R R
      Y Y Y
      Y Y Y
Y O O W W W R R G
Y O O W W W R R G
Y O O W W W R R G
      G G G
      G G G
      O O O
      B B B
      B B B
      B B B
Enter move
(end with period, ex. u., -l., ct1., -tc3.) : -d.
(输入-d.逆时针旋转,则魔方又会原了)
      Y Y Y
      Y Y Y
      Y Y Y
O O O W W W R R R
O O O W W W R R R
O O O W W W R R R
      G G G
      G G G
      G G G
      B B B
      B B B
      B B B
Enter move
(end with period, ex. u., -l., ct1., -tc3.) : ct1.
(使用旋转序列ct1,我们注意到它使前上方的两个小方块的方向变了)
      Y Y Y
      Y Y Y
      Y Y Y
O O O W W W R R R
O O O W W W R R R
O O W G W G W R R
      O G R
      G G G
      G G G
      B B B
      B B B
      B B B
Enter move
(end with period, ex. u., -l., ct1., -tc3.) :
</FONT></PRE></UL>
<P>对魔方的基本旋转是:up、down、left、right、front和back六个面的旋转,上面的udrlfb分别对应它们的开头字母。前面加上-号就是表示逆时针旋转。</P>
<P>下面这个文件是随机产生的一个魔方的状态的求解过程:</P>
<P><a href="http://coo.hsfz.net/prolog/rusolve.txt" target="_blank" >解魔方示例</A></P>
<P>如果你手头上有一个解不开的魔方,可是使用solve下的edit功能来输入你的魔方状态,让此程序帮你解决。输入的方法就和显示的一样,每个字母之间用空格个开,每行使用Enter键换行。</P>
<P>下面开始正式分析程序。</P>
<P> </P>
<P>(<a href="http://coo.hsfz.net/prolog/rubik.zip" target="_blank" >此处下载源文件</A>)</P>

gdmzpyzyj 发表于 2007-8-23 15:37:51

页: [1]
查看完整版本: 找到一个vb求解魔方的程序,含源码