DPダイナミック・プログラミング・知人

HMMパラメータソリューションのフロントでは、動的プログラミングで使用される、いくつかの抽象的、DPが理解するために、栗の簡単な波を与えることを本日決定した確率式のすべての種類の完全な、そこにされ、大きな問題を入れ続ける小さなサブに分割します問題解決するには、この方法では、動的計画である。これは、最も直感的かつ人気の理解です。

再帰的な対DP

DPと再帰再帰は、実際には、前に私はまた、しばしば混乱関数は自分自身を呼び出す。仕方、再帰およびDPで彼らの共通点を持っている。私の理解では、つまり、DPが減少し、再帰的な時間の複雑さを、特定のたとえば、DPは、再帰的に解決し、計算問題(Coverlap)を繰り返す問題を。ヘルプの理解にステーションBからのビデオ段落を見つけるために、問題を説明します。

ケース:フィボナッチ数

知ら:1、1、2、3、5、8、13、21、34 ......

最初の数nの評価。

実際には、これは2つの要素に再帰、再帰フォーカスで表さ問題のクラスの非常に典型的である:終了条件、漸化式

"""
主题:
    动态规划 之 斐波那契数列
问题特点:
    类似递归, 有很多的重复子问题 overlap
描述:
    f(1) = 1
    f(2) = 1
    f(3) = f(2) + f(3)
    f(4) = f(3) + f(2)
    ...
    f(n) = f(n-1) + f(n-1)

"""
def rec_opt(n):
    """递归-求解斐波那契数列"""
    if n <= 2:
        return 1
    return rec_opt(n - 1) + rec_opt(n - 2)

非常によく理解し、そのようなアプローチは、高い時間複雑である問題をもたらすでしょうか?\(O(N-2 ^)\) 例えば、(5)を算出Fへ:

F(5)= F(4)+ F(3)は、2つのフォークに分割されています

左: F = F + F(2)(4)(3)。

F(3)= F(2)+ F(1)

右側: F = F + F(2)(3)(1)。

イェ目が見つからない場合、兄弟を見て、Fの計算における左側(4)ときに算出F(3)、F(2 )、F(1)、 一方、F(3)時間を計算する際に右側、及び二重カウントF(2)、F(1 )、 二重カウントのより大きい量が指数関数的に成長するように、これはオーバーラップと呼ばれる。これは再帰的であり、この時点で、それは非常に最初の時間の複雑さを増加させます。

実際に、どのように我々が考える、時間がFの計算に使用される(3)以降、F(1)、F (2)、F(4) 時間をf(3)+ F(2を使用します )。 ...まあ、なぜ我々は良いを計算するために、これらの過去のデータはそれを直接使用し、最大保存することを考えていないですか?

つまり、あなたは、過去のデータまで保存直接インデックスOKに時間を使うために、リストを使用することができ、複雑さがある: \(O(N)\)そのような使用履歴データは後者の方法は、動的プログラミングで推定します。

  • 終了条件
  • 初期値
  • 状態遷移方程式
def dp_opt(n):
    """DP 求解斐波那契数列"""
    # 用来存历史数据的列表
    lst = [0 for _ in range(n)]
    # 退出条件
    if n < 2:
        return 1
    # 初始化数列的第 0,1 项
    lst[0], lst[1] = 1, 1
    for i in range(2, n):
        # 状态转移方程
        lst[i] = lst[i - 1] + lst[i - 2]
    return lst[n - 1]

ただ、対時間の波をテストします

def calc_func_time(func, n):
    print(f"test 规模为{n}的函数{func}运行时间为:")
    start_time = time.time()
    func(n)
    end_time = time.time()

    print("time used", end_time - start_time, "s.")


if __name__ == '__main__':
    # 递归就是不容易使用装饰器写计时器来哦
    n = 40  # 规模
    calc_func_time(dp_opt, n)
    calc_func_time(rec_opt, n)
C:\Python\Python36\python.exe E:/Pytest/base_case/DP-斐波那契数列.py
test 规模为40的函数<function dp_opt at 0x00000271927B7840>运行时间为:
time used 0.0 s.
test 规模为40的函数<function rec_opt at 0x0000027190902E18>运行时间为:
time used 39.237488985061646 s.

あなたが見ることができる、再帰の複雑さは、本当にひどいです、そしてDPは非常に穏やかで

1,000人以上の再帰の大きさは、私のコンピュータは、移動することはできません...とDPのテスト波、大きなとき。

if __name__ == '__main__':
    # 递归就是不容易使用装饰器写计时器来哦
    n = 10000  # 1万规模
    calc_func_time(dp_opt, n)
test 规模为100000的函数<function dp_opt at 0x0000026675A67840>运行时间为:
time used 0.5376913547515869 s.

DPは本当に複雑後!岩\(O(N)\)まあ

DPのエントリーレベルの場合

ここでは少し複雑に慣れるために栗を解決するための簡単な方法で、私はまだ勉強ではなく、傲慢.....

全体の章では、説明するために、コアのスキルを:選択した選択か

ケース1:nの階段登るの手順

事実は、このです:陳の学生は、オーダーのレベルを登るために、今、大きな足の非常に非常に嫉妬深いのペアを持っているn個、いずれかの三つのステップアップに行き、それぞれの時間のいずれか一歩を行く、私は尋ねる、陳をトップステップに登るの下から学生が、どのように考えられるシナリオの合計を植えるには?

分析:

そこに登るときに最初、関数f(N)の定義は、n番目のクラスを示しているプログラムの最大数であります

F(1)は、プログラムの1ほとんどの種類、ステップアップ=

スキーム1つのステップダウン1にF(2)= 1まで、歩行ステップ1

Fプログラムの二種類に(3)= 2まで、一歩進みを3回繰り返した、またはステップに直接行く3

F(4)= F(1)+ F(3)アッププログラムの三種類に、ステップを4回繰り返した。最初のステップ1、次に3ステップ;ステップ3まず、ステップ

...

実際には、F(N)、N 2の選択前に到達しなければならない:いずれかのF(N-1)から選択される、いずれかのFを選択する(N-3)と、2つのケースは、プログラムの総数であります

F(N)= F(N-1)+ F(N-3)

def dp_craw(n):
    # 1. 用list来存储历史数据,可理解为最优方案的函数值
    opt = [0 for _ in range(n+1)]
    # 2. 写退出条件
    if n <= 2:
        return 1
    elif n == 3:
        return 2
    # 3. 初始化历史数据
    opt[0] = 1
    opt[1] = 1
    opt[2] = 2
    # 4. 状态转移方程
    for i in range(2, n):
        opt[i] = opt[i-1] + opt[i - 3]
    return opt[n-1]

if __name__ == '__main__':
    print(dp_craw(50))
C:Python36\python.exe E:/Pytest/base_case/DP爬台阶.py
83316385

もちろん、重複したエントリを持つこの書き込みアップは、複雑さはありません\(O(N)\)ああ、主に思考のこのラインを明確にするために、変更したくないは次のようです。

ケース2:(制約付き)番号の最大値を所定の束を選択

"""
描述:
    arr = [1, 2, 4, 1, 7, 8, 3]
    下标:  0, 1, 2, 3, 4, 5, 6
需求:
    目标: 从数组(列表) 中选择任意个元素, 求出其之和最大的值
    约束: 相邻元素不能选, 如选了8, 则不能选 7 和 3
思路:
    定义一个最优方案函数 opt(n) 表示前n个中最好的选择方案
    比如本例, opt(1) = 1, opt(2)=1
    tips: 选和不选
    抽象: 对于下标i, 有选和不选
        if 选择当前值arr[i]:
            值 = opt(i-2) + arr[i]  不能选相邻元素, 从前i-2个中选最好方案
        else:
            值 = opt(i-1)  不选,则中前i-1 个中选择最好方案
    最后比较取最大, 即: max( (opt(i-1) + arr[i]), opt(i) )
    递归出口:
        opt[0] = arr[0]  前下标0个, 即第一位, 只有一种最优方案,就是选第一个值arr[0]
        opt[1] = max( arr[0], arr[1]) 前两个,则从1,2中选最大

"""


def rec_opt(arr, i):
    """递归"""
    if i == 0:
        return arr[0]
    elif i == 1:
        return max(arr[0], arr[1])
    else:
        select_i = rec_opt(arr, i - 2) + arr[i]
        no_select_i = rec_opt(arr, i - 1)

        return max(select_i, no_select_i)


# 递归是产生很多的重叠子问题, overlap 之前演示过与DP的对比,复杂度高很多
def dp_opt(arr):
    """DP实现"""
    n = len(arr)
    # 用一个list来存储前 i 个最优方案的值, opt 比 lst 要更形象些
    opt = [0 for _ in range(n)]
    # 初始化前0, 前1的小标下的最优方案值
    opt[0] = arr[0]
    opt[1] = max(arr[0], arr[1])
    # 从第3个元素起, 后面项将前面的项作为其子问题
    for i in range(2, n):
        # 选择下标为i的值和不选, 比较取最大, 前面的值都有存opt
        select_i = opt[i - 2] + arr[i]
        no_select_i = opt[i - 1]
        opt[i] = max(select_i, no_select_i)

    return opt[n - 1]


if __name__ == '__main__':
    opt = [1, 2, 4, 1, 7, 8, 3]
    opt1 = [4, 1, 1, 9, 1]

    print(rec_opt(opt, 6))
    print(dp_opt(opt1))

CASE3:用のデジタル合計値

"""
描述:
    arr = [3, 34, 4, 12, 5, 12]
    S = 9
需求:
    从 arr 中选择数字, 使其值和等于定值 S=9, 如果可以返回 True, 否则 False
思路:
    跟之前一样的思路: 对于每个元素, 有两种选择,选 or 不选
    定义一个subset(arr, i, s)
    if 选择arr[i]:
        A =  subset(arr, i, s-arr[i])
    else:
        B = subset(arr, i-1, s)
    A or B 是True 则True

    退出条件:
    if sub_set(arr[i], s) 中 s=0 则 return True
    if sub_set(arr[0], s) 中 arr[0] != s  return False

    还约定, arr的每个元素都是正整数.即当 arr[i] > s, 只考虑不选, 即: sub_set(arr[i-1], s)


"""

def rec_subset(arr, i, s):
    if s == 0:
        return True
    elif i == 0:
        return arr[0] == s
    elif arr[i] > s:
        return rec_subset(arr, i - 1, s)
    else:
        # arr[i] < s, 有选和不选两种情况
        select_i = rec_subset(arr, i - 1, s - arr[i])
        no_select_i = rec_subset(arr, i - 1, s)
        # 二者其中一个满足条件即可
        return select_i or no_select_i
    
    
import numpy as np

# 用2维数组来记录,行代表arr[i], 列代表s=1, s=2...
def dp_subset(arr, s):
    """DP求解"""
    subset = np.zeros((len(arr), s + 1), dtype=bool)
    subset[:, 0] = True
    subset[0, :] = False
    subset[0, arr[0]] = True
    for i in range(1, len(arr)):
        for s in range(1, s + 1):
            if arr[i] > s:
                subset[i, s] = subset[i - 1, s]
            else:
                select_i = subset[i - 1, s - arr[i]]
                no_select_i = subset[i - 1, s]
                subset[i, s] = select_i or no_select_i

    row, col = subset.shape
    return subset[row - 1, col - 1]


if __name__ == '__main__':
    lst = [3, 34, 4, 12, 5, 12]
    print(rec_subset(lst, len(lst) - 1, 9))
    print(dp_subset(lst, 9))
True
True

このバーの上で取得し、DPの複雑な研究​​の波に続いた。それでも非常に能力を考えて行使する。ああ、Zhashuiそれを感じ、私は特に多次元配列、グラフ、木抽象のこの作品、私にとっては少し難しい感じ。..私の同僚の言葉に....私は、数論であってもよいが、スペースは仕事...この気持ちは、神が食事ああに報いていないいないこと

おすすめ

転載: www.cnblogs.com/chenjieyouge/p/12142877.html