経路の道順を求める
もいっちょいくよー。お次は隣接行列の各成分を「橋の名前」に置き換えます。
んでもって「文字のまま」で行列の積を計算すべく乗算・加算の定義を差し替えます。
#include "sparse/matrix.h" #include "sparse/multiplies.h" #include "sparse/fill_diagonal.h" #include <string> // トークンの切り出し template<typename T> bool tokenize(std::basic_string<T>& str, std::basic_string<T>& token, const std::basic_string<T>& delim) { using tstring = std::basic_string<T>; tstring::size_type bpos = str.find_first_not_of(delim); if ( bpos == tstring::npos ) return false; tstring::size_type epos = str.find_first_of(delim, bpos); if ( epos == tstring::npos ) { token = str.substr(bpos); str.clear(); } else { token = str.substr(bpos, epos-bpos); str.erase(0, epos+1); } return true; } // 乗算 <*> // 0 <*> 0 = 0 // 0 <*> 'x' = 0 // "x" <*> 0 = 0 // "a+b" <*> 'c' = "ac+bc" struct nullable_multiplies { std::string* operator()(const std::string* ap, const char* bp, std::string* result) const { if ( ap == nullptr ) return nullptr; if ( bp == nullptr ) return nullptr; result->clear(); bool atfirst = true; std::string delim = "+"; std::string token; std::string str = *ap; char ch = *bp; if ( str.length() == 0 ) { *result = ch; } else { while ( tokenize(str, token, delim) ) { if ( atfirst ) atfirst = false; else *result += delim; *result += token; *result += ch; } } return result; } }; // 加算 <+> // 0 <+> 0 = 0 // 0 <+> "x" = "x" // "x" <+> 0 = "x" // "x" <+> "y" = "x+y" struct nullable_plus { std::string* operator()(const std::string* ap, const std::string* bp, std::string* result) const { if ( ap == nullptr && bp == nullptr ) return nullptr; if ( ap == nullptr ) *result = *bp; else if ( bp == nullptr ) *result = *ap; else *result = (*ap) + "+" + (*bp); return result; } }; #include <iostream> // cout, endl int main() { using namespace sparse; matrix<std::string> A; fill_diagonal(A, 4, std::string()); matrix<char> B; B.insert(0, 0, 'a'); B.insert(0, 1, 'b'); B.insert(0, 2, 'c'); B.insert(1, 2, 'd'); B.insert(2, 3, 'e'); B.insert(3, 1, 'f'); B.finalize(); nullable_multiplies mulop; nullable_plus addop; A = multiplies(A, B, 4, 4, 4, mulop, addop); A = multiplies(A, B, 4, 4, 4, mulop, addop); A = multiplies(A, B, 4, 4, 4, mulop, addop); A.enumerate([](int row, int col, std::string val) { std::cout << '[' << row << ',' << col << "] " << val << std::endl;}); std::cout << std::endl; }
この結果は「島iから島jに到達するときに通過する橋のリスト」なんですね。"[0,2] aac+abd" は「島0から3本の橋を渡って島2に行くには、a,a,cまたはa,b,dの順に渡れ」ってことです。
最後の例:文字式での行列積は状態遷移の検証/テストに使えますね。「ある状態からある状態へn個のイベントで遷移できるか、できるならどのイベントがどんな順序で発生すればいいか」が示されますからね。
……ゴールデンウィークはこんなコードで遊んでました、TwitterやFacebokあるいはWebページのリンクを基に巨大なグラフを作ってこんな演算を施せば、かなり面白い結果が得られそうです。