草庐IT

java - 运行深度优先搜索时出现 StackOverflowError(无向图)

coder 2024-04-02 原文

我有一个 DFS 访问递归方​​法,有时会抛出 StackOverflowError。由于图的大小很大(大约 20000 个顶点),递归调用很多,所以我尝试使用 -Xss10M 运行并且一切正常。 我只是想了解为什么在方法的开头添加 System.out.println,即使没有 -Xss10M,该方法也不会抛出任何 StackOverflowError。怎么可能?

这是DFS的访问方式:

private int dfsVisit(Vertex<T> v, int time){
  // System.out.println("Hello");
  Vertex<T> n;
  time++;
  v.d = time;
  v.color = Vertex.Color.GRAY;
  for (Map.Entry<Vertex<T>, Float> a : v.neighbours.entrySet()){
     n = a.getKey();
     if(n.color == Vertex.Color.WHITE){
        n.previous = v;
        time = dfsVisit(n, time);
     }
  }
  v.color = Vertex.Color.BLACK;
  time++;
  v.f = time;

  return time;
}

这是完整的代码

import java.io.*;
import java.util.*;

class Graph<T> {
   private final Map<T, Vertex<T>> graph; 

   public static class Edge<T>{
      public final T v1, v2;
      public final float dist;

      public Edge(T v1, T v2, float dist) {
         this.v1 = v1;
         this.v2 = v2;
         this.dist = dist;
      }
   }

   public static class Vertex<T> implements Comparable<Vertex>{ // SPOSTARE VAR IST NEL COSTRUTTORE
      public enum Color {WHITE, GRAY, BLACK, UNKNOWN};
      public final T name;
      public float dist;
      public Vertex<T> previous;
      public final Map<Vertex<T>, Float> neighbours;
      public Color color;
      public int d, f;

      public Vertex(T name) {
         this.name = name;
         dist = Float.MAX_VALUE; 
         previous = null;
         neighbours = new HashMap<Vertex<T>, Float>(); // adjacency list
         color = Color.UNKNOWN;
         d = 0;
         f = 0;
      }

      private void printPath() {
         if (this == this.previous) { 
            System.out.print(this.name);
         } else if (this.previous == null) {
            System.out.print(this.name + " unreached");
         } else {
            this.previous.printPath();
            System.out.print(" -> " + this.name + "(" + this.dist + ")");
         }
      }

      public int compareTo(Vertex other){
         if(this.dist == other.dist)
            return 0;
         else if(this.dist > other.dist)
            return 1;
         else
            return -1;
      }

   }

   // Builds a graph from an array of edges 
   public Graph(ArrayList<Graph.Edge> edges) {
      graph = new HashMap<>(edges.size());

      // add vertices
      for (Edge<T> e : edges) {
         if (!graph.containsKey(e.v1)) graph.put(e.v1, new Vertex<>(e.v1));
         if (!graph.containsKey(e.v2)) graph.put(e.v2, new Vertex<>(e.v2));
      }

      // create adjacency list
      for (Edge<T> e : edges) {
         graph.get(e.v1).neighbours.put(graph.get(e.v2), e.dist);
         graph.get(e.v2).neighbours.put(graph.get(e.v1), e.dist); 
      }
   }


   public void dijkstra(T startName) {
      if (!graph.containsKey(startName)) {
         System.err.println("Graph doesn't contain start vertex " + startName);
         return;
      }
      final Vertex<T> source = graph.get(startName);
      NavigableSet<Vertex<T>> q = new TreeSet<>(); // priority queue

      // set-up vertices
      for (Vertex<T> v : graph.values()) {
         v.previous = v == source ? source : null;
         v.dist = v == source ? 0 : Float.MAX_VALUE;
         q.add(v);
      }

      dijkstra(q);
   }

   private void dijkstra(final NavigableSet<Vertex<T>> q) {      
      Vertex<T> u, v;
      while (!q.isEmpty()) {

         u = q.pollFirst();
         if (u.dist == Float.MAX_VALUE) break; //???????????


         for (Map.Entry<Vertex<T>, Float> a : u.neighbours.entrySet()) {
            v = a.getKey(); 

            final float alternateDist = u.dist + a.getValue();
            if (alternateDist < v.dist) { 
               q.remove(v);
               v.dist = alternateDist;
               v.previous = u;
               q.add(v);
            } 
         }
      }
   }


   public void printPath(T endName) {
      if (!graph.containsKey(endName)) {
         System.err.println("Graph doesn't contain end vertex " + "\"" + endName + "\"" );
         return;
      }

      graph.get(endName).printPath();
      System.out.println();
   }

   public void printAllPaths() {
      for (Vertex<T> v : graph.values()) {
         v.printPath();
         System.out.println();
      }
   }

   public Vertex<T> getVertex(T key){
      if(graph.containsKey(key))
         return graph.get(key);
      return null;
   }

   public void printAdjacencyList(){
      System.out.println("Adjacency list:");
      for(Vertex<T> v : graph.values()){
         System.out.print(v.name + ":\t");
         for (Map.Entry<Vertex<T>, Float> a : v.neighbours.entrySet()){
             System.out.print(a.getKey().name + "(" + a.getValue() + ") | ");
         }
         System.out.println();
      }

   }
   /*
   P.S. I know that if only used to calculate the connected components of the graph, dfs visit 
   could be written differently but I preferred to write it in a more general way, so that it 
   can be reused if necessary.
   */
   private int dfsVisit(Vertex<T> v, int time){
     // System.out.println("ciao");
      Vertex<T> n;
      time++;
      v.d = time;
      v.color = Vertex.Color.GRAY;
      for (Map.Entry<Vertex<T>, Float> a : v.neighbours.entrySet()){
         n = a.getKey();
         if(n.color == Vertex.Color.WHITE){
            n.previous = v;
            time = dfsVisit(n, time);
         }
      }
      v.color = Vertex.Color.BLACK;
      time++;
      v.f = time;

      return time;
   }

   /*
   Print the size of the connected components of the graph
   */
   public void connectedComponents(){
      for(Vertex<T> v : graph.values()){
         v.color = Vertex.Color.WHITE;
         v.previous = null;
      }

      for(Vertex<T> v : graph.values()){
         if(v.color == Vertex.Color.WHITE)
             System.out.println(dfsVisit(v, 0)/2);
      }

   }



}

这是测试类

import java.io.*;
import java.util.*;

public class Dijkstra {

   private static ArrayList<Graph.Edge> a = new ArrayList<Graph.Edge>();
   private static final String START = "torino";
   private static final String END = "catania";

   public static void main(String[] args) {
      String fileName = "italian_dist_graph.txt";
      try{
         Scanner inputStream = new Scanner(new File(fileName));
         String record;
         while(inputStream.hasNextLine()){
            record = inputStream.nextLine();
            String[] array = record.split(",");
            String from = array[0];
            String to = array[1];
            float dist = Float.parseFloat(array[2]);
            a.add(new Graph.Edge(from, to, dist));
         }
         inputStream.close();
      } catch(FileNotFoundException e){
         System.out.println("Impossibile trovare il file "+fileName);
      }



      Graph<String> g = new Graph<String>(a);

      g.dijkstra(START);
      g.printPath(END);
     //System.out.printf("%f\n", g.getVertex(END).dist/1000.0f);
      g.connectedComponents();
   }
}

注意尝试评论 g.dijkstra(START) 和 g.printPath(END);一切似乎都正常。

这是数据集的链接

https://drive.google.com/open?id=0B7XZY8cd0L_fZVl1aERlRmhQN0k

最佳答案

一些一般性建议:
您的代码混合了顶点的属性,这些属性与 dfs 的单次运行相关,并且是顶点的直接属性。 坏坏坏风格。这很可能会破坏任何更复杂的算法,可能会产生意外行为,并且需要在每次运行后清除状态,以确保代码的稳定性。相反,保持与算法的单次运行相关的状态仅对该函数可见。例如。将状态存储在 Map 中,使用装饰器模式创建提供附加属性并具有方法局部作用域等的数据结构。例如:在同一个图上运行代码两次(相同的 Object)具有相同的输入而不清除所有状态将导致错误的结果 (1)。

此外:创建 DFS 的迭代版本并不难,所以您应该尝试一下,特别是因为您的图形看起来非常大。

至于为什么您的代码以其方式工作(或不工作):
这很难说,因为它取决于很多因素。您没有提供完整的代码,所以我无法重新运行任何测试,也无法验证一切是否按预期方式运行。最可能的答案:

Vertex 使用 Object 提供的默认哈希码。这导致邻居 map 中条目的随机排序,因此特定路径的遍历顺序在每次运行中都是随机的,并且很可能不同。因此,您正在使用随机路径遍历图形,每次运行很可能(特别是由于图形的大小)不同。原因不是 System.out.println,而是一个事实,即您的代码每次运行时都会生成不同的结构(来自排序 POV,而不是数学),加上巧合,由于某些非常奇怪的原因,图表的每次构建都没有达到 StackOverflow 所需的递归深度,并且使用 System.out.println 编译的代码一起出现。

Java 编译器或 JIT 以一种奇怪的方式修改代码的行为。现代编译器倾向于生成非常奇怪的代码,以尝试优化他们可以推迟的所有内容。

关于java - 运行深度优先搜索时出现 StackOverflowError(无向图),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37489743/

有关java - 运行深度优先搜索时出现 StackOverflowError(无向图)的更多相关文章

  1. ruby - 如何从 ruby​​ 中的字符串运行任意对象方法? - 2

    总的来说,我对ruby​​还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用

  2. ruby - ECONNRESET (Whois::ConnectionError) - 尝试在 Ruby 中查询 Whois 时出错 - 2

    我正在用Ruby编写一个简单的程序来检查域列表是否被占用。基本上它循环遍历列表,并使用以下函数进行检查。require'rubygems'require'whois'defcheck_domain(domain)c=Whois::Client.newc.query("google.com").available?end程序不断出错(即使我在google.com中进行硬编码),并打印以下消息。鉴于该程序非常简单,我已经没有什么想法了-有什么建议吗?/Library/Ruby/Gems/1.8/gems/whois-2.0.2/lib/whois/server/adapters/base.

  3. ruby - 在 64 位 Snow Leopard 上使用 rvm、postgres 9.0、ruby 1.9.2-p136 安装 pg gem 时出现问题 - 2

    我想为Heroku构建一个Rails3应用程序。他们使用Postgres作为他们的数据库,所以我通过MacPorts安装了postgres9.0。现在我需要一个postgresgem并且共识是出于性能原因你想要pggem。但是我对我得到的错误感到非常困惑当我尝试在rvm下通过geminstall安装pg时。我已经非常明确地指定了所有postgres目录的位置可以找到但仍然无法完成安装:$envARCHFLAGS='-archx86_64'geminstallpg--\--with-pg-config=/opt/local/var/db/postgresql90/defaultdb/po

  4. ruby - 如何每月在 Heroku 运行一次 Scheduler 插件? - 2

    在选择我想要运行操作的频率时,唯一的选项是“每天”、“每小时”和“每10分钟”。谢谢!我想为我的Rails3.1应用程序运行调度程序。 最佳答案 这不是一个优雅的解决方案,但您可以安排它每天运行,并在实际开始工作之前检查日期是否为当月的第一天。 关于ruby-如何每月在Heroku运行一次Scheduler插件?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/8692687/

  5. ruby-on-rails - 如何在 ruby​​ 中使用两个参数异步运行 exe? - 2

    exe应该在我打开页面时运行。异步进程需要运行。有什么方法可以在ruby​​中使用两个参数异步运行exe吗?我已经尝试过ruby​​命令-system()、exec()但它正在等待过程完成。我需要用参数启动exe,无需等待进程完成是否有任何ruby​​gems会支持我的问题? 最佳答案 您可以使用Process.spawn和Process.wait2:pid=Process.spawn'your.exe','--option'#Later...pid,status=Process.wait2pid您的程序将作为解释器的子进程执行。除

  6. ruby - 无法运行 Rails 2.x 应用程序 - 2

    我尝试运行2.x应用程序。我使用rvm并为此应用程序设置其他版本的ruby​​:$rvmuseree-1.8.7-head我尝试运行服务器,然后出现很多错误:$script/serverNOTE:Gem.source_indexisdeprecated,useSpecification.Itwillberemovedonorafter2011-11-01.Gem.source_indexcalledfrom/Users/serg/rails_projects_terminal/work_proj/spohelp/config/../vendor/rails/railties/lib/r

  7. ruby - Sinatra:运行 rspec 测试时记录噪音 - 2

    Sinatra新手;我正在运行一些rspec测试,但在日志中收到了一堆不需要的噪音。如何消除日志中过多的噪音?我仔细检查了环境是否设置为:test,这意味着记录器级别应设置为WARN而不是DEBUG。spec_helper:require"./app"require"sinatra"require"rspec"require"rack/test"require"database_cleaner"require"factory_girl"set:environment,:testFactoryGirl.definition_file_paths=%w{./factories./test/

  8. java - 等价于 Java 中的 Ruby Hash - 2

    我真的很习惯使用Ruby编写以下代码:my_hash={}my_hash['test']=1Java中对应的数据结构是什么? 最佳答案 HashMapmap=newHashMap();map.put("test",1);我假设? 关于java-等价于Java中的RubyHash,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/22737685/

  9. 使用 ACL 调用 upload_file 时出现 Ruby S3 "Access Denied"错误 - 2

    我正在尝试编写一个将文件上传到AWS并公开该文件的Ruby脚本。我做了以下事情:s3=Aws::S3::Resource.new(credentials:Aws::Credentials.new(KEY,SECRET),region:'us-west-2')obj=s3.bucket('stg-db').object('key')obj.upload_file(filename)这似乎工作正常,除了该文件不是公开可用的,而且我无法获得它的公共(public)URL。但是当我登录到S3时,我可以正常查看我的文件。为了使其公开可用,我将最后一行更改为obj.upload_file(file

  10. ruby-on-rails - 如何在 Ruby on Rails 中实现无向图? - 2

    我需要在RubyonRails中实现无向图G=(V,E)并考虑构建一个Vertex和一个Edge模型,其中Vertex有_多条边。由于边恰好连接两个顶点,您将如何在Rails中执行此操作?您是否知道任何有助于实现此类图表的gem或库(对重新发明轮子不感兴趣;-))? 最佳答案 不知道有任何现有库在ActiveRecord之上提供图形逻辑。您可能必须实现自己的Vertex、EdgeActiveRecord支持的模型(请参阅Rails安装的rails/activerecord中的vertex.rb和edge.rb/test/fixtur

随机推荐