簡體   English   中英

使用動態編程遞歸到迭代轉換

[英]Recursion to iteration conversion using dynamic programming

    public static int n;
    public static int w;
    public static int[] s;
    public static int[] p;

    static void Main(string[] args)
    {
        n = 5;
        w = 5;

        s = new int[n + 1];
        p = new int[n + 1];
        Random rnd = new Random();

        for (int i = 1; i <= n; i++)
        {

            s[i] = rnd.Next(1, 10);
            p[i] = rnd.Next(1, 10);
        }

        Console.WriteLine(F_recursion(n, w));
        Console.WriteLine(DP(n, w));
    }

    // recursive approach
    public static int F_recursion(int n, int w)
    {
        if (n == 0 || w == 0)
            return 0;
        else if (s[n] > w)
            return F_recursion(n - 1, w);
        else
        {                          
            return Math.Max(F_recursion(n - 1, w), (p[n] + F_recursion(n - 1, w - s[n])));
        }
    }

    // iterative approach
    public static int DP(int n, int w)
    {
        int result = 0;

        for (int i = 1; i <= n; i++)
        {

            if (s[i] > w)
            {
                continue;
            }
            else
            {                   
                result += p[i];
                w = w - s[i];
            }
        }

        return result;
    }

我需要將F_recursion函數轉換為迭代函數。 我目前正在編寫下面的函數DP,該函數有時有效,但並非總是如此。 我了解到問題出在F_recursion(n-1, w-s [n] )中,我不知道如何在迭代解決方案中使w-s [n]正常工作。 如果將w-s [n]和w-s [i]更改為僅w,則程序始終有效。

在控制台中:

s[i] = 2 p[i] = 3
-------
s[i] = 3 p[i] = 4
-------
s[i] = 5 p[i] = 3
-------
s[i] = 3 p[i] = 8
-------
s[i] = 6 p[i] = 6
-------
Recursive:11
Iteration:7

但有時它有效

s[i] = 5 p[i] = 6
-------
s[i] = 8 p[i] = 1
-------
s[i] = 3 p[i] = 5
-------
s[i] = 3 p[i] = 1
-------
s[i] = 7 p[i] = 7
-------
Recursive:6
Iteration:6

當涉及到更大的數字(特別是s )時,以下方法可能很有用,因此二維數組就不必要大了,實際上只有幾個w值會用於計算結果。

這個想法:通過從w開始,對i in [n, n-1, ..., 1]每個i in [n, n-1, ..., 1]預先計算可能的w值,確定值w_[i] ,其中w_[i+1] >= s[i]無重復項。 然后在n迭代i_n並僅對有效w_[i]值計算子結果。

我選擇了一個Dictionary數組作為數據結構,因為這樣設計稀疏數據相對容易。

public static int DP(int n, int w)
{
    // compute possible w values for each iteration from 0 to n
    Stack<HashSet<int>> validW = new Stack<HashSet<int>>();
    validW.Push(new HashSet<int>() { w });
    for (int i = n; i > 0; i--)
    {
        HashSet<int> validW_i = new HashSet<int>();
        foreach (var prevValid in validW.Peek())
        {
            validW_i.Add(prevValid);
            if (prevValid >= s[i])
            {
                validW_i.Add(prevValid - s[i]);
            }
        }
        validW.Push(validW_i);
    }

    // compute sub-results for all possible n,w values.
    Dictionary<int, int>[] value = new Dictionary<int,int>[n + 1];
    for (int n_i = 0; n_i <= n; n_i++)
    {
        value[n_i] = new Dictionary<int, int>();
        HashSet<int> validSubtractW_i = validW.Pop();
        foreach (var w_j in validSubtractW_i)
        {
            if (n_i == 0 || w_j == 0)
                value[n_i][w_j] = 0;
            else if (s[n_i] > w_j)
                value[n_i][w_j] = value[n_i - 1][w_j];
            else
                value[n_i][w_j] = Math.Max(value[n_i - 1][w_j], (p[n_i] + value[n_i - 1][w_j - s[n_i]]));
        }
    }

    return value[n][w];
}

重要的是要了解一些空間和計算是“浪費”的,以便預先計算可能的w值並支持稀疏的數據結構。 因此,此方法對於s具有較小值的大型數據集可能效果不佳,其中大多數w值可能是子結果。

經過一番思考后,我意識到,如果需要考慮空間問題,那么實際上可以丟棄除上一次外循環迭代之外的所有內容的子結果,因為該算法的遞歸遵循嚴格的n-1模式。 但是,我暫時不將其包含在我的代碼中。

您的方法行不通,因為您的動態programmig狀態空間(顯然只有一個變量)與遞歸方法的簽名不匹配。 動態編程方法的目標應該是定義和填充狀態空間,以便在需要時可以使用所有評估結果。 在檢查遞歸方法時,請注意F_recursion的遞歸調用可能會同時更改參數nw 這表明應該使用二維狀態空間。

第一個參數(顯然限制了項的范圍)的范圍可以從0n而第二個參數(顯然對項屬性的總和有限制)的范圍可以從0w

您應該定義一個二維狀態空間

int[,] value = new int[n,w];

用於容納值。 接下來,您應該將值初始化為undefined; 您可以為此使用值Int32.MaxValue ,因為如果計算出具有不同值的最小值,它將以合適的方式運行。

接下來,該算法的迭代版本應使用兩個循環,以預先方式進行迭代,這與減少參數的遞歸迭代不同。

for (int i = 0; i < n; i++)
{
    for (int j = 0; j < w; j++)
    {
        // logic for the recurrence relation goes here
    }
}

在最里面的塊中,您可以使用遞歸關系的修改版本。 您無需訪問遞歸調用,而訪問存儲在value ;中的value 而不是返回值,而是將值寫入value

從語義上講 ,這與備忘錄相同,但是評估順序斷言始終存在必要的值,從而使不必要的附加邏輯不再使用實際的遞歸調用。

一旦狀態空間被填充,您必須檢查其最后狀態(即數組中第一個索引為n-1 ),以確定整個輸入的最大值。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM