【问题标题】:How to print values in memoization method-Dynamic pragraming如何在记忆方法中打印值-动态编程
【发布时间】:2016-12-02 01:40:50
【问题描述】:

我知道一个可以使用 DP 解决的问题,可以通过制表(自下而上)方法或记忆化(自上而下)方法来解决。我个人发现记忆是简单甚至有效的方法(只需分析即可获得递归公式,一旦获得递归公式,就可以轻松地将暴力递归方法转换为存储子问题的结果并重用它。)唯一的问题是我在这种方法中面临的是,我无法从我按需填写的表中构造实际结果。

例如,在Matrix Product Parenthesization problem 中(决定在矩阵上执行乘法的顺序以使乘法成本最小)我能够计算无法在算法中生成顺序的最小成本。

例如,假设 A 是 10 × 30 矩阵,B 是 30 × 5 矩阵,C 是 5 × 60 矩阵。那么,

(AB)C = (10×30×5) + (10×5×60) = 1500 + 3000 = 4500 operations
A(BC) = (30×5×60) + (10×30×60) = 9000 + 18000 = 27000 operations.

在这里我可以得到 27000 的最低成本,但无法获得 A(BC) 的订单。

我用过这个。假设 F[i, j] 表示与 Ai.....Aj 相乘所需的最少乘法次数,并且给出了一个数组 p[],它表示矩阵链,使得第 i 个矩阵 Ai 的维数为 p[i-1 ] xp[i]。所以

                    0                 if i=j
     F[i,j]=
                   min(F[i,k] + F[k+1,j] +P_i-1 * P_k * P_j   where k∈[i,j)


下面是我创建的实现。

#include<stdio.h>
#include<limits.h>
#include<string.h>
#define MAX 4
int lookup[MAX][MAX];

int MatrixChainOrder(int p[], int i, int j)
{
    if(i==j) return 0;
    int min = INT_MAX;
    int k, count;

    if(lookup[i][j]==0){
        // recursively calculate count of multiplcations and return the minimum count
        for (k = i; k<j; k++) {
            int gmin=0;
            if(lookup[i][k]==0)
                lookup[i][k]=MatrixChainOrder(p, i, k);

            if(lookup[k+1][j]==0)
                lookup[k+1][j]=MatrixChainOrder(p, k+1, j);

            count = lookup[i][k] + lookup[k+1][j] + p[i-1]*p[k]*p[j];
            if (count < min){
                min = count;
              printf("\n****%d  ",k); // i think something has be done here to represent the correct answer ((AB)C)D  where first mat is represented by  A second by B and so on.
            }

        }
        lookup[i][j] = min;
    }

    return lookup[i][j];
}

// Driver program to test above function
int main()
{
    int arr[] = {2,3,6,4,5};
    int n = sizeof(arr)/sizeof(arr[0]);

    memset(lookup, 0, sizeof(lookup));
    int width =10;

    printf("Minimum number of multiplications is %d ", MatrixChainOrder(arr, 1, n-1));
    printf("\n  ---->");
    for(int l=0;l<MAX;++l)
    printf(" %*d ",width,l);
    printf("\n");
    for(int z=0;z<MAX;z++){
        printf("\n  %d--->",z);
    for(int x=0;x<MAX;x++)
    printf(" %*d ",width,lookup[z][x]);
    }

    return 0;
}

我知道使用制表方法打印解决方案很容易,但我想在记忆技术中做到这一点。


谢谢。

【问题讨论】:

    标签: algorithm matrix dynamic-programming memoization


    【解决方案1】:

    您的代码正确地计算了最小乘法次数,但您正在努力显示矩阵乘法的最佳链。

    有两种可能:

    1. 计算表时,可以将找到的最佳索引存储在另一个记忆数组中。
    2. 您可以根据记忆数组中的结果重新计算最佳分割点。

    第一个将涉及在单独的数组中创建分割点:

    int lookup_splits[MAX][MAX];
    

    然后在 MatrixChainOrder 函数中更新它:

        ...
        if (count < min) {
            min = count;
            lookup_splits[i][j] = k;   
        }
    

    然后您可以像这样递归地生成乘法链:

    void print_mult_chain(int i, int j) {
        if (i == j) {
            putchar('A' + i - 1);
            return;
        }
        putchar('(');
        print_mult_chain(i, lookup_splits[i][j]);
        print_mult_chain(lookup_splits[i][j] + 1, j);
        putchar(')');
    }
    

    您可以使用print_mult_chain(1, n - 1)main 调用该函数。

    第二种可能性是你没有缓存lookup_splits并在必要时重新计算它。

    int get_lookup_splits(int p[], int i, int j) {
        int best = INT_MAX;
        int k_best;
        for (int k = i; k < j; k++) {
            int count = lookup[i][k] + lookup[k+1][j] + p[i-1]*p[k]*p[j];
            if (count < best) {
                best = count;
                k_best = k;
            }
        }
        return k;
    }
    

    这与您在 MatrixChainOrder 中所做的计算基本相同,因此如果您使用此解决方案,您应该适当地考虑代码以避免有两个副本。

    有了这个函数,你可以调整上面的print_mult_chain来使用它而不是lookup_splits数组。 (您需要传入 p 数组)。

    [此代码均未经过测试,因此您可能需要编辑答案以修复错误]。

    【讨论】:

    • 我得到的第一个,它工作正常,但在第二种情况下它是如何完成的?
    • 我不明白你在问什么关于第二种情况。你不知道它是如何工作的,或者你不知道如何使用新功能编写print_mult_chain
    • 我没有得到你想要在第二个 fn 中实现的目标。你没有在附加数组中记住 k。从哪里调用 fn get_lookup_splits,它与我用于查找填充的代码非常相似.
    • 它可以在打印链时替换对lookup_splits 数组的访问。请参阅:gist.github.com/paulhankin/6c43ab65236b52dce643d92f19fabdbf。每次调用都是 O(n),因此打印链 O(n^2),但 MatrixChainOrder 已经是 O(n^2) [在最坏的情况下都是如此]。
    猜你喜欢
    • 2021-10-01
    • 1970-01-01
    • 2013-05-11
    • 1970-01-01
    • 2021-03-24
    • 1970-01-01
    • 2020-12-10
    • 2010-10-15
    • 2023-03-05
    相关资源
    最近更新 更多