简体   繁体   English

迭代和递归解决方案的时间复杂度

[英]Time complexity on iterative and recursive solution

I'm trying to solve the following problem: 我正在尝试解决以下问题: 编程问题

I feel like I've given it a lot of thoughts and tried a lot of stuff. 我觉得我已经给了很多想法,尝试了很多东西。 I manage to solve it, and produce correct values but the problem is that it isn't time efficient enough. 我设法解决了这个问题,并产生了正确的值,但是问题是时间效率不够高。 It completes 2 out of the Kattis tests and fails on the 3 because of the time limit 1 second was exceeded. 它完成了Kattis测试中的2个,但由于超过了1秒的时间限制而未能完成3个测试。 There is noway for me to see what the input was that they tested with I'm afraid. 恐怕我没有办法看到他们测试的输入是什么。

I started out with a recursive solution and finished that. 我从一个递归解决方案开始,并完成了这一过程。 But then I realised that it wasn't time efficient enough so I instead tried to switch to an iterative solution. 但是后来我意识到这不够省时,因此我尝试改用迭代解决方案。

I start with reading input and add those to an ArrayList. 我从读取输入开始,然后将其添加到ArrayList中。 And then I call the following method with target as 1000. 然后,我将目标为1000的以下方法调用。

public static int getCorrectWeight(List<Integer> platesArr, int target) {
    /* Creates two lists, one for storing completed values after each iteration,
    one for storing new values during iteration. */
    List<Integer> vals = new ArrayList<>();
    List<Integer> newVals = new ArrayList<>();

    // Inserts 0 as a first value so that we can start the first iteration.
    int best = 0;
    vals.add(best);

    for(int i=0; i < platesArr.size(); i++) {
        for(int j=0; j < vals.size(); j++) {
            int newVal = vals.get(j) + platesArr.get(i);
            if (newVal <= target) {
                newVals.add(newVal);
                if (newVal > best) {
                    best = newVal;
                }
            } else if ((Math.abs(target-newVal) < Math.abs(target-best)) || (Math.abs(target-newVal) == Math.abs(target-best) && newVal > best)) {
                best = newVal;
            }
        }
        vals.addAll(newVals);
    }
    return best;
}

My question is, is there some way that I can reduce the time complexity on this one for large number of data? 我的问题是,对于减少大量数据,我是否可以通过某种方法来减少时间复杂度?

The main problem is that the size of vals and newVals can grow very quickly, as each iteration can double their size. 主要的问题是大小valsnewVals可以非常快速地增长,因为每个迭代可以增加一倍它们的大小。 You only need to store 1000 or so values which should be manageable. 您只需要存储1000个左右的值即可管理。 You're limiting the values but because they're stored in an ArrayList , it ends up with a lot of duplicate values. 您在限制值,但是因为它们存储在ArrayList ,所以最终会有很多重复的值。

If instead, you used a HashSet , then it should help the efficiency a lot. 相反,如果您使用了HashSet ,那么它将HashSet提高效率。

You only need to store a DP table of size 2001 (0 to 2000) Let dp[i] represent if it is possible to form i kg of weights. 您仅需要存储大小为2001(0到2000)的DP表,让dp[i]表示是否可以形成i kg的权重。 If the weight goes over the array bounds, ignore it. 如果权重超出数组范围,请忽略它。 For example: 例如:

dp[0] = 1;      
for (int i = 0; i < values.size();  i++){
    for (int j = 2000; j >= values[i]; j--){  
        dp[j] = max(dp[j],dp[j-values[i]);
    }
}

Here, values is where all the original weights are stored. 在这里, values是存储所有原始权重的位置。 All values of dp are to be set to 0 except for dp[0] . dp[0]外,所有dp值均应设置为0。

Then, check 1000 if it is possible to make it. 然后,检查是否有可能达到1000。 If not, check 999 and 1001 and so on. 如果不是,请检查999和1001,依此类推。 This should run in O(1000n + 2000) time, since n is at most 1000 this should run in time. 这应该以O(1000n + 2000)时间运行,因为n最多为1000,所以应该及时运行。

By the way, this is a modified knapsack algorithm, you might want to look up some other variants. 顺便说一句,这是一种改进的背包算法,您可能需要查找其他一些变体。

If you think too generally about this type of problem, you may think you have to check all possible combinations of input (each weight can be included or excluded), giving you 2 n combinations to test if you have n inputs. 如果您对这种类型的问题过于笼统,则可能会认为您必须检查所有可能的输入组合(每个权重都可以包括或排除),为您提供2 n种组合以测试是否有n种输入。 This is, however, rather beside the point. 但是,这不是重点。 Rather, the key here is that all weights are integers, and that the goal is 1000. 相反,这里的关键是所有权重都是整数,目标是1000。

Let's examine corner cases first, because that limits the search space. 让我们先检查极端情况,因为这限制了搜索空间。

If all weights are >= 1000, pick the smallest. 如果所有权重均> = 1000,则选择最小的权重。

If there is at least one weight < 1000, that is always better than any weight >= 2000, so you can ignore any weight >= 1000 for combination purposes. 如果至少有一个权重<1000,则总是比任何权重> = 2000更好,因此出于组合目的,您可以忽略任何权重> = 1000。

Then, apply dynamic programming. 然后,应用动态编程。 Keep a set (you got HashSet as suggestion from other poster, but BitSet is even better since the maximum value in it is so small) of all combinations of the first k inputs, and increase k by combining all previous solutions with the k+1'th input. 保留前k个输入的所有组合的集合(从其他海报中获得HashSet作为建议,但BitSet更好,因为其最大值很小),并通过将所有先前的解决方案与k + 1组合来增加k的输入。

When you have considered all possibilities, just search the bit vector for the best response. 考虑完所有可能性后,只需搜索位向量以获得最佳响应。

static int count() {
    int[] weights = new int[]{900, 500, 498, 4};

    // Check for corner case to limit search later
    int min = Integer.MAX_VALUE;
    for (int weight : weights) min = Math.min(min, weight);
    if (min >= 1000) {
        return min;
    }        
    // Get all interesting combinations
    BitSet combos = new BitSet();
    for (int weight : weights) {  
        if (weight < 1000) {
            for (int t = combos.previousSetBit(2000 - weight) ; t >= 0; t = combos.previousSetBit(t-1)) {
                combos.set(weight + t);
            }
            combos.set(weight);
        }
    }
    // Pick best combo
    for (int distance = 0; distance <= 1000; distance++) {
        if (combos.get(1000 + distance)) {
            return 1000 + distance;
        }
        if (combos.get(1000 - distance)) {
            return 1000 - distance;
        }
    }
    return 0;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM