-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmagikarp.cpp
More file actions
283 lines (228 loc) · 9.06 KB
/
Copy pathmagikarp.cpp
File metadata and controls
283 lines (228 loc) · 9.06 KB
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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
#include <iostream>
#include <fstream>
#include <vector>
#include <queue>
#include <cmath>
#include <chrono>
#include <numeric>
#include <iomanip>
#include <random>
#include <algorithm>
#include <string>
// rastrear os elementos e seus sinais
struct Element {
int64_t value;
std::vector<std::pair<int64_t, int>> components; // Par de número e sinal (+1 ou -1)
// elementos iniciais
Element(int64_t v) : value(v) {
components.emplace_back(v, +1);
}
// combinar elementos durante o algoritmo
Element(int64_t v, const Element& e1, const Element& e2) : value(v) {
// componentes do primeiro elemento com o mesmo sinal
components.insert(components.end(), e1.components.begin(), e1.components.end());
// inverte o sinal do segundo elemento
for (const auto& comp : e2.components) {
components.emplace_back(comp.first, -comp.second);
}
}
// fila de prioridade
bool operator<(const Element& other) const {
return value < other.value;
}
};
// Karmarkar-Karp com GRASP SA
int64_t karmarkar_karp_with_grasp(const std::vector<int64_t>& numbers, std::vector<int>& solution, double alpha, std::mt19937& rng) {
auto compare = [](const Element& a, const Element& b) { return a.value < b.value; };
std::priority_queue<Element, std::vector<Element>, decltype(compare)> max_heap(compare);
for (const auto& num : numbers) {
max_heap.push(Element(num));
}
while (max_heap.size() > 1) {
// remove o maior
Element largest = max_heap.top();
max_heap.pop();
// RCL para o segundo
std::vector<Element> rcl;
// seleciona da heap
std::vector<Element> temp_elements;
while (!max_heap.empty()) {
temp_elements.push_back(max_heap.top());
max_heap.pop();
}
// valor mínimo e máximo nos elementos temporários
int64_t min_value = temp_elements.back().value; // Já está ordenado
int64_t max_value = temp_elements.front().value;
// alcance da RCL
int64_t threshold = max_value - static_cast<int64_t>(alpha * (max_value - min_value));
for (const auto& elem : temp_elements) {
if (elem.value >= threshold) {
rcl.push_back(elem);
} else {
break; // Como está ordenado, podemos sair do loop
}
}
// segundo elemento da RCL é aleatório
std::uniform_int_distribution<std::size_t> dist(0, rcl.size() - 1);
std::size_t index = dist(rng);
Element second = rcl[index];
// removeme o segundo
temp_elements.erase(temp_elements.begin() + index);
// a diferença e cria um novo elemento
int64_t difference = std::abs(largest.value - second.value);
Element new_element(difference, largest, second);
// demais voltam pra heap
for (const auto& elem : temp_elements) {
max_heap.push(elem);
}
// novo elemento na heap
max_heap.push(new_element);
}
// o último elemento restante tem a diferença mínima e os componentes
Element final_element = max_heap.top();
max_heap.pop();
// solução inicial
solution.resize(numbers.size(), 0); // vetor de sinais (+1 ou -1)
for (const auto& comp : final_element.components) {
int64_t num = comp.first;
int sign = comp.second;
// Encontra o índice do número original
auto it = std::find(numbers.begin(), numbers.end(), num);
if (it != numbers.end()) {
std::size_t idx = std::distance(numbers.begin(), it);
solution[idx] = sign;
}
}
return final_element.value;
}
// Simulated Annealing na busca local com resfriamento exponencial
void simulated_annealing(const std::vector<int64_t>& numbers, std::vector<int>& solution, int64_t& best_difference, std::mt19937& rng, double initial_temperature, double cooling_rate) {
double temperature = initial_temperature;
int64_t current_difference = 0;
{
int64_t sum1 = 0, sum2 = 0;
for (std::size_t i = 0; i < numbers.size(); ++i) {
if (solution[i] == +1) {
sum1 += numbers[i];
} else {
sum2 += numbers[i];
}
}
current_difference = sum1 - sum2;
}
std::vector<int> current_solution = solution;
std::vector<int> best_solution = solution;
// SA Loop
while (temperature > 1e-6) { // temp min
// nova solução vizinha trocando o sinal de um elemento aleatório
std::uniform_int_distribution<std::size_t> dist(0, numbers.size() - 1);
std::size_t idx = dist(rng);
current_solution[idx] *= -1;
// diferença da nova solução incrementalmente
int64_t delta_value = 2 * current_solution[idx] * numbers[idx];
int64_t new_difference = current_difference + delta_value;
// variação de energia
int64_t delta = std::abs(new_difference) - std::abs(current_difference);
// se aceita a nova
if (delta < 0 || (std::exp(-delta / temperature) > ((double)rng() / rng.max()))) {
// caso sim
current_difference = new_difference;
solution = current_solution;
// atualiza
if (std::abs(current_difference) < best_difference) {
best_difference = std::abs(current_difference);
best_solution = solution;
}
} else {
// caso não, reverte
current_solution[idx] *= -1;
}
// diminui a temp (exp)
temperature *= cooling_rate;
}
// atualiza a solução com a melhor encontrada
solution = best_solution;
}
// ler a instância do arquivo
bool read_instance(const std::string& filename, std::vector<int64_t>& numbers) {
std::ifstream infile(filename);
if (!infile) {
std::cerr << "Erro ao abrir o arquivo: " << filename << std::endl;
return false;
}
int64_t N;
if (!(infile >> N)) {
std::cerr << "Erro ao ler o número de elementos N." << std::endl;
return false;
}
numbers.reserve(N);
int64_t num;
for (int64_t i = 0; i < N; ++i) {
if (!(infile >> num)) {
std::cerr << "Erro ao ler o elemento número " << i + 1 << "." << std::endl;
return false;
}
numbers.push_back(num);
}
infile.close();
return true;
}
int main(int argc, char* argv[]) {
if (argc < 7) {
std::cerr << "Uso: " << argv[0] << " <arquivo_instancia> <alpha> <iteracoes> <temperatura_inicial> <cooling_rate> <seed>" << std::endl;
return 1;
}
std::string filename = argv[1];
double alpha = std::stod(argv[2]); // Alpha para o GRASP
int iterations = std::stoi(argv[3]); // Número de iterações do GRASP
double initial_temperature = std::stod(argv[4]); // Temperatura inicial do SA
double cooling_rate = std::stod(argv[5]); // Fator de resfriamento do SA
int seed = std::stoi(argv[6]); // Seed
std::vector<int64_t> numbers;
if (!read_instance(filename, numbers)) {
return 1;
}
std::mt19937 rng(seed);
int64_t best_difference = INT64_MAX;
std::vector<int> best_solution(numbers.size(), +1); // Vetor de sinais (+1 ou -1)
auto start = std::chrono::high_resolution_clock::now();
// GRASP
for (int i = 0; i < iterations; ++i) {
std::vector<int> solution(numbers.size(), +1); // Solução atual
// solução inicial
int64_t difference = karmarkar_karp_with_grasp(numbers, solution, alpha, rng);
// SA
simulated_annealing(numbers, solution, difference, rng, initial_temperature, cooling_rate);
if (difference < best_difference) {
best_difference = difference;
best_solution = solution;
}
}
auto end = std::chrono::high_resolution_clock::now();
std::chrono::duration<double> duration = end - start;
std::vector<int64_t> subset1, subset2;
for (std::size_t i = 0; i < numbers.size(); ++i) {
if (best_solution[i] == +1) {
subset1.push_back(numbers[i]);
} else {
subset2.push_back(numbers[i]);
}
}
int64_t sum1 = std::accumulate(subset1.begin(), subset1.end(), int64_t(0));
int64_t sum2 = std::accumulate(subset2.begin(), subset2.end(), int64_t(0));
std::cout << "Diferença mínima possível entre as somas dos dois subconjuntos: " << std::abs(sum1 - sum2) << std::endl;
std::cout << "Subconjunto 1: ";
for (auto num : subset1) {
std::cout << num << " ";
}
std::cout << "\nSubconjunto 2: ";
for (auto num : subset2) {
std::cout << num << " ";
}
std::cout << "\n";
std::cout << "Soma do Subconjunto 1: " << sum1 << std::endl;
std::cout << "Soma do Subconjunto 2: " << sum2 << std::endl;
std::cout << "Diferença real entre as somas dos subconjuntos: " << std::abs(sum1 - sum2) << std::endl;
std::cout << "Tempo de execução: " << std::fixed << std::setprecision(6) << duration.count() << " segundos" << std::endl;
return 0;
}