Expected execution time vs actual execution time in python

Question:

I have two different functions for solving the knapsack problem.

The difference in these functions is that the v2 function uses less space over v1. From my time complexity analysis, the v2 function should not be faster than v1.

However, after running my test cases several times, I found that v2 is significantly faster than v1, and I cannot understand why.

I am using Python Unittest.

Here is the test times:

v1 execution time:

Ran 1 test in 35.985s

v2 execution time:

Ran 1 test in 25.294s

Here is my v1 functions:

def knapsack_bottom_up_v1(self):
    N = len(self.values)
    C = self.capacity

    # table
    dp = [[0 for rc in range(C+1)] for i in range(N)]

    # filling out the table
    for i in range(0, N):
        i_weight = self.weights[i]
        i_val = self.values[i]
        for rc in range(1, C+1):
            # edge case
            if i == 0:
                if i_weight > rc:
                    dp[i][rc] = 0
                else:
                    dp[i][rc] = i_val
            # recurrence relation
            if i_weight > rc:
                dp[i][rc] = dp[i-1][rc]
            else:
                dp[i][rc] = max(dp[i-1][rc], dp[i-1][rc-i_weight] + i_val)
    return dp[N-1][C]

Here is my v2 function:

def knapsack_bottom_up_v2(self):
    N = len(self.values)
    C = self.capacity

    # prev_dp == dp[i-1]
    prev_dp = [0]*(C+1)
    # dp == dp[i]
    dp = [0]*(C+1)

    # filling out the table
    for i in range(0, N):
        i_weight = self.weights[i]
        i_val = self.values[i]
        for rc in range(1, C+1):
            # recurrence relation
            if i_weight > rc:
                dp[rc] = prev_dp[rc]
            else:
                dp[rc] = max(prev_dp[rc], prev_dp[rc-i_weight] + i_val)
        prev_dp, dp = dp, prev_dp
        for i in range(len(dp)):
            dp[i] = 0

    return prev_dp[C]

Here is also the test case I’m using:

values = [825594,1677009,1676628,1523970,943972,97426,69666,1296457,1679693,
              1902996,1844992,1049289,1252836,1319836,953277,2067538,675367,853655,
              1826027,65731,901489,577243,466257,369261]
weights = [382745,799601,909247,729069,467902,44328,34610,698150,823460,903959,
               853665,551830,610856,670702,488960,951111,323046,446298,931161,31385,
               496951,264724,224916,169684]
capacity = 6404180
solution = [1,1,0,1,1,1,0,0,0,1,1,0,1,0,0,1,0,0,0,0,0,1,1,1]

Can anyone help me understand why the execution time of v2 is faster than v1? I think it should be about the same, if not, v2 should be slightly slower than v1.

Thanks!

Asked By: Alex Kang

||

Answers:

The time difference mainly comes from two or three more indexes in each internal loop.

I did a test on my machine and did two additional indexes in each internal loop. The difference was about 9 seconds:

>>> lst = [0]
>>> timeit("""for i in range(C):
...     prev = lst[0]
...     for j in range(N):
...         prev
...         prev
... """, globals=globals(), number=1)
3.6931853000132833
>>> timeit("""for i in range(C):
...     for j in range(N):
...         lst[0]
...         lst[0]
... """, globals=globals(), number=1)
12.408428700000513
Answered By: Mechanic Pig