首页 文章

这个Sedgewick代码是否正确?

提问于
浏览
18

我正在解决一个优化问题,其中,我必须最大化流网络 . 我实现了一个基于c代码的流量最大化算法,该算法基于以下 java code 出现在Sedgewick "Algorithms in Java, Third Edition, Part 5: Graph Algorithms"一书中,该算法使用基于顶点的PREFLOW-push算法最大化网络流量:

class NetworkMaxFlow
{ private Network G; private int s, t;
  private int[] h, wt;
  private void initheights()
  NetworkMaxFlow(Network G, int s, int t)
  { this.G = G; this.s = s; this.t = t;
    wt = new int[G.V()]; h = new int[G.V()];
    initheights();
    intGQ gQ = new intGQ(G.V());
    gQ.put(s); wt[t] = -(wt[s] = Edge.M*G.V());
    while (!gQ.empty())
    { int v = gQ.get();
      AdjList A = G.getAdjList(v);
      for (Edge e = A.beg(); !A.end(); e = A.nxt())
        { int w = e.other(v), cap = e.capRto(w);
          int P = cap < wt[v] ? cap : wt[v];
          if (P > 0 && v == s || h[v] == h[w]+1) // first observation (see below)
            { e.addflowRto(w, P);
              wt[v] -= P; wt[w] += P;
              if ((w != s) && (w != t)) gQ.put(w); // enqueue w if it is not source or sink
            }
        }
      if (v != s && v != t && wt[v] > 0) // why check v != t if t never enter the queue?
        { h[v]++; gQ.put(v); }
    }
  }
}

我的实现,基于该代码,无法最大化后续网络
Capacitated network; all flows are in zero
执行后,生成的流程如下
Final state for the previous net
使用此流程,流量值为8,但最大值为9,如图的流程所示根据我的理解,该算法与本书的解释一致 . 但是,我看到两件奇怪的事情

  • 来源没有明确的预流阶段 . 它包含在 while 中并且在谓词 P > 0 && v == s 为真时仅执行一次且仅执行一次 . 也许这是为了缩短代码

  • 根据我的理解和本书的话语,接收器永远不会进入队列 . 但是,当高度增加时,代码会检查v!= t . 有什么理由吗?

这是我在C中实现此算法的摘录

template <class Net, class Q_Type> typename Net::Flow_Type
generic_preflow_vertex_push_maximum_flow(Net & net)
{
  init_height_in_nodes(net); // breadth first traverse from sink to
                 // source. Nodes are labeled with their
                 // minimal distance (in nodes) to sink
  auto source = net.get_source();
  auto sink   = net.get_sink();

  using Itor = __Net_Iterator<Net>;
  Q_Type q; // generic queue (can be fifo, heap or random) of active nodes

  // preflow: floods all nodes connected to the source 
  for (Itor it(source); it.has_curr(); it.next()) 
    {
      auto arc  = it.get_curr();  
      arc->flow = arc->cap; // saturate arc to its maximum 
      auto tgt = net.get_tgt_node(arc);
      put_in_active_queue(q, tgt);
      assert(node_height<Net>(source) == node_height<Net>(tgt) + 1);
      assert(not is_residual<Net>(source, arc));
    }

  while (not q.is_empty()) // while there are active nodes
    {
      auto src = get_from_active_queue(q);
      auto excess = net.get_in_flow(src) - net.get_out_flow(src);

      for (Itor it(src); it.has_curr(); it.next()) 
        {
          auto arc = it.get_curr();
          auto tgt = net.get_connected_node(arc, src);

          if (node_height<Net>(src) != node_height<Net>(tgt) + 1)
            continue; // this arc is not eligible

          typename Net::Flow_Type flow_to_push;
          if (is_residual<Net>(src, arc))
            {
              flow_to_push = std::min(arc->flow, excess);
              arc->flow -= flow_to_push;
            }
          else
            {
              flow_to_push = std::min(arc->cap - arc->flow, excess);
              arc->flow += flow_to_push;
            }

          excess -= flow_to_push;
          if (tgt != sink and tgt != source)
            put_in_active_queue(q, tgt);
        }

    if (excess > 0) // src still active?
      { 
        node_height<Net>(src)++;
        put_in_active_queue(q, src);
      }
  }

  return net.flow_value(); // sum of all outing flow from source
}

¿有人发现我的代码和Sedgewick的代码之间存在任何逻辑上的不一致吗?我的印象是我的代码(也许还有Sedgewick)没有正确处理高度的增加 . 但我无法理解为什么

我显示了一个详细的执行跟踪,网络无法最大化(跟踪从第一个q.get()开始 . 括号中的值是高度的值.IN是到节点的传入流.OUT出来一个 .

例如,该行

4104 (2) --> 0 (1) pushing 1 from 4104 toward 0

参考符合条件的弧4104 - > 0 . 节点4104具有高度2并且节点0具有高度1.表达式"pushing 1"表示将1个统一的流推向目标节点(0) . 第 ================ 行分隔每个队列提取 . 队列是FIFO,并且在每次处理结束时打印其状态 .

请注意,很多时候零流量单位被推或减,但目标节点变为活动状态 .

这是执行跟踪

Initial Queue = 4104 4105 4106 4107 4108

Active node 4104 Height = 2 IN = 1 OUT = 0
    4104 (2) --> source (3) not eligible
    4104 (2) --> 0 (1) pushing 1 from 4104 toward 0
    4104 (2) --> 1 (1) pushing 0 from 4104 toward 1
    4104 (2) --> 2 (1) pushing 0 from 4104 toward 2
    4104 (2) --> 4 (1) pushing 0 from 4104 toward 4
    Excess = 0
    Queue = 4105 4106 4107 4108 0 1 2 4
================
Active node 4105 Height = 2 IN = 3 OUT = 0
    4105 (2) --> source (3) not eligible
    4105 (2) --> 1 (1) pushing 1 from 4105 toward 1
    4105 (2) --> 4 (1) pushing 1 from 4105 toward 4
    4105 (2) --> 6 (1) pushing 1 from 4105 toward 6
    Excess = 0
    Queue = 4106 4107 4108 0 1 2 4 6
================
Active node 4106 Height = 2 IN = 1 OUT = 0
    4106 (2) --> source (3) not eligible
    4106 (2) --> 1 (1) pushing 1 from 4106 toward 1
    4106 (2) --> 5 (1) pushing 0 from 4106 toward 5
    Excess = 0
    Queue = 4107 4108 0 1 2 4 6 5
================
Active node 4107 Height = 2 IN = 1 OUT = 0
    4107 (2) --> source (3) not eligible
    4107 (2) --> 1 (1) pushing 1 from 4107 toward 1
    4107 (2) --> 2 (1) pushing 0 from 4107 toward 2
    4107 (2) --> 3 (1) pushing 0 from 4107 toward 3
    4107 (2) --> 4 (1) pushing 0 from 4107 toward 4
    4107 (2) --> 6 (1) pushing 0 from 4107 toward 6
    Excess = 0
    Queue = 4108 0 1 2 4 6 5 3
================
Active node 4108 Height = 2 IN = 3 OUT = 0
    4108 (2) --> source (3) not eligible
    4108 (2) --> 1 (1) pushing 1 from 4108 toward 1
    4108 (2) --> 2 (1) pushing 1 from 4108 toward 2
    4108 (2) --> 4 (1) pushing 1 from 4108 toward 4
    4108 (2) --> 5 (1) pushing 0 from 4108 toward 5
    4108 (2) --> 6 (1) pushing 0 from 4108 toward 6
    Excess = 0
    Queue = 0 1 2 4 6 5 3
================
Active node 0 Height = 1 IN = 1 OUT = 0
    0 (1) --> sink (0) pushing 1 from 0 toward sink
    0 (1) --> 4104 (2) not eligible
    Excess = 0
    Queue = 1 2 4 6 5 3
================
Active node 1 Height = 1 IN = 4 OUT = 0
    1 (1) --> sink (0) pushing 2 from 1 toward sink
    1 (1) --> 4105 (2) not eligible
    1 (1) --> 4106 (2) not eligible
    1 (1) --> 4107 (2) not eligible
    1 (1) --> 4108 (2) not eligible
    Excess = 2    1 goes back onto queue with label 2
    Queue = 2 4 6 5 3 1
================
Active node 2 Height = 1 IN = 1 OUT = 0
    2 (1) --> sink (0) pushing 1 from 2 toward sink
    2 (1) --> 4108 (2) not eligible
    Excess = 0
    Queue = 4 6 5 3 1
================
Active node 4 Height = 1 IN = 2 OUT = 0
    4 (1) --> sink (0) pushing 2 from 4 toward sink
    4 (1) --> 4105 (2) not eligible
    4 (1) --> 4108 (2) not eligible
    Excess = 0
    Queue = 6 5 3 1
================
Active node 6 Height = 1 IN = 1 OUT = 0
    6 (1) --> sink (0) pushing 1 from 6 toward sink
    6 (1) --> 4105 (2) not eligible
    Excess = 0
    Queue = 5 3 1
================
Active node 5 Height = 1 IN = 0 OUT = 0
    5 (1) --> sink (0) pushing 0 from 5 toward sink
    Excess = 0
    Queue = 3 1
================
Active node 3 Height = 1 IN = 0 OUT = 0
    3 (1) --> sink (0) pushing 0 from 3 toward sink
    Excess = 0
    Queue = 1
================
Active node 1 Height = 2 IN = 4 OUT = 2
    1 (2) --> 4105 (2) not eligible
    1 (2) --> 4106 (2) not eligible
    1 (2) --> 4107 (2) not eligible
    1 (2) --> 4108 (2) not eligible
    Excess = 2    1 goes back onto queue with label 3
    Queue = 1
================
Active node 1 Height = 3 IN = 4 OUT = 2
    1 (3) --> 4105 (2) Reducing 1 from 1 toward 4105
    1 (3) --> 4106 (2) Reducing 1 from 1 toward 4106
    1 (3) --> 4107 (2) Reducing 0 from 1 toward 4107
    1 (3) --> 4108 (2) Reducing 0 from 1 toward 4108
    Excess = 0
    Queue = 4105 4106 4107 4108
================
Active node 4105 Height = 2 IN = 3 OUT = 2
    4105 (2) --> source (3) not eligible
    4105 (2) --> 1 (3) not eligible
    Excess = 1    4105 goes back onto queue with label 3
    Queue = 4106 4107 4108 4105
================
Active node 4106 Height = 2 IN = 1 OUT = 0
    4106 (2) --> source (3) not eligible
    4106 (2) --> 1 (3) not eligible
    4106 (2) --> 5 (1) pushing 1 from 4106 toward 5
    Excess = 0
    Queue = 4107 4108 4105 5
================
Active node 4107 Height = 2 IN = 1 OUT = 1
    4107 (2) --> source (3) not eligible
    4107 (2) --> 2 (1) pushing 0 from 4107 toward 2
    4107 (2) --> 3 (1) pushing 0 from 4107 toward 3
    4107 (2) --> 4 (1) pushing 0 from 4107 toward 4
    4107 (2) --> 6 (1) pushing 0 from 4107 toward 6
    Excess = 0
    Queue = 4108 4105 5 2 3 4 6
================
Active node 4108 Height = 2 IN = 3 OUT = 3
    4108 (2) --> source (3) not eligible
    4108 (2) --> 5 (1) pushing 0 from 4108 toward 5
    4108 (2) --> 6 (1) pushing 0 from 4108 toward 6
    Excess = 0
    Queue = 4105 5 2 3 4 6
================
Active node 4105 Height = 3 IN = 3 OUT = 2
    4105 (3) --> source (3) not eligible
    4105 (3) --> 1 (3) not eligible
    Excess = 1    4105 goes back onto queue with label 4
    Queue = 5 2 3 4 6 4105
================
Active node 5 Height = 1 IN = 1 OUT = 0
    5 (1) --> sink (0) pushing 1 from 5 toward sink
    5 (1) --> 4106 (2) not eligible
    Excess = 0
    Queue = 2 3 4 6 4105
================
Active node 2 Height = 1 IN = 1 OUT = 1
    2 (1) --> sink (0) pushing 0 from 2 toward sink
    2 (1) --> 4108 (2) not eligible
    Excess = 0
    Queue = 3 4 6 4105
================
Active node 3 Height = 1 IN = 0 OUT = 0
    3 (1) --> sink (0) pushing 0 from 3 toward sink
    Excess = 0
    Queue = 4 6 4105
================
Active node 4 Height = 1 IN = 2 OUT = 2
    4 (1) --> 4105 (4) not eligible
    4 (1) --> 4108 (2) not eligible
    Excess = 0
    Queue = 6 4105
================
Active node 6 Height = 1 IN = 1 OUT = 1
    6 (1) --> sink (0) pushing 0 from 6 toward sink
    6 (1) --> 4105 (4) not eligible
    Excess = 0
    Queue = 4105
================
Active node 4105 Height = 4 IN = 3 OUT = 2
    4105 (4) --> source (3) Reducing 1 from 4105 toward source
    4105 (4) --> 1 (3) pushing 0 from 4105 toward 1
    Excess = 0
    Queue = 1
================
Active node 1 Height = 3 IN = 2 OUT = 2
    1 (3) --> 4107 (2) Reducing 0 from 1 toward 4107
    1 (3) --> 4108 (2) Reducing 0 from 1 toward 4108
    Excess = 0
    Queue = 4107 4108
================
Active node 4107 Height = 2 IN = 1 OUT = 1
    4107 (2) --> source (3) not eligible
    4107 (2) --> 2 (1) pushing 0 from 4107 toward 2
    4107 (2) --> 3 (1) pushing 0 from 4107 toward 3
    4107 (2) --> 4 (1) pushing 0 from 4107 toward 4
    4107 (2) --> 6 (1) pushing 0 from 4107 toward 6
    Excess = 0
    Queue = 4108 2 3 4 6
================
Active node 4108 Height = 2 IN = 3 OUT = 3
    4108 (2) --> source (3) not eligible
    4108 (2) --> 5 (1) pushing 0 from 4108 toward 5
    4108 (2) --> 6 (1) pushing 0 from 4108 toward 6
    Excess = 0
    Queue = 2 3 4 6 5
================
Active node 2 Height = 1 IN = 1 OUT = 1
    2 (1) --> sink (0) pushing 0 from 2 toward sink
    2 (1) --> 4108 (2) not eligible
    Excess = 0
    Queue = 3 4 6 5
================
Active node 3 Height = 1 IN = 0 OUT = 0
    3 (1) --> sink (0) pushing 0 from 3 toward sink
    Excess = 0
    Queue = 4 6 5
================
Active node 4 Height = 1 IN = 2 OUT = 2
    4 (1) --> 4105 (4) not eligible
    4 (1) --> 4108 (2) not eligible
    Excess = 0
    Queue = 6 5
================
Active node 6 Height = 1 IN = 1 OUT = 1
    6 (1) --> sink (0) pushing 0 from 6 toward sink
    6 (1) --> 4105 (4) not eligible
    Excess = 0
    Queue = 5
================
Active node 5 Height = 1 IN = 1 OUT = 1
    5 (1) --> sink (0) pushing 0 from 5 toward sink
    5 (1) --> 4106 (2) not eligible
    Excess = 0
    Queue =

1 回答

  • 3

    你的问题

    Preflow

    来源没有明确的预流阶段 . 它包含在while中并且在谓词P> 0 && v == s为真时仅执行一次且仅执行一次 . 也许这是为了缩短代码

    是的,我认为这是为了压缩代码 . 由于在开始时分配 wt[s] = Edge.M*G.V() ,源具有足够的"virtual"过量,以便在算法的第一次迭代中为预流提供燃料 . 如果你想在你的实现中做同样的技巧,你必须在第一次迭代期间,当你遇到源节点时,膨胀 excess 变量(你在飞行中计算而不是将它存储在像Sedgewick这样的数组中) . 但是你不必这样做,你明确的预先喷射似乎很好,甚至可能使事情更具可读性 .

    我还怀疑在条件 P > 0 && v == s || h[v] == h[w]+1 中隐含的运算符优先级 (P > 0 && v == s) || h[v] == h[w]+1 不是意图 . 编写方式时,仅对源大小写执行检查 P > 0 . 在其他情况下不检查它不会有害,因为使用 P == 0 执行主体只会将0过量推到其他节点(没有看到你以相同的方式实现它 . 这没关系,即使轻微浪费计算时间 . 可能 P > 0 && (v == s || h[v] == h[w]+1) 真的是有意的 .

    队列中的接收器

    根据我的理解和本书的话语,接收器永远不会进入队列 . 但是,当高度增加时,代码会检查v!= t . 有什么理由吗?

    我同意,这很奇怪 . 接收器可以进入队列的唯一方法是同时作为源(在只有一个节点且没有边缘的图形中) . 但是,在这种情况下,条件 v != s 已经避免了无限循环,不需要额外的条件 .

    错误的结果

    执行后,生成的流程如下[...]使用此流程,流量值为8,但最大值为9

    初始高度

    您的结果错误,因为您的初始高度是错误的 . 我无法比较Sedgewick 's initialization code with yours because your post doesn' t指定 . 但是我从你的日志文件中了解到,对于 source ,你的高度为3 . 这显然是针对the conditions for height functionssource 必须以高于你图中节点数的高度开始,并在整个算法中保留它 .

    在您的情况下, source 的高度太接近其下游邻居的高度 . 这导致下游邻居很快将一些流量推回源,然后再努力让它流向更下游 . 只有当没有办法让它流到水槽时,它们才会这样做 .

    破碎的图表?

    让我更担心的是边缘 [4104 -> 1] 似乎消失了 . 虽然有人提到在节点4104的处理期间,它在节点1的处理期间从不出现 . 提到所有其他进入边缘,无论是关于"not eligible","pushing"还是"reducing" . 我知道你把这些日志记录的确切位置放在哪里了 . 不过,它留下了一点担忧,我想我会提到它,以防你在修好高度后仍然遇到麻烦 .

相关问题