广度优先搜索Java

时间:2009-01-28 20:33:03

标签: java arraylist breadth-first-search

我不得不在Java中进行广度优先搜索以进行分配。我有一个5x5网格的瓷砖(总共24个 - 一个瓷砖留下'空白')。搜索的目的是通过向上,向下,向左或向右移动“空白”来重新排列图块,以最终将图块重新排列为正确的顺序。

要进行此搜索,我创建了一个Arraylist'队列'。我有一个方法,在这个arraylist的索引0处获取状态,找到可以跟随的每个合法移动,然后将它们分别添加到arraylist的末尾。

理论上,这一直持续到最终找到“目标国家”。问题是当我运行搜索时,'队列'arraylist继续变得越来越大。今天我把它运行了几个小时仍然没有找到解决方案。

这表明我可能以错误的方式解决了这个问题,并且有一个更好的方法让我在Java中进行广度优先搜索。我知道我的解决方案确实有效(最终),因为当我使用与目标状态没有太大差异的开始状态时,找到正确的路径并不需要太长时间。但是,我已经获得了一个使用的开始状态,遗憾的是,它没有接近目标状态!!!

非常感谢任何提示或提示!

11 个答案:

答案 0 :(得分:7)

首先,我肯定会使用实际的Queue对象而不是ArrayList。这是Queue界面上的Java API页面:http://java.sun.com/j2se/1.5.0/docs/api/java/util/Queue.html - 您可以在该页面上看到有许多Queue实现者,如果您不知道要选择什么,一个简单的LinkedList就可以。 ArrayList会有很大的性能影响因为它只是从最后快速删除,如果你从阵列中的任何其他地方删除它必须将一切向下移动一个( SLOW ! )。你会在最后入队并在开始时出队,因此 SLOW

现在你没有明确提到你在完成项目之后将它们删除(删除它们)所以我认为你是这样,因为这是一个原因。

您是否必须专门使用广度优先搜索?如果我算得对,那就有25个! (阶乘)组合,所以这是15511210043330985984000000组合,从理论上讲,如果你正在进行广度优先搜索,你的算法不太可能完成。是不允许深度优先搜索?如果必须使用广度优先搜索,那么使其更快的唯一方法是修剪不可能导致最佳解决方案的状态。不知道你会怎么做。

答案 1 :(得分:2)

没有重复检查的广度优先搜索肯定会为您提供与您所看到的结果相似的结果。事实上,证明你实现它的算法永远不会终止是相当简单的。尽管如此,请随意等待...: - )

您需要的是一个辅助数据结构(最好是Set)来存储预先检查的位置。因此,部分代码如下所示:

public Queue<Position> queue = new LinkedList<Position>();
public Set<Position> done = new HashSet<Position>();

public Position[] findPath(Position start, Position goal) {
    if (done.contains(start)) return null;

    done.add(start);
    // add subsequent states to `queue`
    ...
}

正如其他答案所述,深度优先搜索在这种情况下是一个很多更好的解决方案。假设goal位置可以到达,除了最有用的位置之外,它应该会产生指数优越的搜索时间。

答案 2 :(得分:1)

乍一看,您的图表似乎可能会出现重复状态,从而出现循环。您可能需要考虑某种方式来确定两个状态是否相同以及您之前是否已经访问过两个状态。

编辑:暂且不考虑算法限制,也许有一个公式可以将块X移动到位置Y到位置Z而不会分解任何其他区块。鉴于此,您可以只计算所需的所有转换。我现在只是在思考这个问题。

答案 3 :(得分:0)

虽然它可能不属于你的任务范围,但有一种算法可以解决这类难题。基本上,有两个移动适用于除最后两行之外的每一行,并且有两个移动适用于最后两行。使用这种方法可以为您提供更快的解决方案。

不幸的是,因为这是一项任务,所以毫无疑问你会被要求进行暴力搜索。

答案 4 :(得分:0)

我已经完成了'粗暴搜索' - 问题是,5x5网格有很多组合,它只需要永远。我在上班的时候离开了它... 8小时后,它还在继续,队列arraylist大小达到100k不同状态,我的电脑听起来像是要过热而爆炸lol

我知道我的代码有效(最终,就像我说的那样)所以我很乐意提交它。我只是担心找到解决方案所花费的时间,并想知道除了使用arraylist创建队列之外,是否还有更好的方法。

答案 5 :(得分:0)

我认为这个练习的想法是让你意识到深度优先搜索是解决这个特殊难题的更好方法。

答案 6 :(得分:0)

我必须为两者编写代码。所以我即将进入深度优先搜索。在我这样做之前,我只是想确保我不会因为制作代码而被嘲笑,这些代码需要花费一天时间才能找到解决方案!!!!!!!

答案 7 :(得分:0)

你没有说你是否检查过,但听起来你并没有修剪周期。

首先,假设您不是将移动表示为在某个方向上移动空白方块,而是提供已移动的区块编号。 (它保证是独一无二的。)现在假设合法移动是“3” - 将瓷砖#3移动到空白区域。结果是新的电路板布局。从那里开始的合法移动是再次移动牌3,现在你回到了你开始的地方。

滑块拼图可以轻松拥有更大的周期。如果有一个2x2网格,其中1-2-3为空,那么有效的移动序列是1-2-3-1-2-3-1-2-3-1-2-3 - 它会发送给你回到开头。

如果您的搜索没有检查并修剪重复的电路板,广度优先搜索仍将终止(假设没有错误,并且没有奇偶校验错误(?)使您的电路板无法解决) - 存在正确的解决方案是N移动的长度,你将花费有限的时间来处理每一个K移动序列,所以你最终会得到N和你的解决方案。但是,每个移动长度的时间呈指数增长(每个移动板最多移动4次)。你可能在记忆中挣扎。

不幸的是,检查重复电路板状态的强力方法是在到达时存储每个电路板,这也会快速耗尽存储器 - 尽管可能没有当前方法那么快。事实上,仅仅5x5的电路板可能会令人满意。

答案 8 :(得分:0)

也许是目标导向的深度优先搜索。首先解决9个边缘拼贴,将拼图缩小到4x4网格。然后解决它的7个边缘区块,减少到3x3网格,这应该是原始算法的简单选择。

答案 9 :(得分:0)

我现在这样做的方式如下:

  • 2名Arraylists;队列和访问
  • 开始状态会自动添加到Visited arraylist
  • 获取从开始开始的所有可能的合法移动,并将每个移动与存储在Visited arraylist中的移动进行比较。
  • 不会创建“已访问”状态的合法动作会添加到队列中。
  • 采用arraylist的索引0处的状态,并且该过程重复进行。

我从之前的答案中看到,我应该将arraylists改成不同的集合。但我正在检查状态是否重复。

答案 10 :(得分:0)

接下来的事情是,如果我要访问列表,我会使用一个Set(可能是TreeSet)来自动排序状态,以便搜索是否已访问过新状态以前会快得多。

您需要做的就是让您的州类实现Comparable接口。 (希望你已经预料到这样的事情,并使它成为一个类)。如果您还不知道,使用此接口的compareTo方法,您可以定义对象的排序顺序,这当然将由访问集使用。从那里你可以设置compareTo方法,就像字符串比较一样工作,除了你的瓷砖。

所以,为了清楚说明,如果每个瓷砖都分配了一个字母,并列出了每个州的瓷砖,我们可能会这样:

State 1: ABCDEFGHIJKLMNOP
State 2: BCADEFGHIJKLMNOP

自然状态1将在顺序中排在第一位。所以只需推断compareTo机制来处理tile到tile(我确定你的tile有ID或索引或者它们不是吗?)你将拥有一个已排序的访问列表。然后,当您调用visited.contains(state)时,程序将能够非常快速地计算是否已访问过某个州。