アルゴリズムとデータ構
造
15.「アルゴリズムの
設計」2011 年 4 月 27 日(水)
服部 健太
2011/4/27 アルゴリズムとデータ構造 15 2
N クイーン問題
 N×N のチェス盤に, N 個のクイーンの駒を置いて,それぞれが
他のクイーンの利き筋(縦,横,斜め)に当たらないようにす
る
 N =8の例:正しい配置 間違った配置
2011/4/27 アルゴリズムとデータ構造 15 3
N クイーン問題の力まかせ法による
解
 サイズ n の N クイーン問題を解く
def n_queen(n):
A = array(n)
for i in range(n): A[i] = 0
while A != None:
if valid(A, n): return A
A = next(A, n)
return None
 次候補となる盤面を返す
def next(A, n):
for i in range(n):
if A[i] == n-1:
A[i] = 0
else:
A[i] = A[i] + 1
return A
return None
 盤面が有効かどうかチェック
def valid(A, n):
for i in range(n-1):
k = 1
for j in range(i+1, n):
if A[i] == A[j]:
return False
if (A[i] == A[j] + k
or A[j] == A[i] + k):
return False
k = k + 1
return True
2011/4/27 アルゴリズムとデータ構造 15 4
バックトラック法
 力まかせをより効率的に行
う
 問題の解が n 個の部分から
構成されるとする.
 これらの値を一つずつ決め
ていく
 いくつかの部分の値を決め
た時点で,これ以降どう決
めても解が得られないとわ
かったら,その決め方に関
する計算はそこで打ち切る
 以上の手順を深さ優先探索
の要領で,すべての解の可
能性を調べ終わるまで続け
る
1 2 3 n・・・
・・・
1 2 3 4 n 21 3 4 n
× × × × ×
× × × ×
1 2 3 4 5 n
・・・
1 列目のク
イーンの位置
2 列目のク
イーンの位置
3 列目のク
イーンの位置
・・・
2011/4/27 アルゴリズムとデータ構造 15 5
N クイーン問題のバックトラック
解法
def n_queen(n):
B = board(n); backtrack(B, n, 0)
def board(n):
b = array(n);
for i in range(n):
b[i] = array(n);
for j in range(n):
b[i][j] = False
return b
def backtrack(B, n, level):
if level >= n: print(B); return
row = level
for col in range(n):
if check(B, row, col):
B[row][col] = True
backtrack(B, n, level + 1)
B[row][col] = False
2011/4/27 アルゴリズムとデータ構造 15 6
N クイーン問題のバックトラック解
法 (2)
def check(B, row, col):
for i in range(len(B)):
if B[row][i] or B[i][col]:
return False
if row+i < n and col+i < n and B[row+i][col+i]:
return False
if row-i >= 0 and col+i < n and B[row-i][col+i]:
return False
if row+i < n and col-i >= 0 and B[row+i][col-i]:
return False
if row-i >= 0 and col-i >= 0 and B[row-i][col-i]:
return False
return True
2011/4/27 アルゴリズムとデータ構造 15 7
置けるかどうかの判定を改良する
 horizontal
 0 ~ n-1
 それぞれの段に既に置
かれているかチェック
 major
 0 ~ (row+col) ~ n+n-2
 対角線方向に置かれて
いるかチェック
 minor
 1-n ~ (row-col) ~ n-1
 major とは逆の対角線
方向に置かれているか
チェック
 練習問題
 上記の配列を利用した
改良版のコードを書け
horizontal
major
minor
[0]
[1]
[2]
[n+n-2]
[n-1]
[0] [1][-1]
[0]
[1]
[n-1]
[n-1][1-n]
3 1A
[0] [1] [2]
2011/4/27 アルゴリズムとデータ構造 15 8
解答例
def n_queen(n):
b = board(n); backtrack(b, n, 0)
def board(n):
b = record([‘rows’, ‘horizontal’,
‘major’, ‘minor’])
b.rows = array(n)
b.horizontal = array(n)
for i in range(n): b.horizontal[i] = True
b.major = array(2 * n – 1)
b.minor = array(2 * n – 1)
for i in range(2 * n – 1):
b.major[i] = b.minor[i] = True
return b
2011/4/27 アルゴリズムとデータ構造 15 9
解答例(続き)
def backtrack(B, n, level):
if level >= n: print(B.rows); return
row = level
for col in range(n):
B.rows[row] = col
if (B.horizontal[col] and B.major[row+col]
and B.minor[row-col]):
B.horizontal[col] = False
B.major[row+col] = False
B.minor[row-col] = False
backtrack(B, n, level + 1)
B.rows[col] = None
B.horizontal[col] = True
B.major[row+col] = True
B.minor[row-col] = True
2011/4/27 アルゴリズムとデータ構造 15 10
分割統治法
 問題をいくつかの部分問題に分割し,それぞ
れを独立に解く.
 その結果を組み合わせて全体の問題の解とす
る
 部分問題は元の問題と同じもので,ただデータの
少ないなどの点で簡単になっているだけが違い
 再帰呼び出しを用いて実装することができる
 例:
 クイックソート,マージソート
2011/4/27 アルゴリズムとデータ構造 15 11
動的計画法( DP )
 より小さい部分問題の解を集めて,大きい問
題の解を構成する
 分割統治法と似ているが,各部分問題はちょうど
一度だけ解くようにする
 表 or 再帰呼び出し+メモ化で実装できる
 例: Floyd 法
2011/4/27 アルゴリズムとデータ構造 15 12
動的計画法の例
 フィボナッチ数列:
 再帰的定義にもとづくプログラム
def fib(n):
if n == 0 or n == 1:
return 1
else:
return fib(n-1) + fib(n-2)
 動的計画法を利用したプログラム
def fib(n):
n1 = n2 = result = 1
for i in range(1, n):
result = n1 + n2
n1 = n2
n2 = result
return result
FIB(4)
FIB(3) FIB(2)
FIB(2) FIB(1)
FIB(1) FIB(0)
FIB(1) FIB(0)
同じ計算を何
度も繰り返す
ため,非効率
的
2011/4/27 アルゴリズムとデータ構造 15 13
Levenshtein Distance (編集距離)
 2つの文字列がどれだけ異なっているか
 2つの文字列を一致させるために必要な文字の挿
入,削除,置換操作の最小回数
 ”例: kitten” ”と sitting”
 kitten ⇒ sitten sitt⇒ in sittin⇒ g
 LD(“kitten”, ”sitting”) = 3
 練習問題:次の文字列の編集距離はいくら
か?
 apple と couple
2011/4/27 アルゴリズムとデータ構造 15 14
DP を用いて編集距離を求める
 アルゴリズムのポイ
ント
 長さ 0 の文字列と長さ
n の文字列の LD は n
 LD(s1,s2) は,以下のう
ちの最小のもの
 置換: LD(s1-1,s2-1) + (if
s1.last = s2.last then 0
else 1)
 挿入: LD(s1-1,s2) + 1
 削除: LD(s1,s2-1) + 1
※ 文字列 s-1 は文字列 s から末尾の文字
を
  削除したものとする
k i t t e n
0 1 2 3 4 5 6
s 1 1 2 3 4 5 6
i 2 2 1 2 3 4 5
t 3 3 2 1 2 3 4
t 4 4 3 2 1 2 3
i 5 5 4 3 2 2 3
n 6 6 5 4 3 3 2
g 7 7 6 5 4 4 3
2011/4/27 アルゴリズムとデータ構造 15 15
LD を求めるコード例
def levenshtein_distance(s1, s2):
m = len(s1)
n = len(s2)
D = array2(m+1, n+1)
for i in range(m+1): D[i][0] = i
for j in range(n+1): D[0][j] = j
for i in range(1, m+1):
for j in range(1, n + 1)::
if s1[i-1] == s2[j-1]: cost = 0
else: cost = 1
D[i][j] = min(D[i-1][j]+1,
D[i][j-1]+1,
D[i-1][j-1]+cost)
return D[m][n]
2011/4/27 アルゴリズムとデータ構造 15 16
(整数)ナップサック問題
 大きさ W のナップサックに N 種類の品物を詰め込
むことを考える
 詰め込んだ品物の合計金額を最大にしたい
 各品物は何個取っても良い
 例:
 ナップサックの大きさ:21
 品物の種類:
 (a) 大きさ3,金額5
 (b) 大きさ5,金額7
 (c) 大きさ11,金額21
 練習問題
 上記例の状況で,ナップサックに詰め込んだ品物の最大の
金額と,それぞれの品物の数はどれくらいになるか?
2011/4/27 アルゴリズムとデータ構造 15 17
動的計画法を用いた解法
 入力:問題 P( ナップサックの大きさ w, 品物リスト items)
 items[i].size は品物 i の大きさ, items[i].value は金額を表す
def knapsack(P):
cost = array(P.w)
best = array(P.w)
for i in range(P.w):
cost[i] = best[i] = 0
for i in range(len(P.items)):
for j in range(P.w):
if j >= P.items[i].size:
if cost[j] < cost[j-P.items[i].size]+P.items[i].value:
cost[j] = cost[j-P.items[i].size]+P.items[i].value
best[j] = i
return cost[P.w-1]
2011/4/27 アルゴリズムとデータ構造 15 18
実行の様子
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
5 5 5 10 10 10 15 15 15 20 20 20 25 25 25 30 30 30 35
a a a a a a a a a a a a a a a a a a a
5 5 7 10 10 12 15 15 17 20 20 22 25 25 27 30 30 32 35
a a b a a b a a b a a b a a b a a b a
5 5 7 10 10 12 15 15 21 21 21 26 26 28 31 31 33 36 36
a a b a a b a a c c c c c c c c c c c
j
i
cost1[]
best1[]
cost2[]
best2[]
cost3[]
best3[]
品物 a b c
大き
さ
3 5 11
金額 5 7 21
2011/4/27 アルゴリズムとデータ構造 15 19
本講義で触れられなかった話題
 圧縮・暗号化アルゴリズム
 乱数生成アルゴリズム
 計算幾何
 凸包,重なり問題,最近点問題
 数値計算アルゴリズム
 多倍長数値演算,数値微分・数値積分,連立一次方程式の解法
 常微分方程式の解法,行列の固有値問題,高速フーリエ変換
( FFT )
 線形計画法
 線形制約のもとで目的関数を最大化する
 メタヒューリスティック
 擬似焼きなまし法,遺伝的アルゴリズム
 並列アルゴリズム
 ・・・
2011/4/27 アルゴリズムとデータ構造 15 20
教科書・参考書の紹介
 どれでもよいので,一冊手元に置いて,じっくり読んで理解
することを勧める
 石畑清,「アルゴリズムとデータ構造」,岩波講座ソフト
ウェア科学3, 1989 .
 探索,整列といった一般的なアルゴリズム全般を丁寧に解説して
いる
 本講座の内容の多くはこの教科書に沿ったもの
 R. セジウィック,「アルゴリズム C++ 」,近代科学
社, 1994 .
 様々なアルゴリズムに関して系統的に解説している
 C++ のコード例も豊富
 3 分冊になっているが C 言語版もある
 T.H.Cormen++, “Introduction to Algorithms”, MIT Press,
 かなり専門的な内容の教科書で,アルゴリズムの証明や計算量の
議論も詳しい
 一応,日本語訳もある

アルゴリズムとデータ構造15

  • 1.
  • 2.
    2011/4/27 アルゴリズムとデータ構造 152 N クイーン問題  N×N のチェス盤に, N 個のクイーンの駒を置いて,それぞれが 他のクイーンの利き筋(縦,横,斜め)に当たらないようにす る  N =8の例:正しい配置 間違った配置
  • 3.
    2011/4/27 アルゴリズムとデータ構造 153 N クイーン問題の力まかせ法による 解  サイズ n の N クイーン問題を解く def n_queen(n): A = array(n) for i in range(n): A[i] = 0 while A != None: if valid(A, n): return A A = next(A, n) return None  次候補となる盤面を返す def next(A, n): for i in range(n): if A[i] == n-1: A[i] = 0 else: A[i] = A[i] + 1 return A return None  盤面が有効かどうかチェック def valid(A, n): for i in range(n-1): k = 1 for j in range(i+1, n): if A[i] == A[j]: return False if (A[i] == A[j] + k or A[j] == A[i] + k): return False k = k + 1 return True
  • 4.
    2011/4/27 アルゴリズムとデータ構造 154 バックトラック法  力まかせをより効率的に行 う  問題の解が n 個の部分から 構成されるとする.  これらの値を一つずつ決め ていく  いくつかの部分の値を決め た時点で,これ以降どう決 めても解が得られないとわ かったら,その決め方に関 する計算はそこで打ち切る  以上の手順を深さ優先探索 の要領で,すべての解の可 能性を調べ終わるまで続け る 1 2 3 n・・・ ・・・ 1 2 3 4 n 21 3 4 n × × × × × × × × × 1 2 3 4 5 n ・・・ 1 列目のク イーンの位置 2 列目のク イーンの位置 3 列目のク イーンの位置 ・・・
  • 5.
    2011/4/27 アルゴリズムとデータ構造 155 N クイーン問題のバックトラック 解法 def n_queen(n): B = board(n); backtrack(B, n, 0) def board(n): b = array(n); for i in range(n): b[i] = array(n); for j in range(n): b[i][j] = False return b def backtrack(B, n, level): if level >= n: print(B); return row = level for col in range(n): if check(B, row, col): B[row][col] = True backtrack(B, n, level + 1) B[row][col] = False
  • 6.
    2011/4/27 アルゴリズムとデータ構造 156 N クイーン問題のバックトラック解 法 (2) def check(B, row, col): for i in range(len(B)): if B[row][i] or B[i][col]: return False if row+i < n and col+i < n and B[row+i][col+i]: return False if row-i >= 0 and col+i < n and B[row-i][col+i]: return False if row+i < n and col-i >= 0 and B[row+i][col-i]: return False if row-i >= 0 and col-i >= 0 and B[row-i][col-i]: return False return True
  • 7.
    2011/4/27 アルゴリズムとデータ構造 157 置けるかどうかの判定を改良する  horizontal  0 ~ n-1  それぞれの段に既に置 かれているかチェック  major  0 ~ (row+col) ~ n+n-2  対角線方向に置かれて いるかチェック  minor  1-n ~ (row-col) ~ n-1  major とは逆の対角線 方向に置かれているか チェック  練習問題  上記の配列を利用した 改良版のコードを書け horizontal major minor [0] [1] [2] [n+n-2] [n-1] [0] [1][-1] [0] [1] [n-1] [n-1][1-n] 3 1A [0] [1] [2]
  • 8.
    2011/4/27 アルゴリズムとデータ構造 158 解答例 def n_queen(n): b = board(n); backtrack(b, n, 0) def board(n): b = record([‘rows’, ‘horizontal’, ‘major’, ‘minor’]) b.rows = array(n) b.horizontal = array(n) for i in range(n): b.horizontal[i] = True b.major = array(2 * n – 1) b.minor = array(2 * n – 1) for i in range(2 * n – 1): b.major[i] = b.minor[i] = True return b
  • 9.
    2011/4/27 アルゴリズムとデータ構造 159 解答例(続き) def backtrack(B, n, level): if level >= n: print(B.rows); return row = level for col in range(n): B.rows[row] = col if (B.horizontal[col] and B.major[row+col] and B.minor[row-col]): B.horizontal[col] = False B.major[row+col] = False B.minor[row-col] = False backtrack(B, n, level + 1) B.rows[col] = None B.horizontal[col] = True B.major[row+col] = True B.minor[row-col] = True
  • 10.
    2011/4/27 アルゴリズムとデータ構造 1510 分割統治法  問題をいくつかの部分問題に分割し,それぞ れを独立に解く.  その結果を組み合わせて全体の問題の解とす る  部分問題は元の問題と同じもので,ただデータの 少ないなどの点で簡単になっているだけが違い  再帰呼び出しを用いて実装することができる  例:  クイックソート,マージソート
  • 11.
    2011/4/27 アルゴリズムとデータ構造 1511 動的計画法( DP )  より小さい部分問題の解を集めて,大きい問 題の解を構成する  分割統治法と似ているが,各部分問題はちょうど 一度だけ解くようにする  表 or 再帰呼び出し+メモ化で実装できる  例: Floyd 法
  • 12.
    2011/4/27 アルゴリズムとデータ構造 1512 動的計画法の例  フィボナッチ数列:  再帰的定義にもとづくプログラム def fib(n): if n == 0 or n == 1: return 1 else: return fib(n-1) + fib(n-2)  動的計画法を利用したプログラム def fib(n): n1 = n2 = result = 1 for i in range(1, n): result = n1 + n2 n1 = n2 n2 = result return result FIB(4) FIB(3) FIB(2) FIB(2) FIB(1) FIB(1) FIB(0) FIB(1) FIB(0) 同じ計算を何 度も繰り返す ため,非効率 的
  • 13.
    2011/4/27 アルゴリズムとデータ構造 1513 Levenshtein Distance (編集距離)  2つの文字列がどれだけ異なっているか  2つの文字列を一致させるために必要な文字の挿 入,削除,置換操作の最小回数  ”例: kitten” ”と sitting”  kitten ⇒ sitten sitt⇒ in sittin⇒ g  LD(“kitten”, ”sitting”) = 3  練習問題:次の文字列の編集距離はいくら か?  apple と couple
  • 14.
    2011/4/27 アルゴリズムとデータ構造 1514 DP を用いて編集距離を求める  アルゴリズムのポイ ント  長さ 0 の文字列と長さ n の文字列の LD は n  LD(s1,s2) は,以下のう ちの最小のもの  置換: LD(s1-1,s2-1) + (if s1.last = s2.last then 0 else 1)  挿入: LD(s1-1,s2) + 1  削除: LD(s1,s2-1) + 1 ※ 文字列 s-1 は文字列 s から末尾の文字 を   削除したものとする k i t t e n 0 1 2 3 4 5 6 s 1 1 2 3 4 5 6 i 2 2 1 2 3 4 5 t 3 3 2 1 2 3 4 t 4 4 3 2 1 2 3 i 5 5 4 3 2 2 3 n 6 6 5 4 3 3 2 g 7 7 6 5 4 4 3
  • 15.
    2011/4/27 アルゴリズムとデータ構造 1515 LD を求めるコード例 def levenshtein_distance(s1, s2): m = len(s1) n = len(s2) D = array2(m+1, n+1) for i in range(m+1): D[i][0] = i for j in range(n+1): D[0][j] = j for i in range(1, m+1): for j in range(1, n + 1):: if s1[i-1] == s2[j-1]: cost = 0 else: cost = 1 D[i][j] = min(D[i-1][j]+1, D[i][j-1]+1, D[i-1][j-1]+cost) return D[m][n]
  • 16.
    2011/4/27 アルゴリズムとデータ構造 1516 (整数)ナップサック問題  大きさ W のナップサックに N 種類の品物を詰め込 むことを考える  詰め込んだ品物の合計金額を最大にしたい  各品物は何個取っても良い  例:  ナップサックの大きさ:21  品物の種類:  (a) 大きさ3,金額5  (b) 大きさ5,金額7  (c) 大きさ11,金額21  練習問題  上記例の状況で,ナップサックに詰め込んだ品物の最大の 金額と,それぞれの品物の数はどれくらいになるか?
  • 17.
    2011/4/27 アルゴリズムとデータ構造 1517 動的計画法を用いた解法  入力:問題 P( ナップサックの大きさ w, 品物リスト items)  items[i].size は品物 i の大きさ, items[i].value は金額を表す def knapsack(P): cost = array(P.w) best = array(P.w) for i in range(P.w): cost[i] = best[i] = 0 for i in range(len(P.items)): for j in range(P.w): if j >= P.items[i].size: if cost[j] < cost[j-P.items[i].size]+P.items[i].value: cost[j] = cost[j-P.items[i].size]+P.items[i].value best[j] = i return cost[P.w-1]
  • 18.
    2011/4/27 アルゴリズムとデータ構造 1518 実行の様子 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 5 5 5 10 10 10 15 15 15 20 20 20 25 25 25 30 30 30 35 a a a a a a a a a a a a a a a a a a a 5 5 7 10 10 12 15 15 17 20 20 22 25 25 27 30 30 32 35 a a b a a b a a b a a b a a b a a b a 5 5 7 10 10 12 15 15 21 21 21 26 26 28 31 31 33 36 36 a a b a a b a a c c c c c c c c c c c j i cost1[] best1[] cost2[] best2[] cost3[] best3[] 品物 a b c 大き さ 3 5 11 金額 5 7 21
  • 19.
    2011/4/27 アルゴリズムとデータ構造 1519 本講義で触れられなかった話題  圧縮・暗号化アルゴリズム  乱数生成アルゴリズム  計算幾何  凸包,重なり問題,最近点問題  数値計算アルゴリズム  多倍長数値演算,数値微分・数値積分,連立一次方程式の解法  常微分方程式の解法,行列の固有値問題,高速フーリエ変換 ( FFT )  線形計画法  線形制約のもとで目的関数を最大化する  メタヒューリスティック  擬似焼きなまし法,遺伝的アルゴリズム  並列アルゴリズム  ・・・
  • 20.
    2011/4/27 アルゴリズムとデータ構造 1520 教科書・参考書の紹介  どれでもよいので,一冊手元に置いて,じっくり読んで理解 することを勧める  石畑清,「アルゴリズムとデータ構造」,岩波講座ソフト ウェア科学3, 1989 .  探索,整列といった一般的なアルゴリズム全般を丁寧に解説して いる  本講座の内容の多くはこの教科書に沿ったもの  R. セジウィック,「アルゴリズム C++ 」,近代科学 社, 1994 .  様々なアルゴリズムに関して系統的に解説している  C++ のコード例も豊富  3 分冊になっているが C 言語版もある  T.H.Cormen++, “Introduction to Algorithms”, MIT Press,  かなり専門的な内容の教科書で,アルゴリズムの証明や計算量の 議論も詳しい  一応,日本語訳もある