记忆递归搜索

时间:2015-03-03 02:55:08

标签: java recursion memoization

我正在尝试解决一个问题,在这个问题中,您必须计算给定特定参数可以生成的条形码数量。我递归地解决了这个问题,每次都能得到正确的答案。但是,我的程序非常慢。我尝试使用我读到的关于称为memoization的技术来纠正这个问题,但是当给定某些输入时我的程序仍然会爬行(例如:10,10,10)。这是java中的代码。

有没有人知道我在这里做错了什么?

import java.util.Scanner;

//f(n, k, m) = sum (1 .. m) f(n - i, k - 1, m)

public class BarCode { public static int[][] memo;

public static int count(int units, int bars, int width) {
    int sum = 0;
    if (units >= 0 && memo[units][bars] != -1) //if the value has already been calculated return that value
            return memo[units][bars];

    for (int i = 1; i <= width; ++i) {
        if (units == 0 && bars == 0)
            return 1;
        else if (bars == 0)
            return 0;
        else {
            sum += count(units - i, bars - 1, width);
        }
    }
    if (units > -1)
        memo[units][bars] = sum;

    return sum;
}

public static void main(String[] args) {
    Scanner in = new Scanner(System.in);
    //while (in.hasNext()) {
        int num = in.nextInt();
        int bars = in.nextInt();
        int width = in.nextInt();
        memo = new int[51][51];
        for (int i = 0; i < memo.length; ++i) {
            for (int j = 0; j < memo.length; ++j)
                memo[i][j] = -1;
        }
        int sum = 0;
        sum += count(num, bars, width);
        System.out.println(sum);
    //}
    in.close();
}
}

TL:DR我对递归搜索的记忆太慢了。救命啊!

2 个答案:

答案 0 :(得分:1)

您的memoization实现看起来是有效的。它可能有所帮助,但这里真正的问题是你选择的算法。

从粗略检查您的代码开始,对您的count方法的调用平均会循环width次。每次循环时,再次调用count会使层更深。看起来它似乎会从第一层更深地循环bars层。如果我的渐近分析中几个scotch的指针是正确的,这将导致具有O(宽度^条)运行时复杂度的算法。当您增加输入参数(尤其是条形图)时,应用程序为了计算答案而需要采取的步骤量将大大增加(在条形图的情况下呈指数级增长)。

您的memoization将减少所需的重复计算次数,但是每个被记忆的值仍需要至少计算一次才能使memoization有所帮助。因此无论有没有记忆,你仍然处理非多项式时间复杂度,并且总是表现出糟糕的表现。

您可能需要考虑寻找更有效的方法。可能尝试使用组合学来尝试计算它,而不是试图计算条形码组合的数量。例如,我可以尝试找出小写字符串的数量(仅使用字符az)我可以通过生成所有字符串并计算其中有多少字符串来生成长度为n的字符串,但这将具有指数时间复杂,不会有效。另一方面,我知道基本的组合学告诉我,我可以创建的字符串数量的公式是26 ^ n(每个位置有26个选项,n个位置),计算机可以很容易地快速评估。

寻找类似的计算条形码数量的方法。

答案 1 :(得分:1)

您使用单位&lt;&lt; 单位排除count次来电的所有结果0 来自memoization:

if (units > -1)
    memo[units][bars] = sum;

这会导致对这些值进行大量不必要的count调用。

要包含所有情况,您可以使用带有从单位 bars 值生成的密钥的HashMap。我使用了从 units bars 生成的字符串,如下所示:

//f(n, k, m) = sum (1 .. m) f(n - i, k - 1, m)

public class BarCode {
    public static Map<String, Integer>  memo    = new HashMap<>();

    public static int count(int units, int bars, int width) {
        int sum = 0;

        final String key = units + " " + bars;
        Integer memoSum = memo.get(key);
        if (memoSum != null) {
           return memoSum.intValue();
        }

        for (int i = 1; i <= width; ++i) {
            if (units == 0 && bars == 0)
                return 1;
            else if (bars == 0)
                return 0;
            else {
                sum += count(units - i, bars - 1, width);
            }
        }

        memo.put(key, Integer.valueOf(sum));

        return sum;
    }

    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int num = in.nextInt();
        int bars = in.nextInt();
        int width = in.nextInt();
        memo = new HashMap<>();
        int sum = 0;
        sum += count(num, bars, width);
        System.out.println(sum);
        in.close();
    }
}

例如,这使得count的调用次数从600多万减少到4,150,输入值为“10 10 10”,其中415个条目保存在地图中。