-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdynamic_algo.py
129 lines (99 loc) · 4.02 KB
/
dynamic_algo.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
import time
from config import ACTIONS, BUDGET, DATASET1, DATASET2
def dynamic_algo(data, max_spending):
"""
Dynamic programming algorithm, that uses a 2D matrix to
save the best results at every iteration and compares it to
the best previous result.
####
Where N is the number of actions:
Time complexity: O(N*max_spending)
Space complexity: O(N*max_spending), uses a 2D array.
Further improvements could be made to reduce space complexity,
by using a 2D array with only 2 rows or even further with a
1D array instead of 2D matrix.
"""
# Budget and costs are multiplied per 100 in order to work
# with integers for the matrix.
max_spending *= 100
for line in data:
line[1] = int(line[1] * 100)
line[2] = line[2] * line[1] / 100 # Calculate the profit per actions
# Initiate a matrix with the spendings as columns and actions as rows.
matrix = [[0 for x in range(max_spending + 1)]
for x in range(len(data) + 1)]
# Loop on the rows of actions
for line in range(1, len(data) + 1):
# Loop on the rows of spendings
for c in range(1, max_spending + 1):
# Check if action cost is less than the column spending we are at
if data[line-1][1] <= c:
# We add to the matrix the max between the max profit of previous row,
# and the profit of the current action added to the optimised solution of
# previous row.
# Pseudo-code :
# max(current profit + previous row profit to maximised spending,
# max profit of previous row)
matrix[line][c] = max(
data[line-1][2] + matrix[line-1][c-data[line-1][1]], matrix[line-1][c])
# If the spending is higher than budget, we take previous line solution
else:
matrix[line][c] = matrix[line-1][c]
max_profit = matrix[-1][-1] / 100
# Part 2: get the actions corresponding to the best results
w = max_spending
n = len(data)
actions_selected = []
# While spendings are >=0 and actions remain
while w >= 0 and n >= 0:
# Take the last action, iterate the list backwards
a = data[n-1]
# If current profit == current value profit
# - current value profit minus previous spending,
# then add this action to the list
if matrix[n][w] == matrix[n-1][w - a[1]] + a[2]:
actions_selected.append(a)
# Substract the selected action price from budget
w -= a[1]
# Go to next item
n -= 1
# Convert back to the initial values
for line in actions_selected:
line[1] /= 100
line[2] = round(line[2] / 100, 3)
# Calculate the total spent
spendings = 0
for a in actions_selected:
spendings += a[1]
return max_profit, spendings, actions_selected
def main():
# Initiate chronometer
start = time.time()
result = dynamic_algo(ACTIONS, BUDGET)
print(f"--- Dynamic algo (20 ACTIONS) ---\n \
Total costs : {result[1]}\n \
Maximum total profit : {result[0]}\n \
Selected actions: {result[2]}")
# Calculate runtime and display
end = time.time()
delta_time = end - start
print(f"\n ## The program runs in {delta_time} seconds ##\n")
start = time.time()
result = dynamic_algo(DATASET1, BUDGET)
print(f"--- Dynamic algo (DATASET1) ---\n \
Total costs : {result[1]}\n \
Maximum total profit : {result[0]}\n \
Selected actions: {result[2]}")
end = time.time()
delta_time = end - start
print(f"\n ## The program runs in {delta_time} seconds ##\n")
start = time.time()
result = dynamic_algo(DATASET2, BUDGET)
print(f"--- Dynamic algo (DATASET2) ---\n \
Total costs : {result[1]}\n \
Maximum total profit : {result[0]}\n \
Selected actions: {result[2]}")
end = time.time()
delta_time = end - start
print(f"\n ## The program runs in {delta_time} seconds ##\n")
main()