递归函数g(n)

时间:2015-03-08 23:21:31

标签: java algorithm recursion

我有一个g(n)函数,可以由g(n)=f(n,n)给出。这是由

递归定义的
f(i, j) = 1/3
(f(i−1, j) + f(i−1, j −1) + f(i, j −1))

with f(0,0) = 0;  f(i,0) = 1,i > 0; f(0, j) = 1, j > 0

我编写了一个java程序来计算10-15的值。快速处理前几个值,但是在值结束时,程序变得非常慢并且需要很长时间来处理结果。我的代码有问题,还是只是一个冗长的计算?

public class javaapplication4 {
    private static double f(double i, double j) {
        if (i == 0.0 && j == 0.0) return 0.0;
        if (i == 0.0 || j == 0.0) return 1.0;
        return (f(i - 1, j) + f(i - 1, j - 1) + f(i, j - 1));
    }

    private static double g(double n) {
        return f(n, n);
    }

    public static void main (String[] args) {
        for (int n = 10; n < 16; n ++) {
            System.out.println("g(" + (int) n + "): " + g(n));
        }
    }
}

5 个答案:

答案 0 :(得分:6)

首先,您似乎忘记了此行中的1.0 / 3

  

return(f(i - 1,j)+ f(i - 1,j - 1)+ f(i,j - 1));

其次,你的程序很慢,因为多次计算相同的值。例如,f(i - 1, j)会调用f(i - 1, j - 1),也会从f(i, j)调用。{/ p>

要解决此问题,请在矩阵中计算递归关系:

 f(0,0) = 0; f(i,0) = 1,i > 0; f(0, j) = 1, j > 0
 for i = 1 to n:
   for j = 1 to n:
     f[i, j] = 0.33*(f[i - 1, j] + f[i, j - 1] + f[i - 1, j - 1])

或者保持递归实施,但使用memoization加快实施速度:

  

记忆功能“记住”与某些特定输入相对应的结果。使用记忆输入的后续调用将返回记住的结果而不是重新计算结果,从而消除了除了使用这些参数对函数进行的第一次调用之外的所有参数的调用的主要成本。

基本上,您仍然可以使用矩阵来存储结果,并执行以下操作:

private static double f(double i, double j) { // make i and j ints, they do not need to be doubles here.
    if (i == 0.0 && j == 0.0) return 0.0;
    if (i == 0.0 || j == 0.0) return 1.0;
    if (storageMatrix[i, j] != -1) {
      return storageMatrix[i, j];
    }

    storageMatrix[i, j] = (1.0 / 3) * (f(i - 1, j) + f(i - 1, j - 1) + f(i, j - 1));
    return storageMatrix[i, j];
}

您可以通过注意到如果您实现迭代解决方案我为上面提供了伪代码,您可以进一步优化事物,您只使用矩阵的当前行和前一行。因此,您可以使用两个长度为n的数组而不是方形n x n矩阵来计算您的函数。

答案 1 :(得分:1)

通过记忆很容易加快。不要丢掉你努力计算的价值。

这是我的输出:

"C:\Program Files\Java\jdk1.7.0_45\bin\java" -Didea.launcher.port=7534 "-Didea.launcher.bin.path=C:\Program Files (x86)\JetBrains\IntelliJ IDEA 14.0.1\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\Java\jdk1.7.0_45\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.7.0_45\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.7.0_45\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.7.0_45\jre\lib\jce.jar;C:\Program Files\Java\jdk1.7.0_45\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.7.0_45\jre\lib\jfxrt.jar;C:\Program Files\Java\jdk1.7.0_45\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.7.0_45\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.7.0_45\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.7.0_45\jre\lib\resources.jar;C:\Program Files\Java\jdk1.7.0_45\jre\lib\rt.jar;C:\Program Files\Java\jdk1.7.0_45\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.7.0_45\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.7.0_45\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.7.0_45\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.7.0_45\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.7.0_45\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.7.0_45\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.7.0_45\jre\lib\ext\zipfs.jar;F:\Projects\Java\stack-overflow\out\production\stack-overflow;F:\Projects\Java\stack-overflow\lib\jxl.jar;F:\Projects\Java\stack-overflow\lib\jdom.jar;F:\Projects\Java\stack-overflow\lib\Jama-1.0.2.jar;F:\Projects\Java\stack-overflow\lib\dom4j-1.6.1.jar;F:\Projects\Java\stack-overflow\lib\commons-io-2.3.jar;F:\Projects\Java\stack-overflow\lib\aopalliance-1.0.jar;F:\Projects\Java\stack-overflow\lib\sqlitejdbc-v056.jar;F:\Projects\Java\stack-overflow\lib\jackson-xc-1.6.2.jar;F:\Projects\Java\stack-overflow\lib\commons-lang3-3.1.jar;F:\Projects\Java\stack-overflow\lib\commons-math3-3.3.jar;F:\Projects\Java\stack-overflow\lib\jackson-all-1.6.2.jar;F:\Projects\Java\stack-overflow\lib\StackWrap4J-1.0.1.jar;F:\Projects\Java\stack-overflow\lib\jackson-jaxrs-1.6.2.jar;F:\Projects\Java\stack-overflow\lib\jackson-smile-1.6.2.jar;F:\Projects\Java\stack-overflow\lib\jackson-mrbean-1.6.2.jar;F:\Projects\Java\stack-overflow\lib\jackson-core-asl-1.6.2.jar;F:\Projects\Java\stack-overflow\lib\jackson-core-lgpl-1.6.2.jar;F:\Projects\Java\stack-overflow\lib\jackson-mapper-asl-1.6.2.jar;F:\Projects\Java\stack-overflow\lib\postgresql-8.1-405.jdbc3.jar;F:\Projects\Java\stack-overflow\lib\jackson-mapper-lgpl-1.6.2.jar;F:\Projects\Java\stack-overflow\lib\mysql-connector-java-5.1.18-bin.jar;F:\Projects\Java\stack-overflow\lib\org.springframework.aop-3.1.0.RC2.jar;F:\Projects\Java\stack-overflow\lib\org.springframework.asm-3.1.0.RC2.jar;F:\Projects\Java\stack-overflow\lib\org.springframework.jms-3.1.0.RC2.jar;F:\Projects\Java\stack-overflow\lib\org.springframework.orm-3.1.0.RC2.jar;F:\Projects\Java\stack-overflow\lib\org.springframework.oxm-3.1.0.RC2.jar;F:\Projects\Java\stack-overflow\lib\org.springframework.web-3.1.0.RC2.jar;F:\Projects\Java\stack-overflow\lib\org.springframework.core-3.1.0.RC2.jar;F:\Projects\Java\stack-overflow\lib\org.springframework.jdbc-3.1.0.RC2.jar;F:\Projects\Java\stack-overflow\lib\org.springframework.beans-3.1.0.RC2.jar;F:\Projects\Java\stack-overflow\lib\org.springframework.aspects-3.1.0.RC2.jar;F:\Projects\Java\stack-overflow\lib\org.springframework.context-3.1.0.RC2.jar;F:\Projects\Java\stack-overflow\lib\org.springframework.expression-3.1.0.RC2.jar;F:\Projects\Java\stack-overflow\lib\org.springframework.instrument-3.1.0.RC2.jar;F:\Projects\Java\stack-overflow\lib\org.springframework.web.struts-3.1.0.RC2.jar;F:\Projects\Java\stack-overflow\lib\org.springframework.transaction-3.1.0.RC2.jar;F:\Projects\Java\stack-overflow\lib\org.springframework.web.portlet-3.1.0.RC2.jar;F:\Projects\Java\stack-overflow\lib\org.springframework.web.servlet-3.1.0.RC2.jar;F:\Projects\Java\stack-overflow\lib\org.springframework.context.support-3.1.0.RC2.jar;F:\Projects\Java\stack-overflow\lib\org.springframework.instrument.tomcat-3.1.0.RC2.jar;F:\Projects\Java\stack-overflow\test-lib\junit-4.10.jar;F:\Projects\Java\stack-overflow\test-lib\org.springframework.test-3.1.0.RC2.jar;C:\Program Files (x86)\JetBrains\IntelliJ IDEA 14.0.1\lib\idea_rt.jar" com.intellij.rt.execution.application.AppMain cruft.RecursionExample
n: 0 g(n): 0.00000015.5 + 
n: 1 g(n): 0.66666715.5 + 
n: 2 g(n): 0.81481515.5 + 
n: 3 g(n): 0.86419815.5 + 
n: 4 g(n): 0.88797415.5 + 
n: 5 g(n): 0.90240315.5 + 
n: 6 g(n): 0.91236115.5 + 
n: 7 g(n): 0.91977515.5 + 
n: 8 g(n): 0.92557415.5 + 
n: 9 g(n): 0.93027315.5 + 
n: 10 g(n): 0.93418015.5 + 
n: 11 g(n): 0.93749715.5 + 
n: 12 g(n): 0.94035715.5 + 
n: 13 g(n): 0.94285715.5 + 
n: 14 g(n): 0.94506715.5 + 
n: 15 g(n): 0.94703915.5 + 
total wall time:         11 ms

Process finished with exit code 0

对我来说,在11毫秒内运行。

package cruft;

import java.util.HashMap;
import java.util.Map;

/**
 * RecursionExample description here
 * @author Michael
 * @link  https://stackoverflow.com/questions/28933093/recursive-function-gn
 * @since 3/8/2015 7:28 PM
 */
public class RecursionExample {

    private static final Map<String, Double> memo = new HashMap<String, Double>();


    private static double f(int  i, int j) {
        if (i < 0) throw new IllegalArgumentException("i cannot be negative");
        if (j < 0) throw new IllegalArgumentException("j cannot be negative");
        if (i == 0 && j == 0) return 0.0;
        if (i == 0 || j == 0) return 1.0;
        String key = Integer.toString(i) + "~" + Integer.toString(j);
        if (memo.containsKey(key)) {
            return memo.get(key);
        } else {
            double value = (f(i-1, j) + f(i-1, j-1) + f(i, j-1))/3.0;
            memo.put(key, value);
            return value;
        }
    }

    private static double g(int n) {
        return f(n, n);
    }

    public static void main (String[] args)
    {
        long begTime = System.currentTimeMillis();
        try {
            for (int n = 0; n < 16; n ++) {
                System.out.println(String.format("n: %d g(n): %f15.5 + ", n, g(n)));
            }
        } finally {
            long endTime = System.currentTimeMillis();
            System.out.println(String.format("total wall time: %10d ms", (endTime-begTime)));
        }
    }
}

答案 2 :(得分:1)

您忘记将结果除以3,否则您的代码就可以了。您可以通过添加保存以前结果的数组来加速任意数量的值,我这样做:

public class Recursivity {

static double[][] results = new double[20][20];

private static double f(int i, int j) {
    if (i == 0 && j == 0) {
        return 0.0;
    }
    if (i == 0 || j == 0) {
        return 1.0;
    }
    if (results[i][j] != -1.0) {
        return (results[i][j]);

    }
    double output = (f(i - 1, j) + (f(i - 1, j - 1) + (f(i, j - 1))));
    results[i][j] = output;
    return output;

}

private static double g(int n) {
    return f(n, n);
}

public static void main(String[] args) {

    for (int i = 0; i < results.length; i++) {
        for (int j = 0; j < results.length; j++) {
            results[i][j] = -1.0;
        }
    }

    for (int n = 10; n < 16; n++) {

        System.out.println("g(" + (int) n + "): " + g(n));

    }
}

}

运行速度要快得多,但它要求你将整数作为f的输入。我不知道是否有任何类型的列表可以通过非离散数字进行索引,但对于有限数量的数字应该是可能的。

答案 3 :(得分:0)

为了补充上面的答案,假设系统可以在时间t计算f(k,k)。为了计算f(k + 1,k + 1),它必须计算f(k,k + 1),f(k,k)和f(k + 1,k)。这将花费大约3倍的时间。换句话说,对于任何给定的k,计算f(k + 1,k + 1)的时间是f(k,k)的三倍。这快速失控:计算f(15,15)将是计算f(10,10)的243倍!

答案 4 :(得分:-1)

你的代码很好,但你忘记在这里添加1/3;

    return (f(i - 1, j) + f(i - 1, j - 1) + f(i, j - 1));

所以它应该是这样的;

    return (f(i - 1, j)*0.33 + f(i - 1, j - 1) + f(i, j - 1));

或         return(f(i - 1,j)*(1/3)+ f(i - 1,j - 1)+ f(i,j - 1));