返回首页

下载演示应用程序 - 86 KB下载Java小程序源 - 67 KB简介
技术,解决困难的问题,使用一个计算系统之一, 是运用蛮力搜索。这意味着穷举搜索 所有可能的组合,直到找到一个解决方案。在本文中,我 将目前实施蛮力搜索算法,可以 适用于各种各样的问题。对这篇文章的想法是采取 由   ; 在CodeProject上的文章,其中提出,解决了一个C程序 爱因斯坦的谜。这篇文章的功能扩展, 为了利用面向对象编程的好处,并& #160; 产生一个通用的求解器类,可以从多个基类 特定问题的求解器类。许多问题的求解器类的例子 将提交​​,其中包括解决爱因斯坦的里德尔。
注意:执行的语言是C。虽然有些 销售先进的语言功能的使用(模板类,STL),。 在这篇文章中的技术细节都保持在最低限度,使连 经验不足的程序员可以按照蛮力的逻辑 算法,例如拼图的乐趣。
八皇后之谜
我将介绍蛮力搜索开始算法的思想 从八皇后之谜。这是众所周知的配售8问题 非攻击棋盘上的皇后。为解决这一难题的源代码 可以发现在许多软件书籍。在下载一节中,你会发现, 一个Java applet()实现了这个谜团。
计算系统可以帮助我们迅速解决这个谜团。此外,它   ; 可以帮助我们找到所有可能的解决方案。为了这样做,我们必须指示我​​们 8皇后棋盘上的所有可能的展示位置的程序来搜索。 有一个共64!63!62!61!60!59!58!57!不同 配售。尝试搜索所有这些,这将是非常低效 不同的组合。它要解决的问题要好得多 电感。这是首先要解决的问题放置一个皇后在的 一个的1x8的棋盘上,然后放置在一个2X8的两位皇后的问题 棋盘和完整的解决方案继续下去,直到找到。在这种方式 大集团显然是错误的组合的早期发现在 过程不需要进行测试。如果有一对攻击 皇后在棋盘的第n列,则无论身在何处 其余8 - N皇后放置,组合不能构成 解决方案。   ;
感性的搜索算法的工作原理如下。我们开始放置  60; 在A1的第一个皇后。第二个皇后被放置在B3(B1和B2 60; 第一皇后的攻击)。然后,我们来看看unattacked平方米 第三列。我们将继续以同样的方式,直到我们到达一列, 没有unattacked广场。在这种情况下,我们返回到上 列和我们尝试将女王摆在那里的另一个unattacked 平方米。我们只审查比以前更高的行广场 之一。如果这是不可能的,我们将另一列,我们尝试 这样做。如果发现,我们在上一篇专栏文章中的一个新的unattacked平方米  0;准备再次向前移动。每次我们都能够放置一个皇后 & #160; 第八行,发现一个新的解决方案。我们增加一个变量,存储 0; 表中的皇后的位置,我们返回到第7列找到 一个新的unattacked平方米。当我们需要返回,搜索完成 从第2列第1列和王后是摆在A8。一个可视化 该算法可以点击"解决"按钮 以前的小程序。 泛化
感性的搜索算法的概念,可以用在更多的&# 160; 一般情况。为了能够重新使用的功能 算法,我们将定义一个"抽象"之谜。这种"抽象"之谜 代表一个N细胞的表,必须由充满未来元素 从一个有序列表(见图1)。 {S0}
图1:一般之谜
为了解决这个谜团电感我们先填写1细胞,然后细胞2等 &# 160; 直到所有的细胞都充满了。该算法的流程图  60; 如下图: {S1}
图2:流程图
,对于这种技术的工作,我们必须定义两个功能: check_placement(M):如果检查的第一分谜语 M细胞是解决 get_next_element(E):它给出一个元素e,返回 &# 160; 紧随其后的元素的有序列表。 &# 160;
C类riddle_solverlt; move_tgt;实现 60; "抽象"之谜。


template <class move_t>

class riddle_solver

{

public:

  /** Constructor. */

  riddle_solver<move_t>();



  /**  Destructor. */

   riddle_solver<move_t>();



  /**

  Solves the riddle using inductive searching algorithm. 

  If not instructed otherwise

  (by restricting the number of solutions to search for),

  all possible combinations

  are searched.



  @result Returns true if one or more solutions have been found.

  */

  bool solve();



  /**

  Prints all solutions found.

  */

  void print_all();



  /**

  Sets the maximum number of solutions to search for. 

  If 0 is given, solve function

  will scan all possible combinations.



  @param n Maximum number of solutions to search for.

  */

  void set_solutions_count(int n);



  /**

  Sets the size of the riddle, that is the size 

  of the table it must be filled

  to solve the riddle.



  @param s Riddle's size.

  */

  void set_size(int s);



  /**

  Sets null element. When null element is given as input 

  to get_next_element function,

  the first element in the ordered element list will 

  be returned. Null element cannot

  be used to fill a cell.



  @param mn Null element.

  */

  void set_null_element(move_t mn);



private:

  /**

  tries to fill the next cell in the table. 

  If this is impossible it will return

  false. The algorithm must then return to a previous cell. 

  It must not be overridden.



  @result Returns true if it was possible to make a new move.

  */

  bool make_next_move();



  /**

  It tries to find another valid element in a previous cell. 

  It will go back as many

  cells as required. If this impossible, 

  false will be returned and the searching

  will stop. It must not be overridden.



  @result Returns true if a new valid element in

  a previous cell has been found.

  */

  bool go_back();



protected:  // virtual functions



  /**

  Checks if the subriddle of size moves_count is solved. 

  It may be overridden. It is

  declared virtual, because it is called from solve function 

  of the base class.

  When called in the context of a subclass, 

  solve function must call check_placement

  of the subclass and not this version.



  @result Returns moves_count if the subriddle is solved, 0 otherwise.

  */

  virtual int check_placement();



  /**

  Gets next element from the elements ordered list.



  @param pos Position of cell that contains the 

  element to be replaced by the

  next one found. A new cell will contain null 

  element, so that we start the

  searching from the first element in the list. 

  It must be overridden.

  @result Returns next element.

  */

  virtual move_t get_next_element(int pos);



  /**

  Registers move at specified position. 

  In its simplest form, it just inserts

  element move at cell pos. It may be overridden.



  @param pos Position of cell to place the element.

  @param move Element to be placed.

  */

  virtual void register_move(int pos, move_t move);



  /**

  Undoes last move (at position moves_count - 1). 

  In its simplest form, it just

  removes the element placed at the last cell and 

  fills it with null element. It may

  be overridden.

  */

  virtual void unregister_last_move();



  /**

  Prints solution in the standard output.



  @param sol Vector containing the solution.

  */

  virtual void print_solution(vector<move_t> sol) {;};



protected:  // Data members



  /// Table storing the moves made.

  vector<move_t> moves_array;



  /// Riddle's size (moves_array size).

  int size;



  /// Number of moves made so far.

  int moves_count;



private:

  /// Null element.

  move_t move_null;



  /// Maximum number of solutions to look for.

  int sol_number_max;



  /// Vector for storing all solutions found.

  vector<vector<move_t> > sol_v;

};

  

注:riddle_solverlt; move_tgt;是一个模板 类。 move_t类型定义的元素的类型 填写表中的细胞。在本文的所有例子 riddle_solver将被实例化作为move_t INT。 不过类可用于与任何其他原生类型(如float或double) 或用户定义的类,重载=运算符。
riddle_solverlt; intgt;作为基类,八 一类是可以解决的皇后之谜那样简单: {C}
在下面的段落将被用于riddle_solver类 为解决更多的难题。 爱因斯坦的里德尔
这是谜语,最初给我这篇文章的动机。这 是怎么一回事呢:
 0; 五种不同的颜色有5个房子。  ; 在每间房子,生活与不同国籍的人。 &# 160;这五个业主喝了某种类型的饮料,烟一定  0; 品牌的雪茄,并保持一定的宠物。 没有业主有相同的宠物,吸烟的同一品牌的雪茄或饮料 & #160; 相同的饮料。
,对于这一离奇的邻里,我们知道以下内容:
英国人住在红房子。 瑞典人保持作为宠物狗。  60; 丹麦人喝茶。 绿色的房子是在白宫的左侧。 绿房子的主人喝咖啡。 抽Pall Mall香烟养鸟的人。 黄色房子主人抽Dunhill香烟。 该名男子居住在该中心的房子喝牛奶。 挪威人住在第一间房子的。 旁边的人抽烟的混合生活保持猫。 保持马的人住抽Dunhill香烟的人。 吸烟BlueMaster喝啤酒的所有者。 德国王子抽烟。 挪威人住蓝色房子旁边。 的人吸烟的混合有一个喝水的邻居是谁。
现在的问题是:谁拥有的鱼吗?要回答这个问题,必须找到 国籍,肤色,宠物,饮料和烟对应 所有的房子。 {S2}
图3:爱因斯坦的谜语附近
,为了适应谜语我们的抽象谜语的形式,我们定义一个表 大小25。前5个细胞中含有业主的国籍,在未来五年 房子的颜色,那么宠物的饮料和抽烟。这 意味着,我们构建了一个解决方案,同时,我们将首先发现的国籍 所有业主,然后将房屋等的颜色。元素 列表中包含数字1到5,具有明显的顺序。我们有5财产 &# 160; 每5个属性值类型(国籍,肤色,宠物,饮料和烟)。 我们每个属性值映射为一个数字,从1到5。表细胞 充满了从1到5的数字,但是有限制,两院 不能包含任何给定的属性相同的值。实现的细节 出现在文件eisteins_riddle.cpp和einsteins_riddle.hpp。 &# 160; 魔方
一阶幻方ñ是NXN表,其中包含的所有号码从1 nsup2;以这样的方式,所有的行,列和主的总和 对角线上是相同的。为构建一个幻方的算法 任意顺序存在。此外,还有一个 CodeProject上,将做的工作。枚举的问题 幻方的一个特定的顺序听起来更具挑战性 ()。我们将尝试使用riddle_solver 类枚举3阶,4和5的幻方。
在上述抽象谜语的概念,我们要 60; 构造幻方的细胞,通过细胞来访逐一 下面的顺序排列。 {S3}
{A8}图4:访问幻方的顺序
下面列出了实施check_placement 常规。每当一个细胞充满了一个数字,例行 如果某行的检查,一列或主对角线完成。如果这 发生的事情,它计算的总结和测试,如果它是正确的。总和 所有的行,列和对角线幻方NXN的主应 N [nsup2; 1 / 2。

int

magic_square_solver::check_placement()

{

  int r, c;



  if (moves_count == 0)

    return(0);



  r = (moves_count-1) / square_size;

  c = (moves_count-1) % square_size;



  // A row has just been completed

  if (c == square_size-1)

  {

    if (!test_row(r))

      return(0);

  }



  if (r == square_size - 1)

  {

    // Reverse diagonal has been completed

    if (c == 0)

    {

      if (!test_reverse_diagonal())

        return(0);

    }

    // Diagonal has been completed

    if (c == square_size - 1)

    {

      if (!test_diagonal())

        return(0);

    }



    // A column has just been completed

    if (!test_column(c))

      return(0);

  }



  return(moves_count);

}

  

虽然这段代码可以很好地用于3x3的正方形,你会发现,它 太长任何使用4x4的正方形。使其更快 我们要加强check_placement常规,以便及时发现 早期错误的组合。我们通过修改check_placement实现这一点, 所以,它使每一个新的填充单元测试,不仅为细胞  0; 完成一行,一列或主对角线。对于每一个新填充   ; 的细胞,我们计算的部分填充的行和列的款项, 属于。如果它是一个初级对角线的一部分,我们也计算 对角线总和至今。然后我们检查,如有其中部分款项 已经是所需金额较大的,或如果它是比它至少少 应。如果是这样的情况下,我们回到一个细胞。最低金额 部分填充的行/列/对角线的计算公式如下。 如果纳米细胞填充,局部的总和应等于或更大的 ñ [nsup2; 1] / 2 - nsup2; - (nsup2; -1) - ... - (nsup2; - M 1)。

int

magic_square_solver::check_placement()

{

  int r, c;



  if (moves_count == 0)

    return(0);



  r = (moves_count-1) / square_size;

  c = (moves_count-1) % square_size;



  // A row has just been completed

  if (c == square_size-1)

  {

    if (!test_row(r))

      return(0);

  }



  if (r == square_size - 1)

  {

    // Reverse diagonal has been completed

    if (c == 0)

    {

      if (!test_reverse_diagonal())

        return(0);

    }

    // Diagonal has been completed

    if (c == square_size - 1)

    {

      if (!test_diagonal())

        return(0);

    }



    // A column has just been completed

    if (!test_column(c))

      return(0);

  }

  else

  {

    // Test partial row, column, diagonal

    if (c != square_size-1 && !test_partial_row(r, c))

      return(0);

    if (!test_partial_column(c, r))

      return(0);

    if (c == r && !test_partial_diagonal(r))

      return(0);

    if ((c == square_size - r - 1) && !test_partial_reverse_diagonal(r))

      return(0);

  }



  return(moves_count);

}

  
& #160;
随着增强check_placement程序,您将能够枚举 0; 几分钟4x4的正方形。比较的结果,你会发现与, 提出在。请注意,如果我们有一个解决方案, 我们不难发现,通过反射和旋转7。如果这些 在同一个解决方案的变化不计,我们必须划分我们的结果 与8。
当我们转移到5x5的正方形,我们注意到,即使是增强版 效率不高。其实有275305224 * 8 5阶幻方。如果 该算法能够找到一个解决方案,每1ms,它仍然 &# 160; 需要25天半来枚举所有的解决方案!无需得到  0; 气馁虽然。一个简单的技巧,我们可以提高的速度  0; 算法。我们要做的是改变顺序,我们走访了细胞:
{S4}的
{A10}图5:访问5x5的幻方的顺序
magic_square_5类解决了5x5的幻方的问题 以下上图中的访问顺序。类只执行 60; 基本的测试,当一排,一个单元或一个主对角线完成。在我 机,它能够找到解决方案,每一个或两个第二。这是 仍然很慢,但我相信,它可以与几个优化 调整列举在一个体面的时期的5阶幻方 时间。
从这个例子可以得出结论,是非常重要的追赶无效  0;尽可能早的组合,即使这意味着check_placement  0; 日常必须作出更复杂,因而更费时。这样做的好处 必须测试的组合数量限制可 在check_placement度过了时间的重要性远远大于。 可以实现相同的效果(限制组合的数量) 改变填充表的顺序。陀螺接龙
这是一个经典的棋盘游戏。要赢得比赛,你必须删除所有的钉子 断板,除了最后一个。一个挂钩可以移动到任意位置,两个 在左,右,同比增长或下降的位置,在另一个PEG跳跃。 " 跃过PEG被删除。你可以玩游戏的Java applet  0;你会发现在下载部分。
为了解决这个谜riddle_solver类,谜语   ; 必须定义表和元素的列表。谜语是解决时31 & #160; 移动,因此应之谜的表大小为31,并包含 赢得比赛所需的所有动作。我们还必须找到一种方法来表示 一招。这样做是基于PEG,是移动和位置 移动的方向(见图4)。 {五}
{A11}图6:移动代表
的举动责令其数值的基础上,这是一个一招 较高的数值是尝试后,移动具有较低的数值。 实现的细节,可以发现在solo.cpp和solo.hpp文件。链接 &# 160; MathWorld - 从MathWorld幻方的信息。  0; 幻方的枚举 - 一个关于枚举的法宝页 正方形。   ; 我的主页 - Java的难题。 历史 2004年6月3日。初始发行

回答

评论会员:BNC 时间:2011/12/02
你做了了不起的工作。我一直在等待这样一个一年多的文章,虽然我写了一些它的一部分。

再次感谢u。 {S6}
评论会员:游客 时间:2011/12/02
Giannakakis科斯塔斯卡
感谢您的好话。的代码是有点老了,它已被一些时间,因为我最后一次更新它。您可能需要做的,自己
评论会员:。extus 时间:2011/12/02
Ithela NA SE rothso parakoloytheis论坛,自我parakoloutho giati TORA mathaino的Visual C,eimai 83 genhthhs启spoudazo Biomhxanikh Pliroforikh sthn卡瓦拉。出Kapoies apories我ENA项目宝卡诺sxetika我eksomiosh eNOS的systhmatos MIA Biomhxanias本身的Visual C。硒rothsa poio PANO asxoleise我论坛GIA NA卡诺邮政kamia erothsh ..?一,一个书房asxoleise我的Visual C eida OTI parapano烷烃我的Java,mhpos ksereis poia einai的TA vhmata NA sindetho SE MIA击Dedomenon我THN的Java,THN THN击出宽永我Oracle9i中的SQL。谢谢!

------------------------------{ BR}如果你无法找到
- - - - - - - - - -
方式..让的方式找到你...
------------------------------{ BR}
评论会员:Arch4ngel 时间:2011/12/02
。GIA苏启SE esena启江户时代Biomixaniki Pliroforiki ASE达纳托斯
评论会员:吉姆Xochellis 时间:2011/12/02
文EDW Biomixaniki,psaxnoume GIA TIS paparies玩具kaboyrlazoy
评论会员:游客 时间:2011/12/02
Giannakakis科斯塔斯卡
。任何机构可以共享/删除说了些什么

须以英文发表在论坛上。

"模具凡人!"如果有人说,不留看他是不是
评论会员:。吉姆Xochellis 时间:2011/12/02
大文章(5 / 5)

我非常喜欢的事实,您正试图(显然是成功的)建立一个通用的框架,可以帮助解决各种问题。感动!

最好的问候,
吉姆Xochellis

P.S
你还提醒我,我的Prolog编程天...


-------------------{ BR}当谈到解决一个问题,总是有正确的方法,走错了路,希腊的方式和我的路... ...
评论会员:弗拉基米尔Ralev 时间:2011/12/02
我很高兴你喜欢它!我很好奇,虽然我的文章,以何种方式提醒您的Prolog!

最好的问候,
科斯塔斯卡
评论会员:游客 时间:2011/12/02
Giannakakis科斯塔斯卡
Giannakakis科斯塔斯卡写道:
我很高兴你喜欢它!我很好奇,虽然我的文章,以何种方式提醒您的Prolog!

一)在您的8皇后之谜的解决方案,您正在尝试一种解决方案的路径,如果你不走回头路,尝试下一种可能性。在Prolog解决问题,这是典型的方式。

二)10年前的8皇后之谜的解决方案,写在Prolog,Prolog的方案,我用来测试(与或)并行的Prolog虚拟机的性能之一。 (我当时大学的项目。)这些测试的结果证实,调度,这是我此prolog机或并行设计,是非常有效!

最好的问候,
吉姆Xochellis


-------------------{ BR}当谈到解决一个问题,总是有正确的方法,走错了路,希腊的方式和我的路... ...
评论会员:兰迪李 时间:2011/12/02
你使用一些早期的好坏决定削减呢?像ALPHA测试版搜索?

我一直在寻找这样的框架之前一年。会帮助我非常...

投我一票!
{A12}
{A13}