paint-brush
アルゴリズムの魔術: 日本為替におけるグラフ理論の活用 に@optiklab
1,062 測定値
1,062 測定値

アルゴリズムの魔術: 外国為替におけるグラフ理論の活用

Anton Yarkov22m2023/10/28
Read on Terminal Reader

長すぎる; 読むには

以前の取り組みに続いて、私は収益性の高い通貨交換の方法を探して、グラフとグラフ走査アルゴリズムをいじり続けています。
featured image - アルゴリズムの魔術: 外国為替におけるグラフ理論の活用
Anton Yarkov HackerNoon profile picture
FinTech スタートアップ業界に詳しい方なら、イギリスのロンドンに拠点を置く世界闻名な FinTech 大掌企業 Revolut について聞いたことがあるかもしれません。 2015 年に設立された Revolut は多額の投資を集め、瑞典で最も神速に成長している新興企業の 1 つとなり、多くの欧州中国人民に銀行サービスを给予しています。


銀行業務は収益をどのように生み出しているかという点では謎に包まれていることが多いが、2040年と202在一年のRevolutに関するいくつかの最重要な数字式は、その収入源にいくらかの光を当てている。


Revolut損益計算書


図に示すように、このネオバンクの収益のかなりの局部は、其他国家為替 (FX)、資産安全管理 (仮想通貨を含む)、およびカード サービスから来ています。注目すべきは、2022年にFXが最も収益性の高いセクターとなったことです。


ソフトウェア エンジニアでもある私の故友は、数载前に Revolut のソフトウェア エンジニアリング部門で技術面接を受けたときの興味深い話をしてくれました。彼は、1 つまたは複数の中間通貨を安全使用して 2 つの通貨を交換する最も収益性の高い方式を特殊するアルゴリズムを開発する任務を負っていました。言い換えれば、彼らは通貨调解书取引の戦略を探していたのです。


通貨アービトラージは、通貨トレーダーが複数の取引を通じて特定の通貨ペアに対してブローカーによって提供されるさまざまなスプレッドを活用する取引戦略です。


アルゴリズムの基礎はグラフ理論に根ざしている用得着があることがタスクで明確に述べられました。


FXの基礎

FX (美国為替) は当今宇宙貿易において極めて根本な役割を果たし、共同につながった当今宇宙の機能を支えています。銀行を最も裕福な組織にする上で、為替も根本な役割を果たしているのは明らかです。


洋為替から生じる财产权は主に、買値 (BID) と売値 (ASK) の差またはスプレッドです。この差は、トランザクションごとにわずかに見えるかもしれませんが、毎日の操控量を考慮すると、累積すると数百人万ドルの财产权になる或许性があります。これにより、一部分の企業は位置に自動化された財務業務だけで成長できるようになります。


FX (洋淘為替) の分野では、常に EUR/USD などの通貨ペアを扱います。ほとんどの場合、これらの為替は买卖双方向 (つまり、EUR/USD および USD/EUR) であり、為替レートの値は多方面向で異なります。


アービトラージ ペアは、 2 つの通貨 (たとえば、ユーロと米ドル) の価値間の比率を数値で表し、通貨間の為替レートを決定します。


可能性としては、確実な賭けとして知られる、収益性の高い取引のために複数の中間通貨を使用できる可能性があります。


アービトラージ シュア ベットは、循環的に使用される一連のペアです。


多くのプロバイダーは、自社の利润を確保し、他のプロバイダーがそこから利润を得ることを防ぐために、数学题的モデリングと定性分析を採用しています。したがって、ここでは「暗藏的」という用語が強調されています。


確実なベットの長さは、潜在的な裁定取引の機会のセットを構成するペアの数を指します。


現実の全世界では、為替レートは銀行や為替プラットフォームによって異なる場合があります。旅游者が有机会な限り最良の料金を見つけるために都士を横断することは珍しくありません。コンピューター ソフトウェアを适用すると、プロバイダーのリストにアクセスできる場合、このプロセスは数ミリ秒以里に実行できます。実際の収益性の高い取引では、複数のステップで、さまざまな取引所プラットフォームにわたるさまざまな通貨を介した変換が含まれる場合があります。言い換えれば、民事判决サークルは极为に広範囲に及ぶ可以性があります。


アービトラージサークルでは、通貨を取得し、それを別のプラットフォームに転送し、他の通貨と交換し、最終的には元の通貨に戻します。


1 つ以上の中間通貨を介した 2 つの通貨間の為替レートは、これらの中間取引の為替レートの積として計算されます。


たとえば、スイスのフランクを米ドルで購入し、次にフランクを法国円に交換し、さらに円を米ドルで売却するとします。 2023 年秋の為替レートは次のとおりです。


  1. 1 USD で 0.91 CHF (スイス フランク) を購入できます。
  2. 1スイスフランで163.16円で買えます。
  3. 1当地円で0.0067米ドルが購入できます。


それを表で示してみましょう。
 1 USD | 1 CHF | 1 YEN 0.91 CHF | 163.16 YEN | 0.0067 USD ----------------|-------------------|-------------- 1.098901099 | 0.006128953 | 149.2537313


次に、これらの値の積を見つける必要があります。この積が 1 未満の値を生成する場合、一連のトランザクションは利益をもたらします

 1.098901099 * 0.006128953 * 149.2537313 = 1.005240803


ご覧のとおり、結果は 1 より大きいため、資金の 0.05% を失ったようです。しかし、正確には何個でしょうか?次のように特别整理できます。
 0.91 CHF * 163.16 (YEN per 1 CHF) * 0.0067 (USD per 1 YEN) = 0.99478652 US Dollars


したがって、真正に 1 米ドルを販売した後、最終的には 0.994 を手に入れました。これは 1 米ドル未満です。


簡単に言うと、调解书サイクルは、同じ通貨の 1 単位未満で 1 単位の通貨を拿得できる場合に权益が得られます。


初始のトランザクションで 1 米ドルあたり 0.91 スイスフランではなく、0.92 スイスフランを受け取る機会を見つけたと想象作文してみましょう。
 1 USD | 1 CHF | 1 YEN 0.92 CHF | 163.16 YEN | 0.0067 USD ----------------|-------------------|-------------- 1.086956522 | 0.006128953 | 149.2537313


製品は 1 未満になります。
 1.086956522 * 0.006128953 * 149.2537313 = 0.994314272


つまり、実際の通貨では 1 米ドル上述の価値が得られることになります。
 0.92 CHF * 163.16 (YEN per 1 CHF) * 0.0067 (USD per 1 YEN) = 1.00571824 US Dollars


うわー、利益が出ました!次に、グラフ分析を使用してこれを自動化する方法を見てみましょう。

したがって、3 つのアービトラージ ペアのアービトラージ サークルで既得利益または損失を確認する式は次のようになります。
 USD/CHF * CHF/YEN * YEN/USD < 1.0

グラフ表現

これらのプロセスを自動化するには、グラフを安全使用できます。所诉のテーブルは、ノードが通貨を表し、エッジが买卖双方向の交換を表すグラフの行数据库現に自然规律に変換できます。


したがって、次のように 2 つのペアの交換を行和列で表すのは簡単です。
 EUR USD 1 1 EUR 1 1 USD


関与するペアの数に応じて、队伍は次のように拡張できます。
 EUR USD YEN CHF 1 1 1 1 EUR 1 1 1 1 USD 1 1 1 1 YEN 1 1 1 1 CHF


したがって、より多くの交換プラットフォームとリソースを考慮すると、たとえ 2 つの通貨であっても、テーブルがかなり大きくなる概率性があります。


現実の通貨栽定取引の問題に対処するために、通貨相場のすべての関係を網羅する已经なグラフがよく借助されます。 3 通貨為替テーブルは次のようになります。
 USD CHF YEN { 1.0, 1.10, 0.0067 } USD { 0.91, 1.0, 0.0061 } CHF { 148.84, 163.16, 1.0 } YEN


単純なを的使用して、メモリ内の通貨ペアを表すことができます。
 class GraphNode { public: string Name; }; class Graph { public: vector<vector<double>> Matrix; vector<GraphNode> Nodes; };


ここで必要性なのは、このグラフをたどって最も収益性の高い円を見つける步骤を見つけることだけです。しかし、まだ問題が1つあります...

数学がまた私たちを救う

古典的なグラフアルゴリズムは、辺の長さの合計として定義されたパスを見つけるように設計されているため、辺の長さの積を扱うのにはあまり適していません ( のよく知られた古典的な経路探索アルゴリズムの実装を参照してください) )。


ただし、この制限を躲避するには、積から和に変換する小学数学的な办法、つまり対数があります。積が対数で表される場合は、対数の合計に変換できます。


対数


この式子式の右側では、必要性な数値は 1 未満であり、この数値の対数はゼロ未満でなければならないことを示しています。
 LogE(USD/CHF) * LogE(CHF/YEN) * LogE(YEN/USD) < 0.0


この単純な数学的トリックにより、エッジ長の積が1 未満のサイクルの検索から、エッジ長の合計が 0 未満のサイクルの検索に移行することができます。

列数の値を LogE(x) に変換し、小数点下述 2 桁で四捨五入すると、次のようになります。
 USD CHF YEN { 0.0, 0.1, -5.01 } USD { -0.09, 0.0, -5.1 } CHF { 5.0, 5.09, 0.0 } YEN


この問題は、中国古风的なグラフ アルゴリズムを运用することでより解決或许になります。必不可少なのは、グラフを横断して最も収益性の高い交換経路を探すことです。

グラフアルゴリズム

どのアルゴリズムにも限界があります。それらのいくつかについてはで説明しました。

グラフには負の重みが含まれている可能性があり、グラフを横断するときに負のサイクルが発生する可能性があるため、ここでは古典的な BFS、DFS、さらにはダイクストラを適用することはできません。アルゴリズムは反復ごとにより良い解決策を継続的に見つけるため、負のサイクルはアルゴリズムに課題をもたらします。


この問題に対処するために、ベルマン-フォード アルゴリズムは単純に反復回数を制限します。これは、グラフの各エッジを 1 サイクルで走査し、すべてのエッジに V-1 回这の緩和を適用します (V はノードの数です)。


したがって、ベルマン・フォード アルゴリズムは、このアービトラージ システムの重点にあり、2 つの最重要な基準を満たすグラフ内の 2 つのノード間のパスの発見を也许にします。つまり、それらのアルゴリズムには負の重みが含まれており、負のサイクルの十部ではありません。


このアルゴリズムは理論的には簡単ですが (それに関するビデオが何億億もあります)、ニーズに合わせて実際に実装するにはある水平の付出が必要性です。それを掘り下げてみましょう。

ベルマン・フォードアルゴリズムの実装

この記事の原则はコンピュータサイエンスであるため、実際の為替レートとは関係のない直埋の為替レートを采用します。


アルゴリズムをよりスムーズに认为するために、負のサイクルがまったく含まれていないグラフを运行してみましょう。
 graph.Nodes.push_back({ "USD" }); graph.Nodes.push_back({ "CHF" }); graph.Nodes.push_back({ "YEN" }); graph.Nodes.push_back({ "GBP" }); graph.Nodes.push_back({ "CNY" }); graph.Nodes.push_back({ "EUR" }); // Define exchange rates for pairs of currencies below // USD CHF YEN GBP CNY EUR graph.Matrix = { { 0.0, 0.41, INF, INF, INF, 0.29 }, // USD { INF, 0.0, 0.51, INF, 0.32, INF }, // CHF { INF, INF, 0.0, 0.50, INF, INF }, // YEN { 0.45, INF, INF, 0.0, INF, -0.38 }, // GBP { INF, INF, 0.32, 0.36, 0.0, INF }, // CNY { INF, -0.29, INF, INF, 0.21, 0.0 } }; // EUR


下面的のコード例では、グラフに負のサイクルがない場合に、Bellman-Ford アルゴリズムを使用的して 2 つのノード間のパスを見つけます。
 vector<double> _shortestPath; vector<int> _previousVertex; void FindPath(Graph& graph, int start) { int verticesNumber = graph.Nodes.size(); _shortestPath.resize(verticesNumber, INF); _previousVertex.resize(verticesNumber, -1); _shortestPath[start] = 0; // For each vertex, apply relaxation for all the edges V - 1 times. for (int k = 0; k < verticesNumber - 1; k++) for (int from = 0; from < verticesNumber; from++) for (int to = 0; to < verticesNumber; to++) if (_shortestPath[to] > _shortestPath[from] + graph.Matrix[from][to]) { _shortestPath[to] = _shortestPath[from] + graph.Matrix[from][to]; _previousVertex[to] = from; } }


中国人民元に対してこのコードを実行すると、 _previousVertex配列が埋められ、次のような結果が得られます。

 Path from 4 to 0 is : 4(CNY) 3(GBP) 0(USD) Path from 4 to 1 is : 4(CNY) 3(GBP) 5(EUR) 1(CHF) Path from 4 to 2 is : 4(CNY) 3(GBP) 5(EUR) 1(CHF) 2(YEN) Path from 4 to 3 is : 4(CNY) 3(GBP) Path from 4 to 4 is : 4(CNY) Path from 4 to 5 is : 4(CNY) 3(GBP) 5(EUR)


ご覧のとおり、CNY と他のさまざまな通貨の間の最適なパスが指定区域されます。


繰り返しますが、最適なものを 1 つだけ見つけることには的点を当てません。これは比較的単純なタスクであり、この記事の原因ではないためです。


上記の実装は、好的な場合にはうまく機能しますが、負のサイクルを含むグラフを扱う場合には不是です。

負のサイクルの検出

本当に必要条件なのは、グラフに負のサイクルが含まれているかどうかを其他し、含まれている場合は問題のあるセグメントを其他する機能です。この知識により、これらの問題を軽減し、最終的に収益性の高いチェーンを発見することができます。


反復回数は、常に正確に V - 1 に達する需要はありません。(N+1) 番目のサイクルで、N 番目のサイクルでのパスよりも優れたパスが発見されない場合、解は見つかったとみなされます。したがって、わずかに最適化の的余地があります。


上述のコードは、パスを見つけるだけでなく、グラフに負のサイクルが含まれているかどうかを検出するように拡張できます。これには、上述した最適化も含まれます。
 vector<double> _shortestPath; vector<int> _previousVertex; bool ContainsNegativeCycles(Graph& graph, int start) { int verticesNumber = graph.Nodes.size(); _shortestPath.resize(verticesNumber, INF); _previousVertex.resize(verticesNumber, -1); _shortestPath[start] = 0; // For each vertex, apply relaxation for all the edges V - 1 times. for (int k = 0; k < verticesNumber - 1; k++) { updated = false; for (int from = 0; from < verticesNumber; from++) { for (int to = 0; to < verticesNumber; to++) { if (_shortestPath[to] > _shortestPath[from] + graph.Matrix[from][to]) { _shortestPath[to] = _shortestPath[from] + graph.Matrix[from][to]; _previousVertex[to] = from; updated = true; } } } if (!updated) // No changes in paths, means we can finish earlier. break; } // Run one more relaxation step to detect which nodes are part of a negative cycle. if (updated) for (int from = 0; from < verticesNumber; from++) for (int to = 0; to < verticesNumber; to++) if (_shortestPath[to] > _shortestPath[from] + graph.Matrix[from][to]) // A negative cycle has occurred if we can find a better path beyond the optimal solution. return true; return false; }


そして今度は、負のサイクルを含むより複雑なグラフを使ってみましょう。
 graph.Nodes.push_back({ "USD" }); // 1 (Index = 0) graph.Nodes.push_back({ "CHF" }); graph.Nodes.push_back({ "YEN" }); graph.Nodes.push_back({ "GBP" }); graph.Nodes.push_back({ "CNY" }); graph.Nodes.push_back({ "EUR" }); graph.Nodes.push_back({ "XXX" }); graph.Nodes.push_back({ "YYY" }); // 8 (Index = 7) // USD CHF YEN GBP CNY EUR XXX YYY graph.Matrix = { { 0.0, 1.0, INF, INF, INF, INF, INF, INF }, // USD { INF, 0.0, 1.0, INF, INF, 4.0, 4.0, INF }, // CHF { INF, INF, 0.0, INF, 1.0, INF, INF, INF }, // YEN { INF, INF, 1.0, 0.0, INF, INF, INF, INF }, // GBP { INF, INF, INF, -3.0, 0.0, INF, INF, INF }, // CNY { INF, INF, INF, INF, INF, 0.0, 5.0, 3.0 }, // EUR { INF, INF, INF, INF, INF, INF, 0.0, 4.0 }, // XXX { INF, INF, INF, INF, INF, INF, INF, 0.0 } }; // YYY


私たちのプログラムは単純に为止し、メッセージを说道します。
 Graph contains negative cycle.


問題を示すことはできましたが、グラフの問題のあるセグメントを躲避する重要性があります。

負のサイクルを避ける

これを実現するには、負のサイクルの有一部电影である頂点を定数値 NEG_INF でマークします。
 bool FindPathsAndNegativeCycles(Graph& graph, int start) { int verticesNumber = graph.Nodes.size(); _shortestPath.resize(verticesNumber, INF); _previousVertex.resize(verticesNumber, -1); _shortestPath[start] = 0; for (int k = 0; k < verticesNumber - 1; k++) for (int from = 0; from < verticesNumber; from++) for (int to = 0; to < verticesNumber; to++) { if (graph.Matrix[from][to] == INF) // Edge not exists { continue; } if (_shortestPath[to] > _shortestPath[from] + graph.Matrix[from][to]) { _shortestPath[to] = _shortestPath[from] + graph.Matrix[from][to]; _previousVertex[to] = from; } } bool negativeCycles = false; for (int k = 0; k < verticesNumber - 1; k++) for (int from = 0; from < verticesNumber; from++) for (int to = 0; to < verticesNumber; to++) { if (graph.Matrix[from][to] == INF) // Edge not exists { continue; } if (_shortestPath[to] > _shortestPath[from] + graph.Matrix[from][to]) { _shortestPath[to] = NEG_INF; _previousVertex[to] = -2; negativeCycles = true; } } return negativeCycles; }


これで、_shortestPath 配列内で NEG_INF が見つかった場合、メッセージを说してそのセグメントをスキップしながら、他の通貨に対する最適なソリューションを对应できるようになります。たとえば、ノード 0 (USD を表す) の場合は次のようになります。
 Graph contains negative cycle. Path from 0 to 0 is : 0(USD) Path from 0 to 1 is : 0(USD) 1(CHF) Path from 0 to 2 is : Infinite number of shortest paths (negative cycle). Path from 0 to 3 is : Infinite number of shortest paths (negative cycle). Path from 0 to 4 is : Infinite number of shortest paths (negative cycle). Path from 0 to 5 is : 0(USD) 1(CHF) 5(EUR) Path from 0 to 6 is : 0(USD) 1(CHF) 6(XXX) Path from 0 to 7 is : 0(USD) 1(CHF) 5(EUR) 7(YYY)


うわー!私たちのコードは、データが「少し汚れていた」という事実にもかかわらず、収益性の高いチェーンを很多其他することができました。テスト データを含む上記のすべてのコード例は、で通用されています。

小さな変動でも問題になる

ここで、学んだことを定着させてみましょう。 3 つの通貨の為替レートのリストがあれば、負のサイクルを簡単に検出できます。
 graph.Nodes.push_back({ "USD" }); // 1 (Index = 0) graph.Nodes.push_back({ "CHF" }); graph.Nodes.push_back({ "YEN" }); // 3 (Index = 2) // LogE(x) table: USD CHF YEN graph.Matrix = { { 0.0, 0.489, -0.402 }, // USD { -0.489, 0.0, -0.891 }, // CHF { 0.402, 0.89, 0.0 } }; // YEN from = 0; FindPathsAndNegativeCycles(graph, from);


結果:
 Graph contains negative cycle. Path from 0 to 0 is: Infinite number of shortest paths (negative cycle). Path from 0 to 1 is: Infinite number of shortest paths (negative cycle). Path from 0 to 2 is: Infinite number of shortest paths (negative cycle).


ただし、為替レートのわずかな変更 (つまり、マトリックスの調整) でさえ、大きな差異が生じる可能会性があります。
 // LogE(x) table: USD CHF YEN graph.Matrix = { { 0.0, 0.490, -0.402 }, // USD { -0.489, 0.0, -0.891 }, // CHF { 0.403, 0.891, 0.0 } }; // YEN from = 0; FindPathsAndNegativeCycles(graph, from);


見てください、収益性の高いチェーンが 1 つ見つかりました。
 Path from 0 to 0 is : 0(USD) Path from 0 to 1 is : 0(USD) 2(YEN) 1(CHF) Path from 0 to 2 is : 0(USD) 2(YEN)


これらの的概念を、複数の通貨を含むさらに大きなグラフに適用できます。
 graph.Nodes.push_back({ "USD" }); // 1 (Index = 0) graph.Nodes.push_back({ "CHF" }); graph.Nodes.push_back({ "YEN" }); graph.Nodes.push_back({ "GBP" }); graph.Nodes.push_back({ "CNY" }); // 5 (Index = 4) // LogE(x) table: USD CHF YEN GBP CNY graph.Matrix = { { 0.0, 0.490, -0.402, 0.7, 0.413 }, // USD { -0.489, 0.0, -0.891, 0.89, 0.360 }, // CHF { 0.403, 0.891, 0.0, 0.91, 0.581 }, // YEN { 0.340, 0.405, 0.607, 0.0, 0.72 }, // GBP { 0.403, 0.350, 0.571, 0.71, 0.0 } }; // CNY from = 0; runDetectNegativeCycles(graph, from);


その結果、盈利を得る候補が複数見つかる将性があります。
 Path from 0 to 0 is : 0(USD) Path from 0 to 1 is : 0(USD) 2(YEN) 1(CHF) Path from 0 to 2 is : 0(USD) 2(YEN) Path from 0 to 3 is : 0(USD) 2(YEN) 3(GBP) Path from 0 to 4 is : 0(USD) 2(YEN) 4(CNY) 


スクルージ・マクダック


ただし、重要的な元素が 2 つあります。
  1. 主に通貨価格の光速な変動により、栽定取引プロセスを実行する際には時間が必要な要点となります。その結果、確実な賭けの期限は比较に短くなります。
  2. プラットフォームは取引ごとに手数料を徴収します。


したがって、時間コストを最小限に抑え手数料を削減することが最も重要であり、これは確実なベットの長さを制限することによって実現されます。


経験的には、許容可能会な確実なベットの長さは一般性 2 ~ 3 ペアの範囲であることが示唆されています。これを超えると、計算要件がエスカレートし、取引プラットフォームにより高額な手数料が課せられます。


したがって、収入を得るには、そのようなテクノロジーを全部的するだけでは相当ではなく、低レベルの手数料を采取することも有需要です。平常、そのようなリソースを持っているのは一手财富管理機関だけです。


スマートコントラクトを使用した自動化

FX 实操方式方法のロジックとそこから获利を引き出す方式方法については詳しく説明しましたが、これらの实操方式方法を実行するために使用的されるテクノロジーについては触れていません。この話題は少し本筋からそれますが、スマート コントラクトについては触れずにはいられませんでした。


スマート コントラクトの使用は、接下来の為替使用を行うための最も革故鼎新的な措施の 1 つです。スマート コントラクトにより、遅延や人間の插足なしでリアルタイムの FX 使用が已经になります (スマート コントラクトの制作を除く)。


Solidity は、暗语通貨を含む经融業務を自動化するスマート コントラクトを做成するための特色なプログラミング言語です。スマート コントラクトの全世界は動的であり、级速な技術変化や進化する規制の影響を受けます。これは、かなりの誇大広告と、ウォレットや移民法順守に関連した很大なリスクを伴う分野です。


この分野から权利を得ている就能ある個人やチームがいることは間違いありませんが、市場ルールが確実に守られるように努めている規制派出所もいます。

なぜこれを調査するのでしょうか?

这个环境経済の複雑さ、曖昧さ、予測不几率性にもかかわらず、外国人為替は科技金融界の隠れた原動力であり続けています。これは、这个环境中の何千もの企業と何千万もの個人が、国境を越えて愉悦的に協力し、サービスを保证し、相互间に好处をもたらすことを几率にする最重要な要点です。


もちろん、政治课、規制、中共中央銀行などのさまざまな要因が為替レートや為替効率に影響を与えます。こうした複雑さにより、互联网金融情勢は複雑化しています。しかし、これらの複雑さは公益的のためのより大きな目标に役立つと信じることが不能够欠です。


地球経済における為替レートの会有と決定については、数多くの专业論文が詳しく調査されており、その例としては次のようなものがあります。

これらの論文は、看待することがまだ難しく、1 つのモデルに当てはめることが難しい对外直接投资為替のいくつかの一般的なメカニズムに光を当てています。


ただし、コードをいじって実際の問題の解決策を見つけようとすると、それについてもう少し手がかりを得ることができました。皆さんも私と同じようにこの小さな探検穷游を楽しんでいただければ幸いです。乞うご想法!

リンク

  • Sedgewick R. - C のアルゴリズム、パート 5: グラフ アルゴリズム


でも公開されています。


바카라사이트 바카라사이트 온라인바카라