Compare commits
15 Commits
2f3c0c82b7
...
dnk-genera
| Author | SHA1 | Date | |
|---|---|---|---|
| aec9898091 | |||
| 3c8bed290d | |||
| 13ab972fdc | |||
| 4ca1da90fb | |||
| 6bab964dd5 | |||
| d7d9d5e6ea | |||
| 46d3a584b4 | |||
| f5a8420025 | |||
| 1e7ad317b8 | |||
| 0a47e5ffcb | |||
| 5f153a71d3 | |||
| 901e3a00ff | |||
| 10b10a4f61 | |||
| 7b3b1eb23e | |||
| 87528c097e |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -32,3 +32,4 @@
|
|||||||
*.out
|
*.out
|
||||||
*.app
|
*.app
|
||||||
|
|
||||||
|
build
|
||||||
|
|||||||
55
README.md
55
README.md
@@ -1,46 +1,43 @@
|
|||||||
# Pontifícia Universidade Católica de Goiás
|
# Solução para o problema Bin Packing
|
||||||
|
|
||||||
## Escola Politécnica e de Artes
|
Este projeto é a solução do Danilo Barcelos e do Allann Cintra para o 2º Desafio em Otimização com Meta-heurística. Ele segue esta seguinte estrutura de arquivos:
|
||||||
|
|
||||||
### Ciência da Computação
|
- A pasta "src" contém o código-fonte do programa da solução
|
||||||
|
- A pasta "Docs" contém o arquivo do Writer que foi convertido em PDF para submissão
|
||||||
|
- A pasta "test" contém os casos de teste utilizados. Ela contém o conjunto completo de casos de teste, o conjunto reduzido para debugging, e o arquivo contendo as soluções ótimas de todos os casos
|
||||||
|
- Na pasta raiz você encontrará os scripts que ajudam a testar vários casos de teste de forma rápida
|
||||||
|
|
||||||
Alunos: Allann Barbosa Cintra, Danilo Matias Barcelos
|
# Modo de Usar
|
||||||
|
|
||||||
***
|
## No Linux
|
||||||
|
|
||||||
## 2º Desafio em Otimização com Meta-heurística
|
Compile o programa usando makefile `> make`, ou manualmente `> g++ src/main.cpp src/random.cpp src/sa.cpp -o build/sabp`. Após isso você pode rodar o programa manualmente `> build/sabp`, digitando o caso de teste no terminal, ou usando o script "run.sh".
|
||||||
|
|
||||||
### Problema do Empacotamento – Solução com Recozimento Simulado
|
Para usar ele, basta chamá-lo, por padrão ele busca o executável no lugar onde o makefile compila, e os testes em `test/reduced`. Caso queira mudar onde ele busca as coisas, pode-se usar estas variáveis:
|
||||||
|
|
||||||
#### Tópicos:
|
- EXEC: Caminho do executável
|
||||||
|
- TEST_FOLDER: Caminho da pasta que contém casos de teste. Cada caso deve ser um arquivo de texto
|
||||||
|
- SOLUTIONS_FILE: Caminho do arquivo de soluções
|
||||||
|
|
||||||
1. Introdução
|
Aqui vai um exemplo de comando para usar o script, supondo que todos os arquivos necessários estão em `/tmp/files`:
|
||||||
|
|
||||||
1.1. Ambiente de Teste & Desenvolvimento
|
`EXEC=/tmp/files/exec.out TEST_FOLDER=/tmp/files/tests SOLUTIONS_FILE=/tmp/files/solucoes.txt ./run.sh`
|
||||||
|
|
||||||
1.2. Instâncias Utilizadas
|
NOTA: O script bash não diz quanto tempo cada caso de teste levou
|
||||||
|
|
||||||
2. Meta-heurística utilizada
|
## No Windows
|
||||||
|
|
||||||
2.1. Descrição
|
Compile o programa manualmente, pois o makefile não funciona no Windows `> g++ src/main.cpp src/random.cpp src/sa.cpp -o build/sabp.exe`. Após isso você pode rodar o programa manualmente `> build\sabp.exe`, digitando o caso de teste no terminal, ou usando o script "run.bat".
|
||||||
|
|
||||||
2.2. Implementação
|
Para usar ele, basta chamálo, por padrão ele busca o executável no lugar onde ele estaria se o makefile funcionasse no Windows (`build\sabp.exe`), e os testes em `test\reduced`. Caso queira mudar seu comportamento, pode-se usar estas variáveis:
|
||||||
|
|
||||||
2.3. Variações Testadas
|
- EXEC: Caminho do executável (Parece que precisa estar em aspas? Batch é estranho)
|
||||||
|
- TEST_FOLDER: Caminho da pasta que contém casos de teste. Cada caso deve ser um arquivo de texto
|
||||||
|
- ARGS: Define os argumentos a se passar para o programa. O programa suporta 2 argumentos: i=Não imprimir informações sobre as iterações; c=Não imprimir conteúdos das caixas
|
||||||
|
- SOLUTIONS_FILE: Caminho do arquivo de soluções
|
||||||
|
|
||||||
2.4. Resultados
|
Aqui vai um exemplo de comando para usar o script, supondo que todos os arquivos necessários estão em `C:\arquivos`, e que você está interessado no conteúdo das caixas, mas não nas informações sobre iterações:
|
||||||
|
|
||||||
3. Conclusão
|
`set EXEC="C:\arquivos\prog.exe"&& set TEST_FOLDER=C:\arquivos\testes&& set SOLUTIONS_FILE=C:\arquivos\testes\Solucoes.txt&& set ARGS=i&& run.bat`
|
||||||
|
|
||||||
***
|
Este script não procura o arquivo de soluções por padrão, mas diz quantos segundos cada caso de teste levou para ser processado. Se a saída deste script for redirecionada para um arquivo, e um arquivo de soluções foi especificado, pode-se usar o script getstats.py para obter estatísticas do teste.
|
||||||
|
|
||||||
## Introdução
|
|
||||||
|
|
||||||
Este documento é a entrega única do nosso grupo, ele contém todas as informações requisitadas para a submissão do desafio.
|
|
||||||
|
|
||||||
### Ambiente de Teste & Desenvolvimento
|
|
||||||
|
|
||||||
Os computadores pessoais dos dois integrantes foram utilizados em todos os processos deste desafio.
|
|
||||||
O computador do integrante Danilo Barcelos é um computador de mesa sem modelo, possui o processador Ryzen 5 3400G, a placa-mãe ASUS A320M, e 12 GB de memória RAM com 2666 MHz de velocidade. Não possui GPU dedicada. Usa o sistema operacional Windows 10, e o IDE Visual Studio Code.
|
|
||||||
O integrante Allann Cintra possui ???. Ele usa o sistema operacional Linux, com a distribuição Gentoo, e programa na IDE NeoVim.
|
|
||||||
Os resultados dos testes contidos neste documento foram obtidos executando o algoritmo através do script “launch.bat” contido no diretório raiz do projeto no repositório. Todos os testes foram realizados no computador do Danilo. Para executar o algoritmo em um ambiente Linux, basta executar o script “launch.sh”.
|
|
||||||
|
|||||||
45
main.cpp
45
main.cpp
@@ -1,45 +0,0 @@
|
|||||||
#include <iostream>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "sa.hpp"
|
|
||||||
|
|
||||||
#ifndef TEMP
|
|
||||||
#define TEMP 1000
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef ALPHA
|
|
||||||
#define ALPHA 0.999
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef TEMP_MIN
|
|
||||||
#define TEMP_MIN 10
|
|
||||||
#endif
|
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
|
||||||
{
|
|
||||||
char flags = 0;
|
|
||||||
for(int i = 1; i < argc; i++){
|
|
||||||
if(argv[i][0] == '-'){
|
|
||||||
int j = 1;
|
|
||||||
while(argv[i][j] != '\0'){
|
|
||||||
if(argv[i][j] == 'i') flags |= SIMULATED_ANNEALING_DISABLE_SHOWITRNUM;
|
|
||||||
else if(argv[i][j] == 'c') flags |= SIMULATED_ANNEALING_DISABLE_SHOWCRATES;
|
|
||||||
j++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int number_of_items = 0;
|
|
||||||
int capacity = 0;
|
|
||||||
std::cin >> number_of_items >> capacity;
|
|
||||||
|
|
||||||
std::vector<long long> items(number_of_items);
|
|
||||||
for (auto &i : items) {
|
|
||||||
std::cin >> i;
|
|
||||||
}
|
|
||||||
|
|
||||||
sa::solution act = sa::solution::simulated_annealing(capacity, items,
|
|
||||||
ALPHA, TEMP, TEMP_MIN);
|
|
||||||
|
|
||||||
act.print_sol(flags);
|
|
||||||
}
|
|
||||||
47
makefile
Normal file
47
makefile
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
# Flag para alterar o padrão do compilador
|
||||||
|
standart = -std=c++17
|
||||||
|
|
||||||
|
# Flags para otimizar o arquivo executável
|
||||||
|
optimize_flags = -O3 -pipe -flto
|
||||||
|
|
||||||
|
# Flags para ativar todos os avisos do compilador
|
||||||
|
warnings = -Wall -Wextra -Werror -Wformat=2 -Wno-maybe-uninitialized \
|
||||||
|
-Wformat-overflow=2 -Wundef -Wconversion -Wwrite-strings
|
||||||
|
|
||||||
|
# Flags para depurar o código
|
||||||
|
sanitize = -fsanitize=address,undefined,pointer-compare,pointer-subtract
|
||||||
|
debug_flags = -ggdb3 -Og -DDEBUG -Wformat-truncation=2 $(sanitize)
|
||||||
|
|
||||||
|
CC := /usr/bin/gcc
|
||||||
|
CXX := /usr/bin/g++
|
||||||
|
|
||||||
|
builddir := build
|
||||||
|
objectname = sabp
|
||||||
|
objectdir = $(builddir)/$(objectname)
|
||||||
|
|
||||||
|
.PHONY: all debug
|
||||||
|
|
||||||
|
all:set_flags $(objectdir)
|
||||||
|
|
||||||
|
debug:set_debug_flags $(objectdir)
|
||||||
|
|
||||||
|
$(objectdir):$(builddir) $(builddir)/random.o $(builddir)/sa.o src/main.cpp
|
||||||
|
$(CXX) $(CPPFLAGS) $(builddir)/random.o $(builddir)/sa.o src/main.cpp -o $(objectdir)
|
||||||
|
|
||||||
|
$(builddir)/random.o:src/random.cpp
|
||||||
|
$(CXX) $(CPPFLAGS) src/random.cpp -o $(builddir)/random.o -c
|
||||||
|
|
||||||
|
$(builddir)/sa.o:src/sa.cpp
|
||||||
|
$(CXX) $(CPPFLAGS) src/sa.cpp -o $(builddir)/sa.o -c
|
||||||
|
|
||||||
|
$(builddir):
|
||||||
|
mkdir -p $(builddir)
|
||||||
|
|
||||||
|
set_flags:
|
||||||
|
$(eval override CPPFLAGS += $(warnings) $(optimize_flags) $(standart))
|
||||||
|
|
||||||
|
set_debug_flags:
|
||||||
|
$(eval override CPPFLAGS += $(warnings) $(sanitize) $(debug_flags))
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -rf $(builddir)
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
#include "random.hpp"
|
|
||||||
|
|
||||||
std::random_device rdevice;
|
|
||||||
std::mt19937_64 rng::rng(rdevice());
|
|
||||||
24
random.hpp
24
random.hpp
@@ -1,24 +0,0 @@
|
|||||||
#ifndef RANDOM_HPP_RANDOMNESS_WHATEVER_HEADER_238947837827_H
|
|
||||||
#define RANDOM_HPP_RANDOMNESS_WHATEVER_HEADER_238947837827_H
|
|
||||||
|
|
||||||
#include <random>
|
|
||||||
|
|
||||||
namespace rng {
|
|
||||||
extern std::mt19937_64 rng;
|
|
||||||
|
|
||||||
template<long long start, long long end>
|
|
||||||
auto random_int() -> int
|
|
||||||
{
|
|
||||||
static std::uniform_int_distribution<> rint(start, end);
|
|
||||||
return rint(rng);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<int start, int end>
|
|
||||||
auto random_double() -> double
|
|
||||||
{
|
|
||||||
static std::uniform_real_distribution<> rdouble(start, end);
|
|
||||||
return rdouble(rng);
|
|
||||||
}
|
|
||||||
} // namespace rng
|
|
||||||
|
|
||||||
#endif
|
|
||||||
7
run.bat
7
run.bat
@@ -3,16 +3,15 @@
|
|||||||
setlocal enabledelayedexpansion
|
setlocal enabledelayedexpansion
|
||||||
|
|
||||||
if not defined EXEC (
|
if not defined EXEC (
|
||||||
set EXEC="build\main.exe"
|
set EXEC="build\sabp.exe"
|
||||||
)
|
)
|
||||||
|
|
||||||
if not defined TEST_FOLDER (
|
if not defined TEST_FOLDER (
|
||||||
set TEST_FOLDER=test\reduced
|
set TEST_FOLDER=test\reduced
|
||||||
)
|
)
|
||||||
|
|
||||||
if defined ARGS (
|
rem I need to fix this later, dear God...
|
||||||
set ARGS=-%ARGS%
|
set ARGS=-ic
|
||||||
)
|
|
||||||
|
|
||||||
if defined SOLUTIONS_FILE (
|
if defined SOLUTIONS_FILE (
|
||||||
for /f "tokens=1,2 skip=1" %%a in (%SOLUTIONS_FILE%) do (
|
for /f "tokens=1,2 skip=1" %%a in (%SOLUTIONS_FILE%) do (
|
||||||
|
|||||||
6
run.sh
6
run.sh
@@ -3,15 +3,15 @@
|
|||||||
IFS=$'\n'
|
IFS=$'\n'
|
||||||
|
|
||||||
if [[ -z $EXEC ]]; then
|
if [[ -z $EXEC ]]; then
|
||||||
EXEC="build/main.out"
|
EXEC="build/sabp"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ -z $TEST_FOLDER ]]; then
|
if [[ -z $TEST_FOLDER ]]; then
|
||||||
TEST_FOLDER="test"
|
TEST_FOLDER="test/reduced"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ -z $SOLUTIONS_FILE ]]; then
|
if [[ -z $SOLUTIONS_FILE ]]; then
|
||||||
SOLUTIONS_FILE=solucoes.txt
|
SOLUTIONS_FILE=test/solucoes.txt
|
||||||
fi
|
fi
|
||||||
|
|
||||||
declare -A solutions
|
declare -A solutions
|
||||||
|
|||||||
32
sa.cpp
32
sa.cpp
@@ -1,32 +0,0 @@
|
|||||||
#include "sa.hpp"
|
|
||||||
#include <cmath>
|
|
||||||
|
|
||||||
auto sa::solution::simulated_annealing(int capacity, const std::vector<long long> &items,
|
|
||||||
const double alpha, double temp,
|
|
||||||
const double temp_min)->sa::solution
|
|
||||||
{
|
|
||||||
sa::solution best(items, capacity);
|
|
||||||
sa::solution prev = best;
|
|
||||||
|
|
||||||
int iteration = 0;
|
|
||||||
|
|
||||||
while (temp > temp_min) {
|
|
||||||
iteration++;
|
|
||||||
sa::solution neighbor(prev, iteration);
|
|
||||||
neighbor.setneighbor();
|
|
||||||
|
|
||||||
long long diff = neighbor.fitness - prev.fitness;
|
|
||||||
if (diff < 0 || rng::random_double<0, 1>() / temp < 0.8) {
|
|
||||||
swap(prev, neighbor);
|
|
||||||
}
|
|
||||||
|
|
||||||
temp *= alpha;
|
|
||||||
|
|
||||||
if (prev.fitness < best.fitness) {
|
|
||||||
best = prev;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
best.setiterations(iteration);
|
|
||||||
return best;
|
|
||||||
}
|
|
||||||
235
sa.hpp
235
sa.hpp
@@ -1,235 +0,0 @@
|
|||||||
#ifndef SIMULATED_ANNEALING_HEADER_12647_H
|
|
||||||
#define SIMULATED_ANNEALING_HEADER_12647_H
|
|
||||||
|
|
||||||
#define SIMULATED_ANNEALING_DISABLE_SHOWCRATES 1
|
|
||||||
#define SIMULATED_ANNEALING_DISABLE_SHOWITRNUM 2
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <iostream>
|
|
||||||
#include <queue>
|
|
||||||
#include <random>
|
|
||||||
#include <set>
|
|
||||||
#include <utility>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "random.hpp"
|
|
||||||
|
|
||||||
namespace sa {
|
|
||||||
|
|
||||||
using content = std::priority_queue<long long, std::vector<long long>, std::greater<>>;
|
|
||||||
|
|
||||||
class box {
|
|
||||||
content items;
|
|
||||||
long long fullness{};
|
|
||||||
|
|
||||||
friend void swap(box &one, box &two) {
|
|
||||||
using std::swap;
|
|
||||||
|
|
||||||
swap(one.items, two.items);
|
|
||||||
swap(one.fullness, two.fullness);
|
|
||||||
}
|
|
||||||
|
|
||||||
void pop() {
|
|
||||||
fullness -= items.top();
|
|
||||||
items.pop();
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
box() = default;
|
|
||||||
|
|
||||||
box(const content &items, long long fullness):
|
|
||||||
items(items), fullness(fullness) {}
|
|
||||||
|
|
||||||
void add_item(long long item) {
|
|
||||||
fullness += item;
|
|
||||||
items.push(item);
|
|
||||||
}
|
|
||||||
|
|
||||||
void clear() {
|
|
||||||
while (!items.empty()) {
|
|
||||||
items.pop();
|
|
||||||
}
|
|
||||||
fullness = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto swappable10(box &other, int capacity) -> bool {
|
|
||||||
return items.top() + other.fullness <= capacity;
|
|
||||||
}
|
|
||||||
|
|
||||||
void swap10(box &other) {
|
|
||||||
other.add_item(items.top());
|
|
||||||
pop();
|
|
||||||
}
|
|
||||||
|
|
||||||
auto swappable11(box &other, int capacity) -> bool {
|
|
||||||
long long choice1 = items.top();
|
|
||||||
long long choice2 = other.items.top();
|
|
||||||
return choice1 != choice2 &&
|
|
||||||
choice1 + other.fullness - choice2 <= capacity &&
|
|
||||||
choice2 + fullness - choice1 <= capacity;
|
|
||||||
}
|
|
||||||
|
|
||||||
void swap11(box &other) {
|
|
||||||
long long choice1 = items.top();
|
|
||||||
long long choice2 = other.items.top();
|
|
||||||
|
|
||||||
pop();
|
|
||||||
other.pop();
|
|
||||||
add_item(choice2);
|
|
||||||
other.add_item(choice1);
|
|
||||||
}
|
|
||||||
|
|
||||||
void print(int ind) {
|
|
||||||
std::cout << "Caixa " << ind << ":";
|
|
||||||
content tmp;
|
|
||||||
while (!items.empty()) {
|
|
||||||
std::cout << ' ' << items.top();
|
|
||||||
items.pop();
|
|
||||||
}
|
|
||||||
std::cout << '\n';
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] auto empty() const -> bool {
|
|
||||||
return fullness == 0;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class solution {
|
|
||||||
std::vector<box> boxes;
|
|
||||||
long long fitness;
|
|
||||||
int capacity;
|
|
||||||
int iterations;
|
|
||||||
int iteration;
|
|
||||||
|
|
||||||
friend void swap(solution &one, solution &two) {
|
|
||||||
using std::swap;
|
|
||||||
|
|
||||||
swap(one.boxes, two.boxes);
|
|
||||||
swap(one.capacity, two.capacity);
|
|
||||||
swap(one.fitness, two.fitness);
|
|
||||||
swap(one.iterations, two.iterations);
|
|
||||||
swap(one.iteration, two.iteration);
|
|
||||||
}
|
|
||||||
|
|
||||||
void random_swap(int choice, std::vector<int> &sequence10, std::vector<int> &sequence11) {
|
|
||||||
typedef void (box::*swap)(box&);
|
|
||||||
swap swaps[2] = {
|
|
||||||
&box::swap10,
|
|
||||||
&box::swap11
|
|
||||||
};
|
|
||||||
std::vector<int>* sequences[2] = {
|
|
||||||
&sequence10,
|
|
||||||
&sequence11
|
|
||||||
};
|
|
||||||
|
|
||||||
int now = rng::random_double<0, 1>() > 0.3 ? 0 : 1;
|
|
||||||
|
|
||||||
if (sequences[now]->empty()) {
|
|
||||||
now ^= 1;
|
|
||||||
if (sequences[now]->empty()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::uniform_int_distribution<> dist(0, (int)sequences[now]->size() - 1);
|
|
||||||
(boxes[choice].*swaps[now])(boxes[(*sequences[now])[dist(rng::rng)]]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
solution() = default;
|
|
||||||
|
|
||||||
solution(const solution &other, int itr): boxes(other.boxes), fitness(other.fitness),
|
|
||||||
capacity(other.capacity), iteration(itr) {}
|
|
||||||
|
|
||||||
solution(const std::vector<long long> &items, int capacity): // Gera a solução inicial
|
|
||||||
capacity(capacity), iteration(0) {
|
|
||||||
std::multiset<long long> its;
|
|
||||||
for (auto i : items) {
|
|
||||||
its.insert(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
long long cap = capacity;
|
|
||||||
box tmp;
|
|
||||||
|
|
||||||
while (!its.empty()) {
|
|
||||||
auto itr = its.upper_bound(cap);
|
|
||||||
if (itr == its.begin()) {
|
|
||||||
cap = capacity;
|
|
||||||
cap -= *its.begin();
|
|
||||||
itr = its.begin();
|
|
||||||
this->boxes.emplace_back(tmp);
|
|
||||||
tmp.clear();
|
|
||||||
} else {
|
|
||||||
itr--;
|
|
||||||
cap -= *itr;
|
|
||||||
}
|
|
||||||
tmp.add_item(*itr);
|
|
||||||
its.erase(itr);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!tmp.empty()) {
|
|
||||||
this->boxes.emplace_back(tmp);
|
|
||||||
}
|
|
||||||
fitness = (int)this->boxes.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
void setneighbor() { // Gera um vizinho da solução
|
|
||||||
int choice = std::uniform_int_distribution<>(0, (int)boxes.size() - 1)(rng::rng);
|
|
||||||
std::vector<int> sequence10;
|
|
||||||
std::vector<int> sequence11;
|
|
||||||
|
|
||||||
sequence10.reserve(boxes.size());
|
|
||||||
sequence11.reserve(boxes.size());
|
|
||||||
|
|
||||||
for (size_t i = 0; i < choice; i++) {
|
|
||||||
if (boxes[choice].swappable10(boxes[i], capacity)) {
|
|
||||||
sequence10.push_back((int)i);
|
|
||||||
}
|
|
||||||
if (boxes[choice].swappable11(boxes[i], capacity)) {
|
|
||||||
sequence11.push_back((int)i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (size_t i = choice + 1; i < boxes.size(); i++) {
|
|
||||||
if (boxes[choice].swappable10(boxes[i], capacity)) {
|
|
||||||
sequence10.push_back((int)i);
|
|
||||||
}
|
|
||||||
if (boxes[choice].swappable11(boxes[i], capacity)) {
|
|
||||||
sequence11.push_back((int)i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
random_swap(choice, sequence10, sequence11);
|
|
||||||
if (boxes[choice].empty()) {
|
|
||||||
swap(boxes[choice], boxes[boxes.size() - 1]);
|
|
||||||
boxes.pop_back();
|
|
||||||
fitness = (int)boxes.size();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void print_sol(char flags = 0) {
|
|
||||||
if(!(flags & SIMULATED_ANNEALING_DISABLE_SHOWITRNUM)){
|
|
||||||
std::cout << "Iteração da solução: " << iteration << '\n';
|
|
||||||
std::cout << "Número de iterações calculadas: " << iterations << '\n';
|
|
||||||
}
|
|
||||||
std::cout << "Número de caixas: " << fitness << '\n';
|
|
||||||
|
|
||||||
if(!(flags & SIMULATED_ANNEALING_DISABLE_SHOWCRATES)){
|
|
||||||
for (size_t i = 0; i < boxes.size(); i++) {
|
|
||||||
boxes[i].print((int)i + 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void setiterations(int itr) {
|
|
||||||
iterations = itr;
|
|
||||||
}
|
|
||||||
|
|
||||||
static auto simulated_annealing(int capacity, const std::vector<long long> &items,
|
|
||||||
double alpha, double temp,
|
|
||||||
double temp_min) -> solution;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace sa
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -15,6 +15,12 @@
|
|||||||
#define TEMP_MIN 10
|
#define TEMP_MIN 10
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Função main.
|
||||||
|
* Usada apenas para ler os items, a capacidade máxima e
|
||||||
|
* imprimir os dados da melhor solução encontrada.
|
||||||
|
*/
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
char flags = 0;
|
char flags = 0;
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
#include "sa.hpp"
|
#include "sa.hpp"
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Implementação do algoritmo de SA.
|
||||||
|
*/
|
||||||
|
|
||||||
auto sa::solution::simulated_annealing(int capacity, const std::vector<long long> &items,
|
auto sa::solution::simulated_annealing(int capacity, const std::vector<long long> &items,
|
||||||
const double alpha, double temp,
|
const double alpha, double temp,
|
||||||
const double temp_min)->sa::solution
|
const double temp_min)->sa::solution
|
||||||
@@ -16,7 +20,7 @@ auto sa::solution::simulated_annealing(int capacity, const std::vector<long long
|
|||||||
neighbor.setneighbor();
|
neighbor.setneighbor();
|
||||||
|
|
||||||
long long diff = neighbor.fitness - prev.fitness;
|
long long diff = neighbor.fitness - prev.fitness;
|
||||||
if (diff < 0 || rng::random_double<0, 1>() / temp < 0.8) {
|
if (diff < 0 || rng::random_double<0, 1>() / temp < 0.05) {
|
||||||
swap(prev, neighbor);
|
swap(prev, neighbor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
76
src/sa.hpp
76
src/sa.hpp
@@ -22,6 +22,7 @@ class box {
|
|||||||
content items;
|
content items;
|
||||||
long long fullness{};
|
long long fullness{};
|
||||||
|
|
||||||
|
/* Troca uma caixa com a outra. */
|
||||||
friend void swap(box &one, box &two) {
|
friend void swap(box &one, box &two) {
|
||||||
using std::swap;
|
using std::swap;
|
||||||
|
|
||||||
@@ -29,6 +30,7 @@ class box {
|
|||||||
swap(one.fullness, two.fullness);
|
swap(one.fullness, two.fullness);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Remove o menor item da caixa. */
|
||||||
void pop() {
|
void pop() {
|
||||||
fullness -= items.top();
|
fullness -= items.top();
|
||||||
items.pop();
|
items.pop();
|
||||||
@@ -38,14 +40,20 @@ class box {
|
|||||||
|
|
||||||
box() = default;
|
box() = default;
|
||||||
|
|
||||||
box(const content &items, long long fullness):
|
/*
|
||||||
items(items), fullness(fullness) {}
|
* Inicialia uma caixa a partir de um conjunto de items e da soma total
|
||||||
|
* dos items.
|
||||||
|
*/
|
||||||
|
box(content items, long long fullness):
|
||||||
|
items(std::move(items)), fullness(fullness) {}
|
||||||
|
|
||||||
|
/* Adiciona um item a caixa. */
|
||||||
void add_item(long long item) {
|
void add_item(long long item) {
|
||||||
fullness += item;
|
fullness += item;
|
||||||
items.push(item);
|
items.push(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Limpa a caixa. */
|
||||||
void clear() {
|
void clear() {
|
||||||
while (!items.empty()) {
|
while (!items.empty()) {
|
||||||
items.pop();
|
items.pop();
|
||||||
@@ -53,15 +61,27 @@ class box {
|
|||||||
fullness = 0;
|
fullness = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Verifica se é possível move o menor item da caixa atual para a
|
||||||
|
* caixa "other" sem violar as restrições de capacidade.
|
||||||
|
*/
|
||||||
auto swappable10(box &other, int capacity) -> bool {
|
auto swappable10(box &other, int capacity) -> bool {
|
||||||
return items.top() + other.fullness <= capacity;
|
return items.top() + other.fullness <= capacity;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Move o menor item da caixa atual para a caixa "other".
|
||||||
|
*/
|
||||||
void swap10(box &other) {
|
void swap10(box &other) {
|
||||||
other.add_item(items.top());
|
other.add_item(items.top());
|
||||||
pop();
|
pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Verifica se é possivel realizar "swap11" com a caixa "other" sem
|
||||||
|
* violar as restrições de capacidade, também verificando se ambos os menores
|
||||||
|
* items são os mesmos.
|
||||||
|
*/
|
||||||
auto swappable11(box &other, int capacity) -> bool {
|
auto swappable11(box &other, int capacity) -> bool {
|
||||||
long long choice1 = items.top();
|
long long choice1 = items.top();
|
||||||
long long choice2 = other.items.top();
|
long long choice2 = other.items.top();
|
||||||
@@ -70,6 +90,10 @@ class box {
|
|||||||
choice2 + fullness - choice1 <= capacity;
|
choice2 + fullness - choice1 <= capacity;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Troca o menor item da caixa atual com o menor item da caixa
|
||||||
|
* "other"
|
||||||
|
*/
|
||||||
void swap11(box &other) {
|
void swap11(box &other) {
|
||||||
long long choice1 = items.top();
|
long long choice1 = items.top();
|
||||||
long long choice2 = other.items.top();
|
long long choice2 = other.items.top();
|
||||||
@@ -80,6 +104,7 @@ class box {
|
|||||||
other.add_item(choice1);
|
other.add_item(choice1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Imprime todos os items da caixa junto com um identificador. */
|
||||||
void print(int ind) {
|
void print(int ind) {
|
||||||
std::cout << "Caixa " << ind << ":";
|
std::cout << "Caixa " << ind << ":";
|
||||||
content tmp;
|
content tmp;
|
||||||
@@ -90,6 +115,7 @@ class box {
|
|||||||
std::cout << '\n';
|
std::cout << '\n';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Retorna um booleano indicando se a caixa está vazia. */
|
||||||
[[nodiscard]] auto empty() const -> bool {
|
[[nodiscard]] auto empty() const -> bool {
|
||||||
return fullness == 0;
|
return fullness == 0;
|
||||||
}
|
}
|
||||||
@@ -102,6 +128,10 @@ class solution {
|
|||||||
int iterations;
|
int iterations;
|
||||||
int iteration;
|
int iteration;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Função para trocar duas soluções.
|
||||||
|
* Overload da função std::swap.
|
||||||
|
*/
|
||||||
friend void swap(solution &one, solution &two) {
|
friend void swap(solution &one, solution &two) {
|
||||||
using std::swap;
|
using std::swap;
|
||||||
|
|
||||||
@@ -112,6 +142,10 @@ class solution {
|
|||||||
swap(one.iteration, two.iteration);
|
swap(one.iteration, two.iteration);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Função para usar o swap10 ou swap11 aleatoriamente quando
|
||||||
|
* possível.
|
||||||
|
*/
|
||||||
void random_swap(int choice, std::vector<int> &sequence10, std::vector<int> &sequence11) {
|
void random_swap(int choice, std::vector<int> &sequence10, std::vector<int> &sequence11) {
|
||||||
typedef void (box::*swap)(box&);
|
typedef void (box::*swap)(box&);
|
||||||
swap swaps[2] = {
|
swap swaps[2] = {
|
||||||
@@ -139,28 +173,30 @@ class solution {
|
|||||||
public:
|
public:
|
||||||
solution() = default;
|
solution() = default;
|
||||||
|
|
||||||
|
/* Inicializa uma solução a partir da outra. */
|
||||||
solution(const solution &other, int itr): boxes(other.boxes), fitness(other.fitness),
|
solution(const solution &other, int itr): boxes(other.boxes), fitness(other.fitness),
|
||||||
capacity(other.capacity), iteration(itr) {}
|
capacity(other.capacity), iteration(itr) {}
|
||||||
|
|
||||||
solution(const std::vector<long long> &items, int capacity): // Gera a solução inicial
|
/* Gera a solução inicial a partir dos items disponíveis. */
|
||||||
|
solution(const std::vector<long long> &items, int capacity):
|
||||||
capacity(capacity), iteration(0) {
|
capacity(capacity), iteration(0) {
|
||||||
std::multiset<long long> its;
|
std::multiset<long long> its; // Items ordenados.
|
||||||
for (auto i : items) {
|
for (auto i : items) {
|
||||||
its.insert(i);
|
its.insert(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
long long cap = capacity;
|
long long cap = capacity; // Capacidade restante da caixa atual.
|
||||||
box tmp;
|
box tmp; // Caixa atual.
|
||||||
|
|
||||||
while (!its.empty()) {
|
while (!its.empty()) {
|
||||||
auto itr = its.upper_bound(cap);
|
auto itr = its.upper_bound(cap);
|
||||||
if (itr == its.begin()) {
|
if (itr == its.begin()) { // Caixa está cheia, cria-se outra.
|
||||||
cap = capacity;
|
cap = capacity;
|
||||||
cap -= *its.begin();
|
cap -= *its.begin();
|
||||||
itr = its.begin();
|
itr = its.begin();
|
||||||
this->boxes.emplace_back(tmp);
|
this->boxes.emplace_back(tmp);
|
||||||
tmp.clear();
|
tmp.clear();
|
||||||
} else {
|
} else { // Caixa consegue colocar outro elemento.
|
||||||
itr--;
|
itr--;
|
||||||
cap -= *itr;
|
cap -= *itr;
|
||||||
}
|
}
|
||||||
@@ -174,15 +210,16 @@ class solution {
|
|||||||
fitness = (int)this->boxes.size();
|
fitness = (int)this->boxes.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
void setneighbor() { // Gera um vizinho da solução
|
/* Muda a solução atual para um de seus vizinhos. */
|
||||||
|
void setneighbor() {
|
||||||
int choice = std::uniform_int_distribution<>(0, (int)boxes.size() - 1)(rng::rng);
|
int choice = std::uniform_int_distribution<>(0, (int)boxes.size() - 1)(rng::rng);
|
||||||
std::vector<int> sequence10;
|
std::vector<int> sequence10; // Possíveis candidatos para swap10
|
||||||
std::vector<int> sequence11;
|
std::vector<int> sequence11; // Possíveis candidatos para swap11
|
||||||
|
|
||||||
sequence10.reserve(boxes.size());
|
sequence10.reserve(boxes.size());
|
||||||
sequence11.reserve(boxes.size());
|
sequence11.reserve(boxes.size());
|
||||||
|
|
||||||
for (size_t i = 0; i < choice; i++) {
|
for (size_t i = 0; i < (size_t)choice; i++) {
|
||||||
if (boxes[choice].swappable10(boxes[i], capacity)) {
|
if (boxes[choice].swappable10(boxes[i], capacity)) {
|
||||||
sequence10.push_back((int)i);
|
sequence10.push_back((int)i);
|
||||||
}
|
}
|
||||||
@@ -201,12 +238,22 @@ class solution {
|
|||||||
|
|
||||||
random_swap(choice, sequence10, sequence11);
|
random_swap(choice, sequence10, sequence11);
|
||||||
if (boxes[choice].empty()) {
|
if (boxes[choice].empty()) {
|
||||||
|
/*
|
||||||
|
* Caixa agora está vazia, é possível remove-la.
|
||||||
|
*/
|
||||||
swap(boxes[choice], boxes[boxes.size() - 1]);
|
swap(boxes[choice], boxes[boxes.size() - 1]);
|
||||||
boxes.pop_back();
|
boxes.pop_back();
|
||||||
fitness = (int)boxes.size();
|
fitness = (int)boxes.size();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Imprime todas as informações da solução:
|
||||||
|
* - Número de caixas
|
||||||
|
* - Items em cada caixa
|
||||||
|
* - Iteração onde foi encontrada a solução
|
||||||
|
* - Iterações totais ate o fim do algoritmo
|
||||||
|
*/
|
||||||
void print_sol(char flags = 0) {
|
void print_sol(char flags = 0) {
|
||||||
if(!(flags & SIMULATED_ANNEALING_DISABLE_SHOWITRNUM)){
|
if(!(flags & SIMULATED_ANNEALING_DISABLE_SHOWITRNUM)){
|
||||||
std::cout << "Iteração da solução: " << iteration << '\n';
|
std::cout << "Iteração da solução: " << iteration << '\n';
|
||||||
@@ -221,10 +268,15 @@ class solution {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Pôe o número de iterações totais para a solução. */
|
||||||
void setiterations(int itr) {
|
void setiterations(int itr) {
|
||||||
iterations = itr;
|
iterations = itr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Declaração do algoritmo, sua implementação está no arquivo
|
||||||
|
* sa.cpp.
|
||||||
|
*/
|
||||||
static auto simulated_annealing(int capacity, const std::vector<long long> &items,
|
static auto simulated_annealing(int capacity, const std::vector<long long> &items,
|
||||||
double alpha, double temp,
|
double alpha, double temp,
|
||||||
double temp_min) -> solution;
|
double temp_min) -> solution;
|
||||||
|
|||||||
Reference in New Issue
Block a user