lemon 代码分析之文本解析

2020-02-09  本文已影响0人  help_youself

 lemon主要用于处理图论相关的算法,最短路径,最大流最小割。lemon代码库中大量使用模板,c++语言的black magic。躲病毒期间,对代码进行了阅读,将自己的理解记录在这里。
相关代码摘取自lemon中的dijkstra_test.cc。
 图的表示方式:

char test_lgf[] =
  "@nodes\n"
  "label\n"
  "0\n"
  "1\n"
  "2\n"
  "3\n"
  "4\n"
  "@arcs\n"
  "     label length\n"
  "0 1  0     1\n"
  "1 2  1     1\n"
  "2 3  2     1\n"
  "0 3  4     5\n"
  "0 3  5     10\n"
  "0 3  6     7\n"
  "4 2  7     1\n"
  "@attributes\n"
  "source 0\n"
  "target 3\n";

 阅读代码的目标,上述文本中表示edge长度的数据,是怎么解析到相应的数据结构中的?即下述run函数,怎么将数据处理到LengthMap中。

template <class Digraph>
void checkDijkstra() {
  LengthMap length(G);

  std::istringstream input(test_lgf);
  digraphReader(G, input).
    arcMap("length", length).
    node("source", s).
    node("target", t).
    run();
}

 文本中的"@arcs\n"一下的内容会在readArcs函数中处理。这里的map_num就是 表示的是" label length\n"这句话中token的个数。

void readArcs() {
      while (readLine() && line >> c && c != '@') {
        line.putback(c);

        std::string source_token;
        std::string target_token;

//  the content in line,e,g: "0 1  0     1\n"
        if (!_reader_bits::readToken(line, source_token))
          throw FormatError("Source not found");

        if (!_reader_bits::readToken(line, target_token))
          throw FormatError("Target not found");

        std::vector<std::string> tokens(map_num);
        for (int i = 0; i < map_num; ++i) {
          if (!_reader_bits::readToken(line, tokens[i])) {
            std::ostringstream msg;
            msg << "Column not found (" << i + 1 << ")";
            throw FormatError(msg.str());
          }
        }
        if (line >> std::ws >> c)
          throw FormatError("Extra character at the end of line");

        Arc a;
        if (!_use_arcs) {

          typename NodeIndex::iterator it;

          it = _node_index.find(source_token);
          if (it == _node_index.end()) {
            std::ostringstream msg;
            msg << "Item not found: " << source_token;
            throw FormatError(msg.str());
          }
          Node source = it->second;

          it = _node_index.find(target_token);
          if (it == _node_index.end()) {
            std::ostringstream msg;
            msg << "Item not found: " << target_token;
            throw FormatError(msg.str());
          }
          Node target = it->second;

          a = _digraph.addArc(source, target);
          if (label_index != -1)
            _arc_index.insert(std::make_pair(tokens[label_index], a));
        } else {
          if (label_index == -1)
            throw FormatError("Label map not found");
          typename std::map<std::string, Arc>::iterator it =
            _arc_index.find(tokens[label_index]);
          if (it == _arc_index.end()) {
            std::ostringstream msg;
            msg << "Arc with label not found: " << tokens[label_index];
            throw FormatError(msg.str());
          }
          a = it->second;
        }

        for (int i = 0; i < static_cast<int>(_arc_maps.size()); ++i) {
          _arc_maps[i].second->set(a, tokens[map_index[i]]);
        }

      }
      if (readSuccess()) {
        line.putback(c);
      }
}

 _arc_maps[i].second->set(a, tokens[map_index[i]])对arc与长度进行处理。_arc_maps就是在arcMap("length", length)注册的。

    template <typename Map>
    DigraphReader& arcMap(const std::string& caption, Map& map) {
      checkConcept<concepts::WriteMap<Arc, typename Map::Value>, Map>();
      _reader_bits::MapStorageBase<Arc>* storage =
        new _reader_bits::MapStorage<Arc, Map>(map);
      _arc_maps.push_back(std::make_pair(caption, storage));
      return *this;
    }

 MapStorage对象中对set函数进行追踪。

    template <typename _Item, typename _Map,
              typename _Converter = DefaultConverter<typename _Map::Value> >
    class MapStorage : public MapStorageBase<_Item> {
    public:
      virtual void set(const Item& item ,const std::string& value) {
        _map.set(_graph.direct(item, dir), _converter(value));
      }
}

 追踪LengthMap中的set函数。LengthMap的定义在lemon/bits/graph_extender.h

  template <typename _Value>
  class ArcMap
     : public MapExtender<DefaultMap<Graph, Arc, _Value> > 
  template <typename _Graph, typename _Item, typename _Value>
  class DefaultMap
    : public DefaultMapSelector<_Graph, _Item, _Value>::Map
  template <typename _Graph, typename _Item, typename _Value>
  struct DefaultMapSelector {
    typedef ArrayMap<_Graph, _Item, _Value> Map;
  };

  template <typename _Graph, typename _Item, typename _Value>
  class ArrayMap
    : public ItemSetTraits<_Graph, _Item>::ItemNotifier::ObserverBase {
  public:
    const Value& operator[](const Key& key) const {
      int id = Parent::notifier()->id(key);
      return values[id];
    }
    void set(const Key& key, const Value& val) {
      (*this)[key] = val;
    }
private:
Value* values;// when this is allocated?
};

 第二个问题出现,Value* values的内存空间是何时分配的?readArcs中的这句话a = _digraph.addArc(source, target)会通知(notifier)observer进行内存分配。这就是ArrayMap继承ObserverBase的作用。

  class ListDigraph : public ExtendedListDigraphBase {
public:
    Arc addArc(Node s, Node t) {
      return Parent::addArc(s, t);
    }
   };
  template <typename Base>
  class DigraphExtender : public Base {
public:
    Arc addArc(const Node& from, const Node& to) {
      Arc arc = Parent::addArc(from, to);
      notifier(Arc()).add(arc);
      return arc;
    }
typedef AlterationNotifier<DigraphExtender, Arc> ArcNotifier;
    ArcNotifier& notifier(Arc) const {
      return arc_notifier;
    }
};
  template <typename _Container, typename _Item>
  class AlterationNotifier {
  public:
   void add(const Item& item) {
      typename Observers::reverse_iterator it;
      try {
        for (it = _observers.rbegin(); it != _observers.rend(); ++it) {
          (*it)->add(item);
        }
      } catch (...) {
        typename Observers::iterator jt;
        for (jt = it.base(); jt != _observers.end(); ++jt) {
          (*jt)->erase(item);
        }
        throw;
      }
    }
};

 ArrayMap中的add函数进行内存分配。

  template <typename _Graph, typename _Item, typename _Value>
  class ArrayMap
    : public ItemSetTraits<_Graph, _Item>::ItemNotifier::ObserverBase {
  protected:

    // \brief Adds a new key to the map.
    //
    // It adds a new key to the map. It is called by the observer notifier
    // and it overrides the add() member function of the observer base.
    virtual void add(const Key& key) {
      Notifier* nf = Parent::notifier();
      int id = nf->id(key);
      if (id >= capacity) {
        int new_capacity = (capacity == 0 ? 1 : capacity);
        while (new_capacity <= id) {
          new_capacity <<= 1;
        }
        Value* new_values = allocator.allocate(new_capacity);
        Item it;
        for (nf->first(it); it != INVALID; nf->next(it)) {
          int jd = nf->id(it);;
          if (id != jd) {
            allocator.construct(&(new_values[jd]), values[jd]);
            allocator.destroy(&(values[jd]));
          }
        }
        if (capacity != 0) allocator.deallocate(values, capacity);
        values = new_values;
        capacity = new_capacity;
      }
      allocator.construct(&(values[id]), Value());
    }
};

 这里分析的最短路径算法是dijkstra。这个算法里面的逻辑挺容易理解的。但是另一个最短路径算法floyd算法,代码五行,但是很不容易理解。
 floyd算法

for(int k=1;k<=n;m++)
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            d[i][j]=min(d[i][j],d[i][k]+d[k][j]); 

 找了一些资料[2,3,4],看了之后,也是迷迷糊糊的。

从任意节点i到任意节点j的最短路径不外乎2种可能,1是直接从i到j,2是从i经过若干个节点k到j。所以,我们假设Dis(i,j)为节点u到节点v的最短路径的距离,对于每一个节点k,我们检查Dis(i,k) + Dis(k,j) < Dis(i,j)是否成立,如果成立,证明从i到k再到j的路径比i直接到j的路径短,我们便设置Dis(i,j) = Dis(i,k) + Dis(k,j),这样一来,当我们遍历完所有节点k,Dis(i,j)中记录的便是i到j的最短路径的距离。

 我觉得需要从这个推理公式理解,当k=1时,若在一次三层循环执行完毕后,Dis(i,1) + Dis(1,j) < Dis(i,j)成立,则i,j之间的最短路径必须进过m。接下来的循环就是遍历其他的k点,是否可以进一步使得i->1之间的距离进一步缩小(1->j同理)。
[1]lemon code
[2]最短路四大算法证明以及分析
[3]Floyd算法的证明
[4]最短路径—Dijkstra算法和Floyd算法

上一篇下一篇

猜你喜欢

热点阅读