c++ - 为什么内存比制表花费更多时间?

标签 c++ recursion dynamic-programming memoization

我试图解决这个基本的硬币找零动态规划问题:

给定一个值 N,如果我们想要找 N 分钱,并且我们有无限供应每种 S = { S1, S2, .. , Sm} 值(value)的硬币,我们可以有多少种找零方式?硬币的顺序并不重要。 这是我可以创建的解决方案:

我用三种通用方法解决了这个问题。 IE。递归、DP 内存和 DP 制表。

C++ 实现:https://ideone.com/14bBIv

#include <iostream>
#include <cstring>
#include <sys/time.h>
#include <limits.h>

using namespace std;

int max2(int a, int b)
{
    return (a>b) ? a : b;
}

int max3(int a, int b, int c)
{
    if(a>c)
        return (a>b) ? a : b;
    else
        return (c>b) ? c : b;
}

int min3(int a, int b, int c)
{
    if(a<c)
        return (a<b) ? a : b;
    else
        return (c<b) ? c : b;
}


int Coins_rec(int * S, int m, int n)
{
    if(n == 0)
        return 1;

    if(n < 0)
        return 0;

    if(m<=0 && n >=0){
        return 0;
    }

    return ( Coins_rec( S, m-1, n ) + Coins_rec( S, m, n - S[m-1] ) );
}


int memo[101][101] ;


int Coins_memoization(int * S, int m, int n)
{

    if(n == 0)
        return 1;

    if(n < 0)
        return 0;

    if(m<=0 && n >=0){
        return 0;
    }

    if(memo[m][n] !=-1) {

        return memo[m][n];
    } else
    {
        memo[m][n] = ( Coins_rec( S, m-1, n ) + Coins_rec( S, m, n - S[m-1] ) );
        return memo[m][n];
    }

}


int Coins_tabulation (int * S, int m, int n)
{
    int L[m+1][n+1] ;   //L[i][j] -> Minimum number of 'i' different types of coins required to make final amonut j
    int i, j;

    L[0][0] = 1;

    for(i=0;i<=m;i++){
        for(j=0;j<=n;j++){
            if (i == 0 && j >= 0) {
                L[0][j] = 0;
            } else if (i > 0 && j == 0) {
                L[i][0] = 1;
            } else {
                L[i][j] = ( (i >= 1) ? L[i-1][j] : 0 ) + ( (j - S[i-1] >=0) ? L[i][j - S[i-1]] : 0 ) ;
            }
        }
    }
    return L[m][n];
}       // -----  end of function Coins_tabulation  ----- 




int main() {

    int arr[] = {2, 5, 3, 6};
    int m = sizeof(arr)/sizeof(arr[0]);
    int n;

    cout << "Enter the amount" << endl;
    cin >> n;

    for(int i=0; i<=101; i++){
        for(int j=0; j<=101; j++){
            memo[i][j] = -1;
        }
    }

    struct timeval t0; gettimeofday(&t0 , NULL);

    cout << "Number of Ways = " << Coins_rec(arr, m, n) << endl;
    struct timeval t1; gettimeofday(&t1 , NULL);
    cout << "recursion : " << (t1.tv_sec - t0.tv_sec) << " seconds and " << (t1.tv_usec - t0.tv_usec) << " microseconds" << endl;

    cout << "Number of Ways (Memoization) = "  << Coins_memoization(arr, m, n) << endl;
    struct timeval t2; gettimeofday(&t2 , NULL);
    cout << "memoization : " << (t2.tv_sec - t1.tv_sec) << " seconds and " << (t2.tv_usec - t1.tv_usec) << " microseconds" << endl;


    cout << "Number of Ways (Tabulation) = "  << Coins_tabulation(arr, m, n) << endl;
    struct timeval t3; gettimeofday(&t3 , NULL);
    cout << "tabulation : " << (t3.tv_sec - t2.tv_sec) << " seconds and " << (t3.tv_usec - t2.tv_usec) << " microseconds" << endl;

    return 0;

}

。 结果如下: (通过标准输入获取的输入)

For n = 10
recursion : 0 seconds and 14 microseconds
memoization : 0 seconds and 17 microseconds
tabulation : 0 seconds and 17 microseconds

For n =100
recursion : 0 seconds and 1300 microseconds
memoization : 0 seconds and 1270 microseconds
tabulation : 0 seconds and 35 microseconds

我无法理解的是,这就是为什么对于稍大的输入,记忆化所花费的时间几乎与简单的递归解决方案相同?在这种情况下,内存是否应该比制表花费更少的时间,因为它跳过了矩阵中许多无用的计算,而这些计算在制表的情况下必须完成?

最佳答案

这是因为在内存算法的递归步骤中,您实际上是递归到 Coins_rec 而不是 Coins_memoization 。一旦你解决了这个问题,你会发现对于更大的数字来说,内存确实和制表一样快。例如,以下是我的机器上 100 的结果:

100
recursion : 0 seconds and 914 microseconds
memoization : 0 seconds and 18 microseconds
tabulation : 0 seconds and 16 microseconds

关于c++ - 为什么内存比制表花费更多时间?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32426526/

相关文章:

C++ 二维字符数组到字符串

recursion - Elixir尾调用递归函数

Java - 通过Arraylist进行递归二分搜索

recursion - 字符串缩减-编程竞赛。需要解决方案

recursion - 用动态规划编写递归算法

c++ - 需要有关函数别名的帮助

C++ Variadic Vector 运算符实现

types - 如何在 OCaml 中定义相互引用的两种类型?

java - 最长递增子序列的长度

c++ - 我可以在 HWND 上设置标签吗?