[アルゴリズム]動的計画の基礎

16693 ワード

ソフトウェアExpert Academy 1~5を参照してください.
1.フィボナッチ数
Introduction
以下の条件の下でnヶ月のウサギの数は?
  • 最初の月に新生ウサギが
  • 存在した.
  • 2 2ヶ月以上のウサギは
  • 繁殖できます
  • 繁殖ウサギは毎月1対の幼子
  • を産むことができる.
  • ウサギ不死
  • 正解
  • nヶ月:a対
  • n+1ヶ月:b対(新生ウサギを含む)
  • n+2ヶ月:a+b対(生a子)
  • 結論:f(n+2)=f(n)+f(n+1)
  • Concept
    フィボナッチ数列
    1つの数の列は、0と1から始まり、最初の2つの数の和は次の項です.
    Code 1
    def fibo(n):
    	if n<2:
    		return n
    	else : 
    		return fibo(n-1) + fibo(n-2)
    BUT! 再帰関数を使用してフィボナッチ数を実装すると、大量の繰り返し呼び出しが存在する.
    2.数学の帰納法と鳩の巣の原理
    数学的帰納法
    フィボナッチ関数がどれだけ繰り返し呼び出されるかを知る方法
    1) 귀납 기본 : 주어진 등식이 n=1(또는 n=0)일 때 성립함을 증명
    2) 귀납 가정 : 임의의 n일 때 성립한다고 가정
    3) 귀납 단계 : n+1일 때 성립함을 증명
    4) 모든 n에 대하여 성립
    証明:数学の帰納法
    仮定:再帰アルゴリズムで構築された再帰ツリーにおけるノード数:T(n)T(n)T(n)
    n≧2 ngeq 2 n≧2のすべてのnnnに対して,T(n)>2 n/2 T(n)>2^{n/2}T(n)>2 n/2
    1)帰納基本:T(2)=T(1)+T(0)+1=3>2=22/2 T(2)=T(1)+1=3>2=2^{2/2}T(1)+1=3>2=2
    2)帰納仮定:2≦m2 m/2 T(m)>2^{m/2}T(m)>2 m/2
    3)帰納段階:
    T(n)=T(n−1)+T(n−2)+1T(n) = T(n-1)+T(n-2)+1T(n)=T(n−1)+T(n−2)+1
    >2(n−1)/2+2(n−2)/2+1> 2^{(n-1)/2} + 2^{(n-2)/2} +1>2(n−1)/2+2(n−2)/2+1
    >2(n−1)/2+2(n−2)/2> 2^{(n-1)/2} + 2^{(n-2)/2}>2(n−1)/2+2(n−2)/2
    =2∗2(n/2)−1= 2*2^{(n/2)-1}=2∗2(n/2)−1
    =2n/2=2^{n/2}=2n/2
    ハトの部屋の原理
  • ハト:n+1羽
  • 鳩の巣:n個
  • それぞれ任意のハトの巣を選択して
  • に入る
  • 少なくとも2羽以上のハトがいる
  • 証明:照合
    仮にn+1 n+1 n+1個の物品をnnn個の箱に入れると、少なくとも1個の箱に2個以上の物品が入っている
    1)nnn個の箱とn+1 n+1 n+1個のものがあると仮定する
    2)それぞれの箱に以下のものが1つあると,すべての箱の中のものの総和が多く,nnn個=家庭的矛盾の結論となる.
    3)そのため、少なくとも1つの箱に2つ以上のものが入っています
    証明:照合
    仮定:重複するフィボナッチ数列
    1)n番目のフィボナッチ数を求めるために知るべき値:fibo(0)fibo(0)fibo(0)~fibo(nά1)fibo(n-1)fibo(nά1)
    2)再帰呼び出しで記述されたfibo(n)fibo(n)fibo(n)fibo(n)関数を呼び出してn個のフィボナッチ数を求める
    -fibo()fibo()fibo()関数2 n/22^{n/2}2 n/2を複数回呼び出す
    - 2n/2>n2^{n/2} > n2n/2>n
    3)以上の証明により,フィボナッチ関数の再帰呼び出しは大量の繰返しをもたらす.
    結論:フィボナッチ関数の時間複雑度はnの増大と共に増大し,2 n 2^n 2 nに比例した.
    3.注記モデリングとダイナミックプランニング
    アノテーション
    コンピュータプログラムを実行するときに、以前に計算した値をメモリに格納して、再計算のたびに全体の実行速度を速める技術です.
    =ダイナミックプランニング法を適用するキーテクノロジー
    フィボナッチ数を解くアルゴリズムでは,入力値nの計算結果を保存することで,実行時間をO(n)O(n)O(n)O(n)O(n)O(n)に減らすことができる.
    Code2
    # memo 배열을 할당하고, 모두 0으로 초기화
    # memo[0]을 0으로 memo[1]은 1로 초기화
    
    def fibo(n):
    	if n>=2 and memo[n] = 0:
        		memo[n] = fibo(n-1) + fibo(n-2) # 처음 계산되는 값이라면 값 저장
    	return memo[n]
    BUT! 余分なメモリ領域が必要なため、n値が大きいほど実行速度が遅くなり、スタックオーバーフローが発生する可能性があります.
    ダイナミックプランニング
    1つの設計方法は、小さい部分から大部分まで解くことによって、最終的に元の与えられた問題を解決する.
    1) 최적화 문제 해결 : 최대값/최소값 구하는 문제, 여러 개의 최적해 중 임의의 최적해 하나를 찾는 것
    2) 완전 검색을 좀 더 효율적으로 하는 방법
    3) 재귀 + 메모이제이션
    4) 점화식을 찾으면 됨 : 문제를 분석하여 재귀적 정의 및 수식 형태 표현 필요
    アプリケーション要件
    1)重複部分問題構造
    - 순환적인 관계(recurrence relation)의 명시적 표현 : 점화식 사용
    - 문제의 순환적 성질로 이전에 계산된 작은 문제의 해가 더 큰 문제의 해를 구할 때 중복으로 사용됨
    - 메모이제이션 사용
    - 이미 해결한 작은 문제의 해가 다시 필요할 때 테이블을 참조하여 중복 계산을 피함
    2)最適な局所問題構造
    - 주어진 문제가 최적화의 원칙을 만족해야 동적 계획법을 효율적으로 적용 가능
    - 최적화 원칙 : 어떤 문제에 대한 해가 최적일 대 그 해를 구성하는 작은 문제들의 해 역시 최적이어야 함
    最適化の原則が適用されない例:最長パスの問題
    -最長パス:ループなしの単純パス
    分割征服VSダイナミックプランニング
    特徴分割征服動的計画問題分割所与の問題を局所問題とし,分割部分問題がより小さい部分問題の解を共有部分問題解の目的がより大きい部分問題の解に変換する場合,より大きな部分問題に属する局所問題を繰り返すことで,局所問題解の計算部分問題を再帰的に解決し,必要に応じて各解を結合することができる.局所問題を1回計算するだけで,結果として部分問題解の活用合併並べ替えと高速並べ替えを小問題解の大きい問題解で繰り返し使用し,必要に応じて部分問題間依存関係を下方シフトする方法を用いる:与えられた大問題に対して小問題がある上方シフト方法:依存性に背かず小問題を解くもっと大きな問題を解く
    動的計画の適用方法
    1) 최적해 구조의 특성 파악 : 문제를 더 작은 부분 문제로 나눔
    2) 최적해 값의 재귀적 정의 : 부분 문제들의 최적 해를 사용하여 더 큰 문제의 최적해 값 정의(점화식 사용 가능)
    3) 상향식 방법으로 최적해 값 계산 : 의존성에 위배되지 않도록 가장 작은 부분문제부터 해를 구하고 테이블 저장, 이를 이용하여 상위 문제 해결
    フィボナッチ数列と動的計画法
    Code 3
    f = [] * n
    
    def fibo_dp(n):
    	f[0] = 0
        f[1] = 1
        for i in range(2, n):
        # n=2인 문제부터 시작해서 큰 문제를 해결해나감
        	f[i] = f[i-1] + f[i-2]
        return f[n] # n번째 인덱스에 n번째 피보나치 수가 저장된 상태
    結論:フィボナッチ数を用いた動的計画は再帰アルゴリズムよりも実行速度が速い(関数呼び出しと繰返し計算がないため)
    4.硬貨のお釣り問題と二項式係数問題
    小銭の問題
    動的計画の適用
    使えるコインが1元、4元、6元の場合、8元の小銭の最低コインの個数はいくらですか?
    階調アプローチ接近:6/1/1ウォン
    最適:4/4元
    8元の再帰アルゴリズムを検索
    =3種類の硬貨を選択して再帰的に解決する
  • 元+7元の最適値
  • 4元+4元の最適値
  • 6元+2元最適
  • 3年中最小の年-
  • ダイナミックプランニングメソッドダイナミックプランニングメソッド:上へ
  • 元の最良年から8元の最良年まで、羅鑑
  • を徐々に求めた.
  • お釣り金額0元~所要金額が1元増えるごとに全ての部分問題の解
  • を求める.
    change = 0 # 거스름돈
    coin = [6,4,1]
    memo = []*n
    
    def coin_change(change):
    	memo[0] = 0 # 거스름돈이 0일때는 0개의 동전이 필요
        for n in range(1,change):
        		n_min = 9999999 # 무한대
                for i in range(1, len(coin)-1):
                	if n>=coin[i]:
                    	if memo[n-coin[i]] < n_min :
                        	#현재 선택한 동전의 금액을 차감한 금액이 최소값인지 계산
                        		n_min = memo[n-coin[i]]
                 memo[n] = n_min +1
        return memo[change] # 거스름돈이 change일 때 필요한 최소 동전 갯수를 반환
                        	
    にこうけいすうもんだい
    (x+y)4=x4+4x3y+?x2y2+4xy3+y4(x+y)^4 = x^4+4x^3y+?x^2y^2+4xy^3+y^4(x+y)4=x4+4x3y+?x2y2+4xy3+y4
    この定理:(x+y)n(x+y)^n(x+y)n展開時にxkyn–kx^ky^{n-k}xkyn–kの係数値を求める定理
    二項係数:xkyn-kx^ky^{n-k}xkyn-kの係数はn個からk個の組合せの偽数である
    パスカルさんかくけい
    さんかくけい
    ルール#ルール#
  • n行目:n個の数字
  • 各行両端:数字1
  • 三角形内部数字:上方対角線数字の和
  • 再帰呼び出しを使用してこの係数を計算=重複呼び出しが多い
    n = 정수
    k = n보다 같거나 작은 수
    
    def bino(n,k):
    	if k==0 or n==k:
    		return 1
    	else:
    		return bino(n-1, k-1) + bino(n-1, k)
    部分的な問題の依存性
    B012...j0(0,0)1(1,0)(1,1)2(2,0)(2,1)(2,2)...i??
  • 位?値を求めるためには、B[i,j,1],B[i,1,j],B[i,1,j][i,b[i,1],B[i,j]の値を知る必要があり、
  • は、非依存性の計算
  • を回避するために解く必要がある.
    まず、
  • 行(-->)
  • をナビゲートする必要があります.
    動的計画法を用いて二項係数=O(nk)を計算する
    def bino(n,k):
    	B = [[] for _ in range(N)]
    	for i in range(0,n):
    		for j in range(0, min(i,k)):
    			if j == 0 or j == i:
    				B[i][j] = 1
    			else:
    				B[i][j] = B[i-1][j-1] + B[i-1][j]
    	return B[n][k]