如何记忆递归Java方法?

时间:2017-02-18 07:02:32

标签: java recursion dynamic-programming memoization

所以我已经建立了这个程序来构建不同的楼梯案例。基本上问题是:给定一个整数N,你可以建立多少种不同的方法来构建楼梯。 N保证大于3且小于200.任何前一步都不能大于其后续步骤,否则会破坏楼梯的目的。

因此给出N = 3 你可以建造一个楼梯:2个步骤,然后是1个步骤

给定N = 4 你可以建造一个楼梯:3个步骤,然后是1个步骤

给定N = 5 您可以建造两个楼梯:3个步骤然后是2个步骤或4个步骤然后是1个步骤。

我的方法如下,它的工作原理,除了它的运行时太慢了。所以我在考虑尝试为该方法做一个memoization,但说实话,我并不完全理解如何实现它。如果我能得到一些帮助,那就是很棒。

Uncaught SyntaxError: http://localhost/h/js/test.js: Unexpected token (1:28)
> 1 | componentWillMount: function() {
    |                             ^
  2 |           this.firebaseRef = firebase.database().ref("items");
  3 |           this.firebaseRef.on("child_added", function(dataSnapshot) {
  4 |               this.items.push(dataSnapshot.val());

2 个答案:

答案 0 :(得分:2)

<强>概述

所以你在这里有一个递归的解决方案。这适用于此类问题。在这个特定的递归解决方案中,您的递归步骤将使用相同的参数多次调用。

一种非常常见的递归解决方案的优化模式是动态编程,其中多次进行相同的计算。我们的想法是,我们不是多次进行相同的计算,而是在第一次执行时对每个计算进行缓存。然后每隔一次,如果我们需要计算完全相同的值,我们就可以从缓存中读取结果。

<强>解决方案

考虑到这一点,这个解决方案应该有效。它使用与原始版本完全相同的逻辑,它只是缓存HashMap中递归步骤的所有结果,因此它永远不需要计算两次相同的事情。它还使用Staircase对象来跟踪(砖块,高度)对。这是因为我们无法将对插入HashMap,我们只能插入单个对象。

只需将变量bricks更改为您想要解决的任何值。

public class Staircase {

    private static HashMap<Staircase, Integer> cache;

    public static void main(String[] args) {
        cache = new HashMap<>();
        int bricks = 6;
        Staircase toBuild = new Staircase(1, bricks);
        System.out.println(toBuild.waysToBuild() - 1);
    }

    public final int height;
    public final int bricksLeft;

    public Staircase(int height, int bricksLeft) {
        this.height = height;
        this.bricksLeft = bricksLeft;
    }

    public int waysToBuild() {
        if (cache.containsKey(this)) {
            return cache.get(this);
        }

        int toReturn;
        if (bricksLeft == 0) {
            toReturn = 1;
        } else if (bricksLeft < height) {
            toReturn = 0;
        } else {
            Staircase component1 = new Staircase(height + 1, bricksLeft - height);
            Staircase component2 = new Staircase(height + 1, bricksLeft);
            toReturn = component1.waysToBuild() + component2.waysToBuild();
        }

        cache.put(this, toReturn);
        return toReturn;
    }

    @Override
    public boolean equals(Object other) {
        if (other instanceof Staircase) {
            if (height != ((Staircase) other).height) {
                return false;
            }
            if (bricksLeft != ((Staircase) other).bricksLeft) {
                return false;
            }
            return true;
        }
        return false;
    }

    @Override
    public int hashCode() {
        int hash = 5;
        hash = 73 * hash + this.height;
        hash = 73 * hash + this.bricksLeft;
        return hash;
    }
}

<强>分析

我测试了它,性能比以前的版本快得多。它可以立即计算出最多200的值。

您的原始功能是O(2^n)。这是因为我们为1n的每个值进行了2次递归调用,因此每次n递增时调用的总次数会加倍。

动态规划解决方案是O(n),因为对于n的每个值,它最多需要计算一次从n砖中制作一个阶梯的方法数。

补充阅读

以下是有关动态编程的更多内容:https://en.wikipedia.org/wiki/Dynamic_programming

答案 1 :(得分:1)

使用小班来保持对(身高,砖块),比如说:

private static class Stairs {
    private int height;
    private int bricks;
    Stairs(int height, int bricks) {
        this.height = height; this.bricks = bricks;
    }
}

然后使用HashMap<Stairs, Integer>中初始化的全局main()

map = new HashMap<Stairs, Integer>();

bricks()函数中,检查特定(高度,砖块)对的解是否在地图中。如果是,只需通过调用get()方法从地图返回即可。否则,进行计算:

Stairs stairsObj = new Stairs(height, bricks);
if(map.get(stairsObj) == null) {
    // Put your compute code here
}

在函数中的每个return语句之前,添加两个附加语句。类似的东西:

int result = <whatever you are returning right now>;
map.put(stairsObj, result);
return result;