我正在编写一个编码项目,我对程序的速度有问题。该程序输入1到80之间的输入,此输入表示许多匹配杆,并输出可以使用该数量的匹配杆制作多少个不同的数字。
Ex:数字1可以用2根火柴棍形成,数字2需要5根火柴棒
以下是该计划的完整提示:
以下是我提出的用于计算所有可能输出的算法的代码,它对于输入的低端功能相当好,尽管对于大输入(如80小时计算所有输入)而言效率非常低可能性。如何将这个时间减少到最低限度?
n代表输入,所有Counter对象都跟踪创建的每个可能的数字
public static void digitCounter(int n, Counter count) {
if (n < 2) {
//ouput
} else {
if (count.getCount() == 0) {
// If there are enough match sticks to form it
// accounts for 0 only once
if (n >= 7) {
// counts 0
count.setCount(10);
// counts 1
digitCounter(n - 2, count);
// counts 2
digitCounter(n - 5, count);
// counts 3
digitCounter(n - 5, count);
// count 4
digitCounter(n - 4, count);
// counts 5
digitCounter(n - 5, count);
// counts 6
digitCounter(n - 6, count);
// counts 7
digitCounter(n - 3, count);
// counts 8
digitCounter(n - 7, count);
// counts 9
digitCounter(n - 5, count);
} else if (n == 6) {
count.setCount(9);
digitCounter(n - 2, count);
digitCounter(n - 5, count);
digitCounter(n - 5, count);
digitCounter(n - 4, count);
digitCounter(n - 5, count);
digitCounter(n - 6, count);
digitCounter(n - 3, count);
digitCounter(n - 5, count);
} else if (n == 5) {
count.setCount(7);
digitCounter(n - 2, count);
digitCounter(n - 5, count);
digitCounter(n - 5, count);
digitCounter(n - 4, count);
digitCounter(n - 5, count);
digitCounter(n - 3, count);
digitCounter(n - 5, count);
} else if (n == 4) {
count.setCount(3);
digitCounter(n - 2, count);
digitCounter(n - 4, count);
digitCounter(n - 3, count);
} else if (n == 3) {
count.setCount(2);
digitCounter(n - 2, count);
digitCounter(n - 3, count);
} else if (n == 2) {
count.setCount(1);
digitCounter(n - 2, count);
}
}
// Accounts for every other number after 0 is accounted for so
// numbers with leading 0's are not formed
// Ex: 001 is illegal
else {
if (n >= 7) {
count.setCount(count.getCount() + 10);
digitCounter(n - 6, count);
digitCounter(n - 2, count);
digitCounter(n - 5, count);
digitCounter(n - 5, count);
digitCounter(n - 4, count);
digitCounter(n - 5, count);
digitCounter(n - 6, count);
digitCounter(n - 3, count);
digitCounter(n - 7, count);
digitCounter(n - 5, count);
} else if (n == 6) {
count.setCount(count.getCount() + 9);
digitCounter(n - 6, count);
digitCounter(n - 2, count);
digitCounter(n - 5, count);
digitCounter(n - 5, count);
digitCounter(n - 4, count);
digitCounter(n - 5, count);
digitCounter(n - 6, count);
digitCounter(n - 3, count);
digitCounter(n - 5, count);
} else if (n == 5) {
count.setCount(count.getCount() + 7);
digitCounter(n - 2, count);
digitCounter(n - 5, count);
digitCounter(n - 5, count);
digitCounter(n - 4, count);
digitCounter(n - 5, count);
digitCounter(n - 3, count);
digitCounter(n - 5, count);
} else if (n == 4) {
count.setCount(count.getCount() + 3);
digitCounter(n - 2, count);
digitCounter(n - 4, count);
digitCounter(n - 3, count);
} else if (n == 3) {
count.setCount(count.getCount() + 2);
digitCounter(n - 2, count);
digitCounter(n - 3, count);
} else if (n == 2) {
count.setCount(count.getCount() + 1);
digitCounter(n - 2, count);
}
}
}
答案 0 :(得分:0)
如果您修改了设计,以便digitCounter(n)
返回可以由n
牙签形成的数字计数,然后将该值缓存在持久性地图中,该怎么办?在进入digitCounter
时检查你的地图并返回缓存的值,如果你已经计算过一次..那么你仍然会有一个递归算法,但它不需要对相同的N进行重复调用。
答案 1 :(得分:0)
您可以将获得的解决方案数量乘以给定数量的匹配项。您还可以将解决方案的数量缓存到给定数字。
public static long solutionsFor(int matchCount) {
long[] counts = new long[matchCount+1];
for(int i = 0; i <= matchCount; i++) {
long count = ... calculate count based on previous values ...
counts[i] = count;
}
return counts[matchCount];
}
性能提升是成本为O(n),对于返回long
的最大问题,您可能会在几毫秒内得到答案。
答案 2 :(得分:0)
您的问题是digitCounter
的一次通话最多可以产生10次digitCounter
来电,这可能会产生更多的电话。因此,您首次尝试优化将减少呼叫次数。
@pamphlet问:
你的许多案例多次调用digitCounter(n-5,count)。做 你希望结果在第二,第三或第四次不同 你叫它吗?
这应该是您优化的第一条线索。每次拨打digitCounter(n-5, count)
时,您添加到计数器的实际计数应该相同。所以我建议你让数字计数器直接返回计数并自己添加。要表示是否计数0,请添加一个标志,指示它是否是对digitCounter
的最顶层调用。
使用n = 10调用digitCounter
。
对于count=0
,您将在以下代码中结束(删除评论)
if (n >= 7) {
count.setCount(10);
digitCounter(n - 2, count);
digitCounter(n - 5, count);
digitCounter(n - 5, count);
digitCounter(n - 4, count);
digitCounter(n - 5, count);
digitCounter(n - 6, count);
digitCounter(n - 3, count);
digitCounter(n - 7, count);
digitCounter(n - 5, count);
} else if (n == 6) {
你有效的电话
1x digitCounter(n - 2, count);
1x digitCounter(n - 3, count);
1x digitCounter(n - 4, count);
4x digitCounter(n - 5, count);
1x digitCounter(n - 6, count);
1x digitCounter(n - 7, count);
调用digitCounter(n - 5, count);
digitCounter(5, count);
n = 10会导致对digitCounter
的额外10次调用。因此,通过调用一次而不是4次,您可以节省3x(1 + 10)= 33次呼叫。
另一项优化是保存您最后一次通话。你知道digitCounter(0)
的结果是0.那你为什么要尝试计算呢?
} else if (n == 2) {
count.setCount(count.getCount() + 1);
digitCounter(n - 2, count);
}
可以改写为
} else if (n == 2) {
count.setCount(count.getCount() + 1);
digitCounter(2 - 2, count);
}
因此您可以删除通话digitCounter(2 - 2, count);
,因为它不会影响您的结果。同样,digitCounter(1)
的结果为0.因此,在优化案例n=3
后,我们删除了2个不必要的调用。 digitCounter(2)
始终为1.因此,对于n=4
,您可以删除3个不必要的电话,只需将计数器增加4而不是3。
你也可以用同样的方式优化n=5
。 digitCounter(3)
始终为2.因此,您可以消除7个额外的呼叫,并将计数器增加10而不是7。我让您自己优化案例n=6
。
这应该已经导致递归调用的大幅减少。它可能看起来很小,但它加起来很快。
下一个优化将是@JVMATL建议的缓存,您可以为大N获得大量节省。digitCounter(n-2)
的调用将导致调用digitCounter((n-2) -2)
,相当于digitCounter(n-4)
。因此,您无需再次计算该值,从而为大n(例如n=80
)节省了大量资金。即使缓存可能对小n没有帮助,甚至可能会增加运行时间(无论如何都要关心毫秒),你对大n有很大的好处(很容易达到秒/分钟)。