如何在Java中优化这两个相互递归的函数?

时间:2019-04-28 14:18:54

标签: java recursion math

我必须编写一些方法来求和并提供这些复合的相互递归函数的输出列表,但是它们的执行会使我的当前实现超时:

public static long fAnn(long n) {
      if (n == 0) return 1;
      else return n - fJohn(fAnn(n-1));
    }

    public static long fJohn(long n) {
      if (n <= 0) return 0;
      else return n - fAnn(fJohn(n-1));
    }
public static List<Long> john(long n) {
    List<Long> res = new ArrayList<Long>();

    for (long i = 0; i < n; i++) {
      res.add(fJohn(i));
    }

    return res;       
}

public static long sumJohn(long n) {
    long sum = 0;
    for (long i = 0; i < n; i++) sum += fJohn(i);
    return sum;
}
public static long sumAnn(long n) {
    long sum = 0;
    for (long i = 0; i < n; i++) sum += fAnn(i);
    return sum;
}

我已经考虑过将函数的最后一个值传递回该函数,但是我不确定如何做到这一点。

2 个答案:

答案 0 :(得分:0)

此解决方案的问题是执行具有相同参数的方法的次数很多。可以使用名为memoization的优化技术来代替。

来自维基百科:

  

在计算中,记忆或记忆是一种优化技术,主要用于通过存储昂贵的函数调用的结果并在再次出现相同的输入时返回缓存的结果来加速计算机程序。

一个实现可能看起来像这样。请注意,这一次您需要创建一个类实例来使用这些方法,因为它们不再是静态的。

private Map<Long, Long> annCache = new HashMap<>();
private Map<Long, Long> johnCache = new HashMap<>();

long fAnn2(long n) {
    return annCache.computeIfAbsent(n, x -> {
        if (x == 0) return 1L;
        else return x - fJohn2(fAnn2(x - 1));
    });
}

long fJohn2(long n) {
    return johnCache.computeIfAbsent(n, x -> {
        if (x <= 0) return 0L;
        else return x - fAnn2(fJohn2(x - 1));
    });
}

答案 1 :(得分:0)

我接受了@Tim Beigeleisen的建议,并决定学习更多有关动态编程的知识,以改进我对该函数所采用的幼稚递归方法,而不是先在这里寻找答案。

这是我想出的代码:

import java.util.List;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;


public class Johnann {
    private static Map<Long, Long> jMem;
    private static Map<Long, Long> aMem;


    public static long fAnn(long n) {
      if (n < 2) {
        aMem = new HashMap<Long, Long>();
        aMem.put(n, (long)1);
        return 1;
      }
      else if (aMem.keySet().contains(n)) {
        return aMem.get(n);
      }
      else {
        long res = n - fJohn(fAnn(n-1));
        aMem.put(n, res);
        return res;
      }
    }


    public static long fJohn(long n) {
      if (n < 2) {
        jMem = new HashMap<Long, Long>();
        jMem.put(n, (long)0);
        return 0;
      }
      else if (jMem.keySet().contains(n)) {
        return jMem.get(n);
      }
      else {
        long res = n - fAnn(fJohn(n-1));
        jMem.put(n, res);
        return res;
      }
    }

    public static List<Long> john(long n) {
        List<Long> res = new ArrayList<Long>();

        for (long i = 0; i < n; i++) {
          res.add(fJohn(i));
        }

        return res;
    }

    public static List<Long> ann(long n) {
        System.out.println(n);
        List<Long> res = new ArrayList<Long>();

        for (long i = 0; i < n; i++) {
          res.add(fAnn(i));
        }

        return res;
    }


    public static long sumJohn(long n) {
        if (n == 0) return 0;
        else if (n < 2) return 1;
        long sum = 0;
        for (long i = 0; i < n; i++) sum += fJohn(i);
        return sum;
    }
    public static long sumAnn(long n) {
        if (n == 0) return 0;
        else if (n < 2) return 1;
        long sum = 0;
        for (long i = 0; i < n; i++) sum += fAnn(i);
        return sum;
    }
}

例如,我看到了其他更好的使用ArrayList而不是映射的实现:

import java.util.*;

public class Johnann {

    private enum Person {JOHN, ANN}

    private static List<Long> getListForName(Person person, long n) {
        List<Long> ann = new ArrayList<>(Arrays.asList(1L));
        List<Long> john = new ArrayList<>(Arrays.asList(0L));

        for (int dayAnn = 1, dayJohn = 1; dayAnn < n || dayJohn < n; ) {
            if (john.size() > ann.get(dayAnn - 1)) {
                ann.add(dayAnn - john.get(Math.toIntExact(ann.get(dayAnn - 1))));
                dayAnn++;
            }
            if (ann.size() > john.get(dayJohn - 1)) {
                john.add(dayJohn - ann.get(Math.toIntExact(john.get(dayJohn - 1))));
                dayJohn++;
            }
        }
        return (person == Person.JOHN ? john : ann).subList(0, Math.toIntExact(n));
    }

    public static List<Long> john(long n) {
        return getListForName(Person.JOHN, n);
    }

    public static List<Long> ann(long n) {
        return getListForName(Person.ANN, n);
    }

    public static long sumJohn(long n) {
        return john(n).stream().mapToLong(Long::longValue).sum();
    }

    public static long sumAnn(long n) {
        return ann(n).stream().mapToLong(Long::longValue).sum();
    }
}

这可能更快一些,但是我很高兴我从该问题中学到了很多有关动态编程和优化递归调用的知识。