首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >递归算法效率- Java

递归算法效率- Java
EN

Stack Overflow用户
提问于 2014-01-23 23:55:48
回答 3查看 194关注 0票数 1

我正在做一个编码项目,我对程序的速度有一个问题。该程序接受1到80之间的输入,该输入表示火柴杆的数量,并输出该火柴杆的数量可以构成多少个不同的数字。

例:数字1可以由2根火柴棒组成,数字2需要5根火柴棒

下面是程序的完整提示符:

这是我想出的计算所有可能输出的算法的代码,它对于输入的低端运行得相当好,尽管对于像80这样的大输入来说效率很低,需要花费几个小时来计算所有的可能性。我怎样才能把这个时间减少到最少呢?

N表示输入,Counter对象所做的全部工作就是跟踪创建的每个可能的数字

代码语言:javascript
复制
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);
            }
        }
    }
EN

回答 3

Stack Overflow用户

发布于 2014-01-24 00:10:45

如果您修改了设计,让digitCounter(n)返回n牙签可能形成的数字计数,然后将该值缓存到一个持久映射中,会怎么样?在进入digitCounter时,如果您已经计算过一次,请检查地图并返回缓存值。那么你仍然会有一个递归算法,但它不需要对相同的N进行重复的调用。

票数 0
EN

Stack Overflow用户

发布于 2014-01-24 01:05:42

你可以用给定的匹配数乘以你得到的解的数目。您还可以缓存解决方案的数量,直到给定的数量。

代码语言:javascript
复制
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的最大问题,您可能会在几毫秒内得到答案。

票数 0
EN

Stack Overflow用户

发布于 2014-01-24 01:33:18

你的问题是,一次digitCounter调用可能会产生多达10个对digitCounter的调用,这可能会产生更多对它的调用。因此,您首先要尝试优化的是减少关闭呼叫的数量。

@宣传册问:

在许多情况下,

会多次调用digitCounter(n-5,计数)。你希望第二次、第三次或第四次调用时结果会有所不同吗?

这应该是你要优化的第一条线索。每次调用digitCounter(n-5, count)时,添加到计数器的实际计数应该相同。所以我建议你让digit counter直接返回计数,然后自己加法。要表示是否计数为0,请添加一个标志,指示它是否是对digitCounter的最顶层调用。

使用n=10调用digitCounter

对于count=0,您将在以下代码中结束(删除注释)

代码语言:javascript
复制
        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);

4倍digitCounter(n - 5, count);

1x digitCounter(n - 6, count);

1x digitCounter(n - 7, count);

调用n= 10时为digitCounter(5, count);digitCounter(n - 5, count);会导致对digitCounter的额外10次调用。因此,通过调用它一次而不是4次,您可以节省3x(1+10)=33次调用。

另一个优化是保存您执行的最后一个调用。您知道digitCounter(0)的结果是0。那么,为什么要尝试计算它呢?

代码语言:javascript
复制
        } else if (n == 2) {
            count.setCount(count.getCount() + 1);
            digitCounter(n - 2, count);
        }

可以重写为

代码语言:javascript
复制
        } else if (n == 2) {
            count.setCount(count.getCount() + 1);
            digitCounter(2 - 2, count);
        }

因此您可以删除调用digitCounter(2 - 2, count);,因为它不会影响您的结果。同样,digitCounter(1)的结果为0。因此,在优化了case n=3之后,我们又删除了2个不必要的调用。digitCounter(2)总是1。所以对于n=4,你可以删除3个不必要的调用,只需将计数器增加4而不是3。

您也可以用同样的方法优化n=5digitCounter(3)始终为2。因此,您可以消除7个额外的调用,并将计数器增加10,而不是仅增加7。我让您自己优化case n=6

这应该已经大大减少了递归调用。它可能看起来很小,但它很快就会积累起来。

下一个优化是缓存,就像@JVMATL建议的那样,你可以节省大量的大N。对digitCounter(n-2)的调用将导致对digitCounter((n-2) -2)的调用,这相当于digitCounter(n-4)。因此,您不需要再次计算该值,这将为大n(如n=80)带来巨大的节省。即使缓存可能对小n没有帮助,甚至可能增加运行时间(不管怎么说,谁关心毫秒),但是大n(轻松达到秒/分钟)会有很大的收益。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/21313177

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档