Ошибка переполнения стека DFS - PullRequest
0 голосов
/ 03 мая 2011

Я делаю 8-кусочную игру-головоломку в Java и команды назначения, которые я выполняю DFS, чтобы найти решение, начиная со случайного состояния.

У меня есть класс Node, каждый объект Nodeзнает, в каком состоянии находится int [] [] и каков его родительский узел.Также он знает, в каком направлении он может двигаться (влево, вправо, вверх, вниз)

     goal state:     start state (random):
     [0 1 2]         [1 8 0]
     [3 4 5]         [3 7 4]
     [6 7 8]         [2 5 6]

, начиная с одного узла, начального узла, моя программа создает узел для всех своих возможных дочерних элементов.Он проверяет, в каких доступных направлениях может двигаться пустой квадрат, и проверяет, не возвращается ли он в состояние, уже принадлежащее другому узлу.Таким образом, в приведенном выше примере начальный узел расширился бы следующим образом:

          [1 8 0]
      A)  [3 7 4]
          [2 5 6]
         /       \
    [1 0 8]     [1 8 4]
B)  [3 7 4]     [3 7 0]  C)
    [2 5 6]     [2 5 6]
   /   /       /       \
...  ...  [1 8 4]     [1 8 4]
      D)  [3 0 7]     [3 7 6]  E)
          [2 5 6]     [2 5 0]
         /  /  /       /    \
     ... ... ... [1 8 4]     NO
             F)  [3 7 6]     CHILD
                 [2 0 5]
                /       \
             ...         ...

То, как я справляюсь с этим, заключается в том, что я исследую первый узел и помещаю все его дочерние элементы в стек, это происходит рекурсивнометод, который циклически расширяет каждый узел, пока не будет достигнуто целевое состояние или не будет достигнута отсечка (число, которое я предоставляю, чтобы избежать зацикливания навсегда)

stack = [A]
pop()
stack = []
A -> B
push(B)
stack = [B]
A -> C
push(C)
stack = [C|B]
A -> NO MORE CHILDREN
pop()
stack = [B]
C -> D
push(D)
stack = [D|B]
C -> E
push(E)
stack = [E|D|B|]
C -> NO MORE CHILDREN
pop()
stack = [D|B|]
E -> F
push(F)
stack = [F|D|B]
E -> NO MORE CHILDREN
pup()
stack = [D|B]

Код работает нормально, пока моя отсечка не слишком высока,если это то, чем я получаю ошибку: java.lang.StackOverflowError

Что я делаю не так?

public void expand(Node parent, int cutoff){
  cutoff--;

  int[][] currentState = parent.getState();

  if(Arrays.deepEquals(currentState, goalState)){
    found = true;
    goalNode = parent;
  }


  if(cutoff>0 && !found){
    if(parent.up){
      Node child = createUPnode();
      child.setParent(parent);
      stack.push(child);
      parent.up = false;
      expand(parent, cutoff)
    }
    else if(parent.right){
      Node child = createRIGHTnode();
      child.setParent(parent);
      stack.push(child);
      parent.right = false;
      expand(parent, cutoff)
    }
    else if(parent.down){
      Node child = createDOWNnode();
      child.setParent(parent);
      stack.push(child);
      parent.down = false;
      expand(parent, cutoff)
    }
    else if(parent.left){
      Node child = createLEFTnode();
      child.setParent(parent);
      stack.push(child);
      parent.left = false;
      expand(parent, cutoff)
    }
    else{
      Node nextNode = stack.pop();
      expand(nextNode, cutoff);
    }
  }
  else{
    System.out.println("CUTOFF REACHED");
  }
}

Ответы [ 2 ]

1 голос
/ 03 мая 2011

Пост 'Что такое ошибка переполнения стека?' содержит много информации о том, что может вызвать ошибку StackOverflowError в Java. Наиболее распространенной причиной такого рода ошибок является неправильный рекурсивный вызов (вызывающий бесконечный цикл). Похоже, вы уже защитились от этого, введя предельное значение для ваших рекурсивных вызовов expand().

Но даже с этим разделителем размер стека может быть небольшим для обработки рекурсивных вызовов. Я верю, что у вас есть два варианта:

1) Используйте «достаточно маленькое» значение для вашей отсечки (на самом деле это работает, как вы уже писали), но это ограничивает глубину поиска.

2) Увеличьте размер стека JVM. Вы можете сделать это, добавив флаг -Xss1024k в качестве аргумента VM. Для получения дополнительной информации о том, как увеличить размер стека, читайте Ошибка переполнения стека Java - как увеличить размер стека в Eclipse?

0 голосов
/ 03 мая 2011

Легко реализовать DFS или BFS, используя Dequeue, без какой-либо рекурсии, просто цикл, чтение из заголовка очереди и генерация новых решений для хвоста (BFS).Для использования DSF добавьте детей в голову.

Я думаю, вы должны использовать Breadth -первый поиск, чтобы найти самое короткое возможное решение, верно?

Если вы уверены в первом поиске глубины (что, я думаю, мало смысла), вы можете покончить с рекурсией и просто использовать созданный вами dequeue (а нестек вызовов).Реквизивные звонки не нужны.Просто цикл: запустите каждый цикл, нажав на узел, сгенерируйте его дочерние элементы и поместите их в голову очереди, затем начните сначала.Если dequeue пусто, решения не существует ...

В большинстве случаев лучше проверить, было ли сгенерированное решение уже сгенерировано до того, как добавит их в конец очереди., чтобы уменьшить использование памяти.Используя простые Коллекции, HashSet, вероятно, быстрее, чем TreeSet. Не забудьте правильно реализовать Node.equals() и Node.hashCode().

Я полагаю, что обычный LinkedList будет наиболее эффективным Dequeue в этом случае- но почему бы не проверить себя.

...