-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathbranch_and_bound.jl
130 lines (117 loc) · 4.23 KB
/
branch_and_bound.jl
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
130
# Encontra solução gulosa a ser utilizada como solução viável inicial
function greedy_knapsack(n, B, v, w)
sorted_items = sort([(v[i] / w[i], i) for i in 1:n], rev=true)
capacity = B
value = 0
items = Int64[]
for j in 1:n
_, i = sorted_items[j]
if w[i] <= capacity
value += v[i]
capacity -= w[i]
push!(items, i)
end
end
return items
end
# Algoritmo Branch and Bound
function branch_and_bound_knapsack(n, B, v, w; time_limit=Inf64)
t0 = time()
# Inicializa valores
greedy = greedy_knapsack(n, B, v, w)
current_max_value = sum(v[greedy])
current_best_solution = greedy
optimal_solution_count = 1
# Upper bounds a serem retornados
largest_upper_bound = -Inf64
root_upper_bound = -Inf64
# Ordena itens de acordo com densidade
sorted_density = sort([(v[i] / w[i], i) for i in 1:n], rev=true)
sorted_items = map(item -> item[2], sorted_density)
should_print_tle = true
# Relaxação de programação linear - problema da mochila fracionário
# Encontra relaxação para problema com itens 1:(i-1) fixos
function fractional_knapsack_items(initial_value, initial_weight, i)
capacity = B - initial_weight
value = initial_value
for sorted_item in sorted_density[i:end]
_, i = sorted_item
if w[i] <= capacity
value += v[i]
capacity -= w[i]
else
value += v[i] * (capacity / w[i])
return value
end
end
return value
end
# Função recursiva para busca em profundidade
function branch_and_bound_inner(i, items, value, weight)
# Atualiza melhor solução conhecida
if value > current_max_value
current_max_value = value
current_best_solution = items
optimal_solution_count = 1
elseif value == current_max_value
optimal_solution_count += 1
end
# Caso alcance uma folha, retorna
if i == n+1
return value, items
end
# Calcula limite superior via mochila fracionário
upper_bound = floor(Int64, fractional_knapsack_items(value, weight, i))
if i == 1
root_upper_bound = upper_bound
end
if upper_bound <= current_max_value
return current_max_value, current_best_solution
end
# Checa se o tempo limite foi excedido
t1 = time()
if t1 - t0 > time_limit
if should_print_tle
println("Tempo limite excedido.")
should_print_tle = false
end
largest_upper_bound = max(largest_upper_bound, upper_bound)
return current_max_value, current_best_solution
end
# Caso não consiga podar, ramifica
current_item = sorted_items[i]
ans1 = -Inf64
ans2 = -Inf64
items1 = []
items2 = []
# Particiona em ordem aleatória
order = rand(0:1)
if order == 1
if weight + w[current_item] <= B
new_items = copy(items)
push!(new_items, current_item)
ans1, items1 = branch_and_bound_inner(i+1, new_items, value + v[current_item], weight + w[current_item])
end
ans2, items2 = branch_and_bound_inner(i+1, items, value, weight)
else
ans2, items2 = branch_and_bound_inner(i+1, items, value, weight)
if weight + w[current_item] <= B
new_items = copy(items)
push!(new_items, current_item)
ans1, items1 = branch_and_bound_inner(i+1, new_items, value + v[current_item], weight + w[current_item])
end
end
# Retorna melhor subproblema
if ans1 >= ans2
return ans1, items1
else
return ans2, items2
end
end
if largest_upper_bound == -Inf64
largest_upper_bound = current_max_value
optimal_solution_count = max(1, optimal_solution_count)
end
t1 = time()
return branch_and_bound_inner(1, [], 0, 0)..., root_upper_bound, largest_upper_bound, optimal_solution_count, t1 - t0
end