SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

PEARライブラリ活用

PHPにおけるグラフ描画とアルゴリズム

PEARライブラリ活用 (3)


  • X ポスト
  • このエントリーをはてなブックマークに追加

ダウンロード サンプル (3.0 KB)

PEAR Structures_Graphでグラフを操作する

 もちろん、グラフは描いて終わりではありません。地図をグラフで表現すれば、2地点間の最短経路や最短時間などを求めることができます(場所をノードとし、距離あるいは時間をエッジの重み属性にします)。部品の依存関係をグラフで表せば、生産のプランニングを行うことができます(部品をノードとし、その依存関係をエッジで表現します)。

 残念なことにグラフを描画するのに利用したImage_GraphVizは、グラフのための高度なアルゴリズムを持っていません。別のパッケージStructures_Graphを使います(逆に、Structures_Graphには描画機能はありません)。悪いことに、この2つのパッケージはどちらもグラフ・オブジェクトを生成するのですが、両者に直接の互換性はありません。本格的に使う場合は、何かしらの変換メカニズムや、抽象化が必要になるでしょう。

グラフの生成

 次のようなグラフを生成します(ここでは描画は行いません)。これ自体は抽象的なグラフととらえてもらってかまわないのですが、具体的なイメージがないと理解しにくい場合には、ある製品の製造工程において、「1」という部品を材料にして、「node #2」という部品と「第3ノード」という部品を作っているとでも考えてください。

グラフの例7(graph7a.phpで描画)
グラフの例7(graph7a.phpで描画)

 まず、グラフを生成します(添付ファイルの「graph7b.php」を参照)。

require_once 'Structures/Graph.php';

$graph = new Structures_Graph();

 デフォルトでは有向グラフが生成されます。Structures_Graph(false)とすれば無向グラフになります。

ノードの生成

 3つのノードを生成し、名前を付けます。名前は何でもかまいません。

$nodeOne = new Structures_Graph_Node();
$nodeTwo = new Structures_Graph_Node();
$nodeThree = new Structures_Graph_Node();

$nodeOne->setData(1);
$nodeTwo->setData('node #2');
$nodeThree->setData('第3ノード');

 生成したノードをグラフに追加します。

$graph->addNode(&$nodeOne);
$graph->addNode(&$nodeTwo);
$graph->addNode(&$nodeThree);

エッジの生成

 nodeOneとnodeTwo、nodeOneとnodeThreeの間にエッジを生成します。

$nodeOne->connectTo($nodeTwo);
$nodeOne->connectTo($nodeThree);

ノードの性質

 グラフのノードが持つエッジの数を次数(degree)と言います。次のようにして、ノードに入るエッジとノードから出るエッジの数が分かります。

echo "<p>NodeOne's inDegree: " . $nodeOne->inDegree()."</p>"; // 0
echo "<p>NodeOne's outDegree: " . $nodeOne->outDegree()."</p>"; // 2

 あるノードにつながっているノードはgetNeighbours()で取得できます。

echo '<ul>';
foreach($nodeOne->getNeighbours() as $node)
  echo '<li>'.$node->getData().'</li>';
echo '</ul>';

 結果は次のとおりです。

  • node #2
  • 第3ノード

トポロジカル・ソート

 少し高度なアルゴリズムを実行してみましょう。トポロジカル・ソートです。トポロジカル・ソートの結果はノードの列です。グラフのすべてのエッジについて、そのヘッド(矢印の起点)のノードの順番が、テール(矢印の終点)のノードの順番よりも小さくなっていなければなりません。これは、ノードが部品、エッジがその依存関係であるときに、製品を完成させることができる部品の作成順序を与えます。

 例えば下のグラフでは、ノード1、7、9がノード3のテールになっているので、トポロジカル・ソートの結果において、1、7、9は3より後に来なければなりません。当然ですが、グラフにループがあると、トポロジカル・ソートはできません。

トポロジカル・ソートするグラフ
トポロジカル・ソートするグラフ

グラフの生成

 有向グラフを生成します(添付ファイルの「graph8.php」を参照)。

require_once 'Structures/Graph/Manipulator/TopologicalSorter.php';

$graph=new Structures_Graph();

ノードとエッジの生成

 エッジの配列からノードとエッジを生成しましょう。

//エッジを配列に格納しておく
$edges=array(
  array(3,9),
  array(3,7),
  array(3,1),
  array(9,2),
  array(9,8),
  array(7,8),
  array(7,6),
  array(8,4),
  array(8,5)
);

foreach($edges as $edge){//$edgesのすべてのエッジについて
  for($i=0;$i<2;++$i){//ノードを処理する
    if(!$nodes[$edge[$i]]){//ノードが生成されていなければ
      $nodes[$edge[$i]]=new Structures_Graph_Node();//ノードを生成し、
      $nodes[$edge[$i]]->setData($edge[$i]);//名前を付け
      $graph->addNode(&$nodes[$edge[$i]]);//グラフに追加する
    }
  }
  $nodes[$edge[0]]->connectTo($nodes[$edge[1]]);//ノード同士をつなぐ
}

ループの検出

 Structures_Graphにはループを検出するためのメソッドStructures_Graph_Manipulator_AcyclicTest::isAcyclicが用意されています。先に述べたように、グラフにループがあるとトポロジカル・ソートはできないので、まず、このメソッドを使って、ループがないことを確かめましょう。

トポロジカル・ソートの実行・結果の表示

 トポロジカル・ソートを実行し、結果を変数に格納します。

if(Structures_Graph_Manipulator_AcyclicTest::isAcyclic($graph)){
  $result=@Structures_Graph_Manipulator_TopologicalSorter::sort($graph);

 結果はノードの配列の配列になっているので、次のように表示させることができます。

  echo '<p>';
  foreach($result as $level) foreach($level as $node)
    echo $node->getData().' '; // 3 9 7 1 2 8 6 4 5
  echo '</p>';
}

 グラフの画像と照らし合わせると、トポロジカル・ソートの条件を満たしていることが分かります。

他には?

 残念なことに、Structures_Graphはまだ発展途上のパッケージで、グラフのための高度なアルゴリズムは、トポロジカル・ソート以外には実装されていません。とはいえ、ノードやエッジの基本的な操作は実装されているので、必要なものを自分で実装することはもちろん可能です。高度で高性能なグラフ・アルゴリズムが必要な場合には、PHPの標準的な枠組みにとらわれるべきではないでしょう。例えば、標準になりつつあるC++ Boostには、Boost Graph Library (BGL)というライブラリが用意されています。このライブラリは、グラフに関する基本的なアルゴリズムを網羅している上に、C++なので性能も高いです。

おわりに

 PEAR Image_GraphVizを使ってグラフを描画する方法と、PEAR Structures_Graphを使ってグラフを使ったアルゴリズムを実行する方法を紹介しました。

 Image_GraphVizを使えば、グラフを簡単に描画することができます。このライブラリは、広く使われているグラフ描画ツールであるGraphvizを内部で利用しており、Graphvizについて調べることで、描画方法をさまざまにカスタマイズすることができます。

 Structures_Graphはまだ発展途上と言えるライブラリですが、PHP上でグラフを扱う方法と、簡単なアルゴリズムを提供してくれます。

 注意しなければならないことは、この2つのライブラリのグラフ・オブジェクトに、直接の互換性がないことです。将来、PEARの名の下に、両者が統一されることを期待しましょう。

参考文献

  • R. ブランデンベルク, P. グリッツマン著, 石田基広訳. 最短経路の本. シュプリンガー・ジャパン, 2007.
  • 対話形式によるグラフ理論への入門書です。
  • トマス・H. コルメンほか著, 浅野哲夫ほか訳. アルゴリズムイントロダクション(第2巻). 近代科学社, 改訂2版, 2007.
  • アルゴリズムの教科書の定番です。原著は1冊ですが、日本語訳は3分冊になっています。第2巻でグラフ・アルゴリズムが詳しく解説されています。
  • ドナルド・E・クヌース著, 有澤誠ほか訳. The Art of Computer Programming Volume1 Fundamental Algorithms, アスキー, 原著第3版, 2004.
  • アルゴリズムのバイブルです。全7巻の予定で刊行中の第1巻で、トポロジカル・ソートについて解説されています。
  • 加納幹雄. 情報科学のためのグラフ理論. 朝倉書店, 2001.
  • グラフ理論の入門書です。グラフを使って解くパズルなど、さまざまな事例が紹介されています。
修正履歴

この記事は参考になりましたか?

  • X ポスト
  • このエントリーをはてなブックマークに追加
PEARライブラリ活用連載記事一覧

もっと読む

この記事の著者

山田 祥寛(ヤマダ ヨシヒロ)

静岡県榛原町生まれ。一橋大学経済学部卒業後、NECにてシステム企画業務に携わるが、2003年4月に念願かなってフリーライターに転身。Microsoft MVP for Visual Studio and Development Technologies。執筆コミュニティ「WINGSプロジェクト」代表。主な著書に「独習シリーズ(Java・C#・Python・PHP・Ruby・JSP&サーブレットなど)」「速習シリーズ(ASP.NET Core・Vue.js・React・TypeScript・ECMAScript、Laravelなど)」「改訂3版JavaScript本格入門」「これからはじめるReact実践入門」「はじめてのAndroidアプリ開発 Kotlin編 」他、著書多数

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

WINGSプロジェクト 矢吹 太朗(ヤブキ タロウ)

WINGSプロジェクトについて>有限会社 WINGSプロジェクトが運営する、テクニカル執筆コミュニティ(代表 山田祥寛)。主にWeb開発分野の書籍/記事執筆、翻訳、講演等を幅広く手がける。2018年11月時点での登録メンバは55名で、現在も執筆メンバを募集中。興味のある方は、どしどし応募頂きたい。著書記事多数。 RSS X: @WingsPro_info(公式)、@WingsPro_info/wings(メンバーリスト) Facebook

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

  • X ポスト
  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/2418 2008/05/16 16:13

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング