生成图表Ruby的路径

时间:2014-04-24 18:36:39

标签: ruby arrays algorithm sorting graph-algorithm

我有一个包含12个节点的无向​​图,我生成了一个这样的数组:

arr = [[3, 12], [8, 12], [0, 3], [0, 5], [0, 10], [7, 9], [5, 5], [4, 9], [5, 12], [0, 1]] 

表示图形边缘。

我想在图表中找到最长的路径,例如每条边最多使用一次。对于给定的示例,它将类似于:

arr = [[1, 0], [0, 5], [5, 5], [5, 12], [12, 3], [3, 0], [0, 10]]

通过搜索欧拉路径但用其他编程语言编写,我已经在许多方面看到了解决这个问题的方法。我将如何在Ruby中继续它?

2 个答案:

答案 0 :(得分:3)

euler路径一次访问每个边缘正好,所以我猜你不需要它。我能提供的是这样的:

def find_path(arr, first_chain = nil, path = [], available = arr.dup)
  edge_idx = available.index do |first, _| 
    first_chain.nil? || first == first_chain
  end
  unless edge_idx
    return path
  end
  edge = available.delete_at(edge_idx)
  path << edge
  find_path(arr, edge.last, path, available)
end

arr = [[0, 5], [3, 0], [5, 5], [1, 0], [0, 10], [5, 12], [12, 3]]

find_path arr
# => [[0, 5], [5, 5], [5, 12], [12, 3], [3, 0], [0, 10]] 

find_path arr, 1
# => [[1, 0], [0, 5], [5, 5], [5, 12], [12, 3], [3, 0], [0, 10]] 

此算法从第一个元素或给定的任何其他整数开始查找某些路径。 保证它使用所有元素,或者它甚至是最长的元素,但它是一条路径......


对于非方向性图表,您需要考虑边缘可能会反转:

def find_path(arr, first_chain = nil, path = [], available = arr.dup)
  edge_idx = available.index do |edge| 
    first_chain.nil? || edge.include?(first_chain)
  end
  unless edge_idx
    return path
  end
  edge = available.delete_at(edge_idx)
  edge = edge.reverse if first_chain && edge.first != first_chain
  path << edge
  find_path(arr, edge.last, path, available)
end

find_path arr
# => [[0, 5], [5, 5], [5, 12], [12, 3], [3, 0], [0, 1]] 

find_path arr, 1
# => [[1, 0], [0, 5], [5, 5], [5, 12], [12, 3], [3, 0], [0, 10]] 

要查找最长路径,您需要构建所有可能路径,并选择最长路径:

def find_longest_path(arr, first_chain = nil, available = arr.dup)
  paths = available.each_index.select do |edge_idx| 
    first_chain.nil? || available[edge_idx].include?(first_chain)
  end.map do |edge_idx|
    edge = available[edge_idx]
    edge = edge.reverse if first_chain && edge.first != first_chain
    [edge, *find_longest_path(arr, edge.last, 
                         available[0...edge_idx] + available[edge_idx+1..-1])]
  end
  # a hack to find longest in all reverse paths
  if first_chain.nil? && arr == available
    paths << find_longest_path(arr, nil, arr.map(&:reverse))
  end
  paths.max_by { |path| path.length }
end

arr = [[3, 12], [8, 12], [0, 3], [0, 5], [0, 10], [7, 9], [5, 5], [4, 9], [5, 12], [0, 1]]

find_longest_path arr
# => [[10, 0], [0, 3], [3, 12], [12, 5], [5, 5], [5, 0], [0, 1]] 

find_longest_path arr, 1
# => [[1, 0], [0, 3], [3, 12], [12, 5], [5, 5], [5, 0], [0, 10]]

此代码的作用是什么?
此算法不是采用可在我们的路径中使用的第一个边缘,而是 all 可以在路径中使用的边缘。

对于每个这样的边缘,它构建了一条新路径:

  1. 以所选边缘开始
  2. available数组
  3. 中删除边缘
  4. 继续从边缘的顶点(递归)开始可用的最长路径
  5. 这将构建所有可能的路径的列表。从这个方法返回最长(最大长度)。

    我标记为# hack的三行是因为当first_chainnil时,算法会找到以任何非反转边开头的最长路径。为了支持反转边缘,我运行它两次 - 第二次反转所有边缘。

    这是不是已知算法的实现,而是一个简单的暴力实现,它可能不是最有效,最简单或最美观的,但它应该让您朝着正确的方向前进。您可以在ruby here

    中找到有关使用图表的更多信息

答案 1 :(得分:0)

虽然可能有更好的方法

,但这样的事情可能对你有用
def generate_array
  a = []
  b2 = nil
  10.times do 
    b1,b2 = [b2 || rand(12),rand(12)]
    a << [b1,b2]
  end
  a
end
generate_array
#=> [[0, 4], [4, 4], [4, 11], [11, 4], [4, 8], [8, 11], [11, 4], [4, 2], [2, 6], [6, 0]]

同样只是基于您的给定输出rand(12)的注释将永远不会返回12,因为上限是使用0索引设置的,因此要返回的最大数字是11