我正在尝试解决一个问题,在这个问题中,您必须计算给定特定参数可以生成的条形码数量。我递归地解决了这个问题,每次都能得到正确的答案。但是,我的程序非常慢。我尝试使用我读到的关于称为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我对递归搜索的记忆太慢了。救命啊!
答案 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个条目保存在地图中。