乙Py先生のプログラミング教室

乙Py先生のプログラミング教室
初学者のためのプログラミング学習サイト

今回はハノイの塔を解くプログラムについてです。


解説動画はこちら



ハノイの塔(Tower of Hanoi)とは

古来からあるパズルゲーム



以下のルールに従って
すべての円盤を右端の杭に移動させられれば完成です。

・3本の杭と、大きさの異なる複数の円盤から構成
・初期配置は左端の杭に昇順で積み重ねられている
・1回に1枚ずつ移動させるられるが
 小さいものの上に大きな円盤を乗せることはできない


ハノイの塔は
「再帰的な方法」で解くことができる古典的な問題です。

ディスクの数 n、出発点の棒 source
目的地の棒 target、および補助の棒 auxiliaryとします。

基底条件として、ディスクが1枚だけの場合は
sourceからtargetに直接移動
それ以外の場合は、再帰的に以下のステップを実行:

n-1枚のディスクをsourceからauxiliaryに移動
残りの1枚のディスクをsourceからtargetに移動
n-1枚のディスクをauxiliaryからtargetに移動

配置するディスクの数 n を変更することで
他のケースも同様に解くことができます。



ハノイの塔を解くコード

# ハノイの塔を攻略
def hanoi(n, source, target, auxiliary):
    if n == 1:
        print(f"{n}を{source}から{target}に移動")
        return
    hanoi(n-1, source, auxiliary, target)
    print(f"{n}を{source}から{target}に移動")
    hanoi(n-1, auxiliary, target, source)

n = 3
hanoi(n, 'A', 'C', 'B')
1をAからCに移動
2をAからBに移動
1をCからBに移動
3をAからCに移動
1をBからAに移動
2をBからCに移動
1をAからCに移動



ハノイの塔を描画するコード

widgetsを使ってstepごとの画像を描画しています。
# ハノイの塔を描画する
import ipywidgets as widgets
import matplotlib.pyplot as plt
import numpy as np

data,img_data = [],[]

# ディスクの色リスト
colors = ['red', 'green', 'blue', 'purple', 'yellow', 'silver', 'orange', 'pink']

# 棒の位置
positions = {'A': 0, 'B': 1, 'C': 2}

# ハノイの塔を攻略
def hanoi(n, source, target, auxiliary):
    if n == 1:
        data.append([n,source,target])
        return
    hanoi(n-1, source, auxiliary, target)
    data.append([n,source,target])
    hanoi(n-1, auxiliary, target, source)

# ディスクを描画するための関数
def draw_towers(state, step):
    fig, ax = plt.subplots()
    ax.set_xlim(-1, 3)
    ax.set_ylim(0, 5)
    ax.set_xticks([0, 1, 2])
    ax.set_xticklabels(['A', 'B', 'C'])
    ax.set_yticks([])
    for tower in 'ABC':
        for j, disk in enumerate(state[tower]):
            color = colors[disk - 1]
            width = 0.2 * disk
            height = 0.5
            ax.bar(positions[tower], height, bottom=j * height, width=width, color=color, edgecolor='black')
    ax.set_title(f'Step {step}')
    fig.canvas.draw()
    img_data.append(np.array(fig.canvas.renderer.buffer_rgba()))
    plt.close(fig)

# 初期状態
n = 4  # ディスクの数
hanoi(n, 'A', 'C', 'B')
towers = {'A': list(range(n, 0, -1)), 'B': [], 'C': []}
draw_towers(towers, 0)

# 各ステップごとに状態を描画
for step, (disk, source, target) in enumerate(data):
    towers[target].append(towers[source].pop())
    draw_towers(towers, step+1)

# img_data に保存された画像を表示するためのウィジェット
def show_image(step):
    plt.imshow(img_data[step])
    plt.axis('off')
    plt.show()

slider = widgets.IntSlider(value=0, min=0, max=len(img_data)-1, step=1, description='Step:')
widgets.interactive(show_image, step=slider)
スクリーンショット 2024-08-31 12.59.50


終わりに

インドの巨大な寺院の話が元ネタのようです。

天地創造の時に神が64枚の純金の円盤を重ねて置いた
司祭たちはそこで、昼夜を通して円盤を別の柱に移し替えている
全ての円盤の移し替えが終わったときに
世界は崩壊し終焉を迎える・・・



ハノイの塔のstep数は 2のディスクの枚数乗 -1 なので
2 ** 64 -1 stepかかります。

すっごい回数
おじさん達が移し替えてると思うと
大変だなーと思う今日この頃です。

それでは。


今回はブロックチェーンのプログラムを
みていきたいと思います。

解説動画はこちら


 
今回は「ブロックチェーン」についてです。


ブロックチェーンとは

分散型台帳技術の一種であり、データを安全かつ
改ざん不可能な形で記録するための仕組みのことです。

連続するブロックで鎖のように構成されています
(参考:NTTデータ)

fig_02_01


最初のブロックは、ジェネシスブロックと呼ばれています。

各ブロックには次のような情報が格納されています。
・トランザクションデータ:取引の詳細情報
・タイムスタンプ:取引が行われた日時
・前のブロックのハッシュ:直前のブロックの暗号学的ハッシュ値
・現在のブロックのハッシュ:現在のブロックのデータから生成されたハッシュ値


ハッシュとは

取引情報やタイムスタンプなどから計算される数値のことです。
ハッシュ計算にはsha256(Secure Hash Algorithm 256-bit)などが使われています。

このハッシュ値を前のブロックの物と比較して
整合性をチェックしているので、改竄が難しくなっています。

ブロック1:データ + ハッシュ0(ジェネシスブロックのハッシュ)
ブロック2:データ + ハッシュ1
ブロック3:データ + ハッシュ2

チェーンの途中のブロックのデータを改竄すると
それ以降全てのハッシュ値を改竄しないといけないため
ものすごい難易度になります。


ブロックチェーンの簡単なプログラム

ブロックチェーンを作ってブロックを繋げるだけの
簡単なサンプルです。
import hashlib
import time

# ブロックを定義するクラス
class Block:
    def __init__(self, index, previous_hash, timestamp, data):
        self.index = index
        self.previous_hash = previous_hash
        self.timestamp = timestamp
        self.data = data
        self.hash = self.calculate_hash()

    def calculate_hash(self):
        sha = hashlib.sha256()
        sha.update((str(self.index) + str(self.previous_hash) + str(self.timestamp) + str(self.data)).encode('utf-8'))
        return sha.hexdigest()

# ブロックチェーンを定義するクラス
class Blockchain:
    def __init__(self):
        self.chain = [self.create_genesis_block()]

    def create_genesis_block(self):
        return Block(0, "0", int(time.time()), "Genesis Block")

    def get_latest_block(self):
        return self.chain[-1]

    def add_block(self, new_block):
        new_block.previous_hash = self.get_latest_block().hash
        new_block.hash = new_block.calculate_hash()
        self.chain.append(new_block)

# 最初のブロックチェーンの初期化
my_blockchain = Blockchain()

# 最初のブロックチェーンに新しいブロックを追加
my_blockchain.add_block(Block(1, "", int(time.time()), "Block 1 Data"))
my_blockchain.add_block(Block(2, "", int(time.time()), "Block 2 Data"))

# ブロックチェーンの内容を表示
for block in my_blockchain.chain:
    print(f"Index: {block.index}")
    print(f"Previous Hash: {block.previous_hash}")
    print(f"Timestamp: {block.timestamp}")
    print(f"Data: {block.data}")
    print(f"Hash: {block.hash}")
    print("-" * 30)
Index: 0
Previous Hash: 0
Timestamp: 1724488836
Data: Genesis Block
Hash: 042bf4db73e03d3047fe19aee7a9d5e4d95a446f8420f56704d2084f231f1350
------------------------------
Index: 1
Previous Hash: 042bf4db73e03d3047fe19aee7a9d5e4d95a446f8420f56704d2084f231f1350
Timestamp: 1724488836
Data: Block 1 Data
Hash: 3cff362cc92bcf78e7d78f0f01435ab18e17d0f543b57634953ee163b6d344f3
------------------------------
Index: 2
Previous Hash: 3cff362cc92bcf78e7d78f0f01435ab18e17d0f543b57634953ee163b6d344f3
Timestamp: 1724488836
Data: Block 2 Data
Hash: bbd7cdf536222d1b757061d459dd7c240dfef1eb311de3b1f5e1ca9f6dfae37e


ブロックの作成時にはHash値が計算され
ブロックには前のブロックのHash値も格納されています。


次は実際の取引っぽいデータでブロックを作り
ブロックチェーンの有効性も検証してみます。
import hashlib
import time
from typing import List

class Block:
    def __init__(self, index: int, previous_hash: str, timestamp: int, transactions: List[dict]):
        self.index = index
        self.previous_hash = previous_hash
        self.timestamp = timestamp
        self.transactions = transactions
        self.hash = self.calculate_hash()

    def calculate_hash(self):
        block_string = f"{self.index}{self.previous_hash}{self.timestamp}{self.transactions}"
        return hashlib.sha256(block_string.encode()).hexdigest()

class Blockchain:
    def __init__(self):
        self.chain = [self.create_genesis_block()]

    def create_genesis_block(self):
        return Block(0, "0", int(time.time()), [{"sender": "genesis", "receiver": "network", "amount": 0}])

    def get_latest_block(self):
        return self.chain[-1]

    def add_block(self, transactions: List[dict]):
        latest_block = self.get_latest_block()
        new_block = Block(latest_block.index + 1, latest_block.hash, int(time.time()), transactions)
        self.chain.append(new_block)

    def is_chain_valid(self):
        for i in range(1, len(self.chain)):
            current_block = self.chain[i]
            previous_block = self.chain[i-1]
            print(f"current_block.hash : {current_block.hash} , current_block.calculate_hash : {current_block.calculate_hash()}")
            print(f"current_block.previous_hash : {current_block.previous_hash} , previous_block.hash : {previous_block.hash}")
            if current_block.hash != current_block.calculate_hash():
                return False
            if current_block.previous_hash != previous_block.hash:
                return False
        return True

# ブロックチェーンの初期化
my_blockchain = Blockchain()

# 取引の例
transactions1 = [
    {"sender": "Alice", "receiver": "Bob", "amount": 50},
    {"sender": "Bob", "receiver": "Charlie", "amount": 30}
]
my_blockchain.add_block(transactions1)

transactions2 = [
    {"sender": "Alice", "receiver": "Charlie", "amount": 20},
    {"sender": "Charlie", "receiver": "Bob", "amount": 10}
]
my_blockchain.add_block(transactions2)

# ブロックチェーンの内容を表示
for block in my_blockchain.chain:
    print(f"Index: {block.index}")
    print(f"Previous Hash: {block.previous_hash}")
    print(f"Timestamp: {block.timestamp}")
    print(f"Transactions: {block.transactions}")
    print(f"Hash: {block.hash}")
    print("-" * 30)

# ブロックチェーンの有効性を検証
print("Is blockchain valid?", my_blockchain.is_chain_valid())
Index: 0 Previous Hash: 0 Timestamp: 1724483719 Transactions: [{'sender': 'genesis', 'receiver': 'network', 'amount': 0}] Hash: ec68f33868e558eca7f3fd6260101430eb12e7e8eb9b328a18882ef6f3ed229c ------------------------------ Index: 1 Previous Hash: ec68f33868e558eca7f3fd6260101430eb12e7e8eb9b328a18882ef6f3ed229c Timestamp: 1724483719 Transactions: [{'sender': 'Alice', 'receiver': 'Bob', 'amount': 50}, {'sender': 'Bob', 'receiver': 'Charlie', 'amount': 30}] Hash: 3a39a53e1423ece90ce7418689a3593959f3041e5b5c5ae7ddf3f95ec9264d25 ------------------------------ Index: 2 Previous Hash: 3a39a53e1423ece90ce7418689a3593959f3041e5b5c5ae7ddf3f95ec9264d25 Timestamp: 1724483719 Transactions: [{'sender': 'Alice', 'receiver': 'Charlie', 'amount': 20}, {'sender': 'Charlie', 'receiver': 'Bob', 'amount': 10}] Hash: b4e7a335791d6b846fba35dece29095f8c2792cde02b6a012403ab6322cd1132 ------------------------------ current_block.hash : 3a39a53e1423ece90ce7418689a3593959f3041e5b5c5ae7ddf3f95ec9264d25 , current_block.calculate_hash : 3a39a53e1423ece90ce7418689a3593959f3041e5b5c5ae7ddf3f95ec9264d25 current_block.previous_hash : ec68f33868e558eca7f3fd6260101430eb12e7e8eb9b328a18882ef6f3ed229c , previous_block.hash : ec68f33868e558eca7f3fd6260101430eb12e7e8eb9b328a18882ef6f3ed229c current_block.hash : b4e7a335791d6b846fba35dece29095f8c2792cde02b6a012403ab6322cd1132 , current_block.calculate_hash : b4e7a335791d6b846fba35dece29095f8c2792cde02b6a012403ab6322cd1132 current_block.previous_hash : 3a39a53e1423ece90ce7418689a3593959f3041e5b5c5ae7ddf3f95ec9264d25 , previous_block.hash : 3a39a53e1423ece90ce7418689a3593959f3041e5b5c5ae7ddf3f95ec9264d25 Is blockchain valid? True

現在のブロックのハッシュと計算されたハッシュ値
現在のブロックにある前のハッシュ値と前のブロックのハッシュ値
で整合性を見ています。





暗号通貨のマイニングの仕組み

マイニング(採掘)とは、分散型ネットワークにて新しい取引を検証し
それをブロックチェーンに追加するプロセスのことです。

特にビットコインなどのProof of Work(PoW)ベースの
暗号通貨において重要な役割を果たしています。

コンセンサスアルゴリズムと呼ばれる
新しいブロックの追加に合意するための仕組みが備わっており
ビットコインなどではProof of Work (PoW)と呼ばれるものが使われています。

Proof of Work (PoW):
計算能力を競い合い、最初に問題を解いたノードが
ブロックを追加する権利を得るというものです。
計算には莫大なリソースが必要になります。


マイニングの基本的な仕組み

ここではすごい荒く説明です。

1.取引の収集と検証:
各取引が有効であることを確認するために
デジタル署名や残高の確認などの検証が行われる

2.ブロックの形成:
検証された取引は、一定数集まると1つのブロックにまとめられる
ブロックには、取引情報の他に、前のブロックのハッシュ値、タイムスタンプ
ナンス(nonce)という特別な値が含まれる。

3.ハッシュ計算とナンスの探索:
マイナーは、ブロックヘッダー(ブロックの要約情報)に
対してハッシュ関数を適用し、ハッシュ値を計算する

目的は、そのハッシュ値が特定の条件(例えば、ハッシュ値が一定の数の先頭ゼロを持つこと)
を満たすようにすること

ナンスはこの条件を満たすために調整される値で
マイナーはナンスを変化させながら何度もハッシュ計算を繰り返します。

4.ブロックの追加:
条件を満たすハッシュ値が見つかると、そのブロックが承認され
ブロックチェーンに追加される

このプロセスを「プルーフ・オブ・ワーク」と呼び
計算量の多さがブロックの正当性を保証する

5.報酬の獲得:
ブロックを成功裏に追加したマイナーには
暗号通貨の新規発行分(ブロック報酬)や取引手数料などの報酬が与えられる

ビットコインの場合、最初のブロック(ジェネシスブロック)以降
約4年ごとにブロック報酬は半減する仕組み(半減期)が導入されている



マイニングのコード

ハッシュ計算とナンスの探索部分を難易度を下げて
実験してみます。


ハッシュを計算する際に
先頭に0が難易度の数値分並ぶかどうかを目的にしています。
0が並ぶまで計算を続け、ヒットしたらハッシュを追加します。

難易度の数値を上げると時間が掛かるようになります。


import hashlib
import time

class Block:
    def __init__(self, index, previous_hash, timestamp, data, nonce=0):
        self.index = index
        self.previous_hash = previous_hash
        self.timestamp = timestamp
        self.data = data
        self.nonce = nonce
        self.hash = self.calculate_hash()

    def calculate_hash(self):
        value = (str(self.index) + str(self.previous_hash) + str(self.timestamp) +
                 str(self.data) + str(self.nonce)).encode()
        return hashlib.sha256(value).hexdigest()

    def mine_block(self, difficulty):
        target = '0' * difficulty
        while self.hash[:difficulty] != target:
            self.nonce += 1
            self.hash = self.calculate_hash()
        print(f"Block mined: {self.hash}")

class Blockchain:
    def __init__(self):
        self.chain = [self.create_genesis_block()]
        self.difficulty = 5  # 難易度

    def create_genesis_block(self):
        return Block(0, "0", time.time(), "Genesis Block")

    def get_latest_block(self):
        return self.chain[-1]

    def add_block(self, new_block):
        new_block.previous_hash = self.get_latest_block().hash
        new_block.mine_block(self.difficulty)
        self.chain.append(new_block)

# ブロックチェーンの初期化
blockchain = Blockchain()

# 新しいブロックを追加
print("Mining block 1...")
new_block = Block(1, "", time.time(), "Block 1 Data")
blockchain.add_block(new_block)

print("Mining block 2...")
new_block = Block(2, "", time.time(), "Block 2 Data")
blockchain.add_block(new_block)

print("Mining block 3...")
new_block = Block(3, "", time.time(), "Block 3 Data")
blockchain.add_block(new_block)
Mining block 1...
Block mined: 00000b1dcfcdb93abbe9d2656e7c36851aae271bfcd45aecb3ec1a0f43474228
Mining block 2...
Block mined: 0000083671360216d051d4ca6a2fef3fc783e311ec3540a484a24a86c85471cb
Mining block 3...
Block mined: 000001d4692c5f50c4e90b74ce7a0bf87839de5893667b424846b8df1c7e5db4


なんとなくではありますが
マイニングの大変さが分かるかと思います。



まとめ

ブロックチェーン技術は暗号通貨の取引や
透明性、セキュリティ、分散化の特性により
今後さらに多くの分野での活用が期待されています。

正当性の証明や、改竄防止関連の仕事が
増えてくると予想しているので
覚えておくと使う機会が来るかもしれません。

それでは





今回は1億年くらいかかりそうな問題を
高速に解く事ができるライブラリ
graphillion についてです。

解説動画はこちら




graphillion


膨大な数のグラフを簡単に操作するためのライブラリです。

グラフとはノードの集合とエッジの集合で構成された
抽象物のことで次のような頂点と線で構成されたものの事です。

2x2

この頂点をノード
線のことをエッジと呼んでいます。

これを応用した問題についてですが
1をスタート
81をゴールとした際に
二回同じところは通らないとして
行くことの出来るルートは何通りあるでしょうか

9x9

これを手計算で特にはかなりしんどいですよね。
このような膨大な数になるグラフを高速に解く事ができるのが
graphillionです。


元ネタ

一応こちらの元ネタは
下記のYouTube動画です。


この動画内では
先ほどの問題は4時間くらいかかっていたそうです。

それ以上のサイズになると計算量が膨大になるので
1億年以上掛かっても解ききれない
ということになっています。

組み合わせ数が爆発した際の
恐ろしさを感じさせる動画ですね。



graphillionのインストール

google colabでは下記コマンド実行のみです。
!pip install graphillion


graphillionの使い方


まずは必要なライブラリをインポートします。
グラフの描画に必要なものも合わせてインポートします。
# 必要なモジュールのインポート
from graphillion import GraphSet
import graphillion.tutorial as tl
import matplotlib.pyplot as plt
import networkx as nx
import random


グリッド表示をさせてみる
# N x M のサイズのグリッド表示
def draw_with_matplotlib(edges, n, m, figsize=(4, 3)):
    G = nx.Graph()
    G.add_edges_from(edges)
    pos,node_labels = {},{}
    for i in range(n):
        for j in range(m):
            node = i * m + j + 1
            pos[node] = (j, -i)
            node_labels[node] = str(node)

    plt.figure(figsize=figsize)
    nx.draw(G, pos, labels=node_labels, with_labels=True, node_size=200, node_color='lightblue', edge_color='gray')
    plt.show()

# サイズを指定
n, m = 2,2
universe = tl.grid(n, m)
GraphSet.set_universe(universe)
draw_with_matplotlib(universe, n + 1, m + 1)
2x2
glidメソッドで辺の数を指定します(頂点の数-1)

単純にパス(道順の総数)を計算する場合は
スタートの頂点の数値と
ゴールの頂点の数値を指定して
GraphSet.paths(start, goal)
で計算できます。
universe = tl.grid(2, 2)
GraphSet.set_universe(universe)
start, goal = 1, 9
paths = GraphSet.paths(start, goal)
print(f"パスの数 : {len(paths)}")
12


頂点が3x3の場合は
12通りである事がわかります。

この12通りを全通り描画してみましょう。
from graphillion import GraphSet
import graphillion.tutorial as tl
import matplotlib.pyplot as plt
import networkx as nx

def make_grid(n):
    edges = []
    for i in range(n):
        for j in range(n):
            if i < n - 1:
                edges.append((i * n + j + 1, (i + 1) * n + j + 1))
            if j < n - 1:
                edges.append((i * n + j + 1, i * n + (j + 1) + 1))
    return edges

def grid_paths(n):
    universe = make_grid(n)
    GraphSet.set_universe(universe)
    start = 1
    goal = n * n
    paths = GraphSet.paths(start, goal)
    return paths

def draw_paths_in_grid(paths, n):
    num_paths = len(paths)
    rows = 3
    cols = 4
    fig, axes = plt.subplots(rows, cols, figsize=(10, 6))
    G = nx.grid_2d_graph(n, n)
    pos = {(i, j): (j, n - 1 - i) for i in range(n) for j in range(n)}

    for i, path in enumerate(paths):
        row = i // cols
        col = i % cols
        ax = axes[row, col]
        path_edges = [(divmod(u - 1, n), divmod(v - 1, n)) for u, v in path]

        nx.draw(G, pos, with_labels=False, node_size=300, node_color='lightblue', edge_color='lightgray', ax=ax)
        nx.draw_networkx_edges(G, pos, edgelist=path_edges, edge_color='red', width=2, ax=ax)
        ax.set_title(f'Path {i + 1}')
        ax.set_xticks([])
        ax.set_yticks([])

    for j in range(i + 1, rows * cols):
        fig.delaxes(axes.flatten()[j])

    plt.tight_layout()
    plt.show()

n = 3
paths = grid_paths(n)
draw_paths_in_grid(paths, n)
2x2_ans1




頂点4を通ってから頂点2を通る道順を出す
というようなこともできます。
# 何通りあるか
start = 1
goal = 9
key = 4
treasure = 2

paths = GraphSet.paths(start, goal)
paths_to_key = GraphSet.paths(start, key).excluding(treasure)
treasure_paths = paths.including(paths_to_key).including(treasure)
len(treasure_paths)
2

# 道順リストの描画
from graphillion import GraphSet
import graphillion.tutorial as tl
import matplotlib.pyplot as plt
import networkx as nx

def draw_paths_in_grid(paths, n):
    num_paths = len(paths)
    rows = 1
    cols = 2
    fig, axes = plt.subplots(rows, cols, figsize=(10, 6))
    G = nx.grid_2d_graph(n, n)
    pos = {(i, j): (j, n - 1 - i) for i in range(n) for j in range(n)}

    for i, path in enumerate(paths):
        row, col = divmod(i, cols)
        ax = axes[col] if rows == 1 else axes[row, col]
        path_edges = [(divmod(u - 1, n), divmod(v - 1, n)) for u, v in path]

        nx.draw(G, pos, with_labels=False, node_size=300, node_color='lightblue', edge_color='lightgray', ax=ax)
        nx.draw_networkx_edges(G, pos, edgelist=path_edges, edge_color='red', width=2, ax=ax)
        ax.set_title(f'Path {i + 1}')
        ax.set_xticks([])
        ax.set_yticks([])

    for j in range(i + 1, rows * cols):
        fig.delaxes(axes.flatten()[j])

    plt.tight_layout()
    plt.show()


n, m = 2, 2
universe = tl.grid(n, m)
GraphSet.set_universe(universe)

start = 1
goal = 9
key = 4
treasure = 2

paths = GraphSet.paths(start, goal)
paths_to_key = GraphSet.paths(start, key).excluding(treasure)
treasure_paths = paths.including(paths_to_key).including(treasure)
draw_paths_in_grid(treasure_paths,n+1)
2x2_ans2



それでは冒頭の
9x9のサイズのグリッドだと
道順は何通りあるのでしょうか?

import time
universe = tl.grid(8, 8)
GraphSet.set_universe(universe)
start, goal = 1, 81
s_time = time.time()
paths = GraphSet.paths(start, goal)
times = time.time() - start
print(f"計算時間 : {times}")
print(f"パスのサイズ : {len(paths):,}")
計算時間 : 1723880338.8543057
パスのサイズ : 3,266,598,486,981,642

約3200兆通りです

1つ選んでみるとこんな結果でした。

9x9_ans

まとめ

膨大な数のグラフ計算を行う際には
graphillion ライブラリが有用です。

以下のような問題を高速に解く事ができます。
最短経路問題
全経路列挙問題
ネットワーク解析
配置問題
etc...

元ネタの
動画では恐ろしく掛かっていた時間も
圧倒的に短縮して解けているので
すごく有用なライブラリです。

グラフの計算を仕事で使う方は
ぜひ使ってみてください。

それでは。

このページのトップヘ