149 lines
4.2 KiB
C++
149 lines
4.2 KiB
C++
#include "Solver.h"
|
|
#include "Solution.h"
|
|
#include "Assignment.h"
|
|
#include "GeneticAlgorithm.h"
|
|
#include "structures/Employee.h"
|
|
#include <vector>
|
|
#include <numeric>
|
|
#include <iostream>
|
|
|
|
#define RANDF() static_cast<float>(rand()) / static_cast<float> (RAND_MAX)
|
|
|
|
void solver(int nPop, int nGen, float rCross, float rMut, Distances& distances, Centers& centers, Employees& employees, Missions& missions)
|
|
{
|
|
|
|
Solutions* population = generateSolutions(nPop, distances, centers, employees, missions);
|
|
GeneticAlgorithm* GA = new GeneticAlgorithm(nGen, rCross, rMut, population, &fitness, &selection, &crossover, &mutator);
|
|
try
|
|
{
|
|
GA->algorithm();
|
|
}
|
|
catch (std::exception ex)
|
|
{
|
|
std::cerr << "Exception during GA.algorithm:\n" << ex.what() << "\n";
|
|
}
|
|
|
|
}
|
|
|
|
float fitness(Solution* individual)
|
|
{
|
|
std::cout << "Unassigned missions: " << individual->unassignedCount() << "\nCost: " << individual->cost() << "\n";
|
|
|
|
//individual->print();
|
|
return individual->cost();
|
|
}
|
|
|
|
Solution* selection(Solutions* population, Scores* scores)
|
|
{
|
|
float fitnessTotal = std::reduce(scores->begin(), scores->end());
|
|
if (fitnessTotal == 0.f)
|
|
return (*population)[rand() % population->size()];
|
|
|
|
int size = scores->size();
|
|
float* probabilities = new float[size];
|
|
float prevProb = 0;
|
|
for (int i = 0; i < size; i++)
|
|
{
|
|
float score = (*scores)[i];
|
|
float prob = score / fitnessTotal + prevProb;
|
|
probabilities[i] = prob;
|
|
prevProb = prob;
|
|
}
|
|
|
|
float x = RANDF();
|
|
int best = 0;
|
|
for (int i = 0; i < size; i++)
|
|
{
|
|
float prob = probabilities[i];
|
|
if (x < prob)
|
|
return (*population)[i];
|
|
}
|
|
// This should never happen, but just in case
|
|
std::cout << "This should never be reached\n";
|
|
return (*population)[0];
|
|
}
|
|
|
|
void crossover(float rCross, Solution* p1, Solution* p2, Solution** c1, Solution** c2)
|
|
{
|
|
*c1 = p1;
|
|
*c2 = p2;
|
|
}
|
|
|
|
void mutator(Solution* individual, float rMut)
|
|
{
|
|
// For each assignment, loop through the missions, roll for each, if over rMut, pick a suitable mission to swap with
|
|
for (Assignment* assignment : *(individual->getAssignments()))
|
|
{
|
|
std::cout << "Mutating assignment for employee: " << assignment->employee->id << "\n";
|
|
for (int i = 0; i < assignment->missions.size(); i++)
|
|
{
|
|
float x = RANDF();
|
|
std::cout << i << ": " << x << " > " << rMut << "\n";
|
|
if (x < rMut)
|
|
{
|
|
std::cout << "Mutating assignment mission " << i << "\n";
|
|
assignment->replaceMission(i, individual->getUnassignedMissions());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
Solutions* generateSolutions(int nPop, Distances& distances, Centers& centers, Employees& employees, Missions& missions)
|
|
{
|
|
std::cout << "Generating " << nPop << " solutions\n";
|
|
Solutions* solutions = new Solutions {};
|
|
for (int i = 0; i < nPop; i++)
|
|
{
|
|
Solution* solution = generateSolution(distances, centers, employees, missions);
|
|
//solution->print();
|
|
solutions->push_back(solution);
|
|
}
|
|
return solutions;
|
|
}
|
|
|
|
Solution* generateSolution(Distances& distances, Centers& centers, Employees& employees, Missions& missions)
|
|
{
|
|
// TODO: randomise
|
|
// Maybe instead iterate over missions and pick a random employee to give it to?
|
|
|
|
MissionList* remainingMissions = missions.copy();
|
|
Solution* solution = new Solution { missions.size(), remainingMissions };
|
|
for (Employee* employee : employees)
|
|
{
|
|
std::cout << "Creating assignment for employee " << employee->id << "\n";
|
|
Assignment* assignment = new Assignment { employee, &distances };
|
|
for (int day = 1; day < 6; day++)
|
|
{
|
|
//for (Mission* mission : *remainingMissions)
|
|
for(int index = 0; index < remainingMissions->size(); index++)
|
|
{
|
|
Mission* mission = (*remainingMissions)[index];
|
|
if (mission->day != day)
|
|
continue;
|
|
|
|
Mission* lastMission = assignment->getLastMission(day);
|
|
if (lastMission != nullptr && lastMission->end > mission->start)
|
|
continue;
|
|
float dailyWorkTime = assignment->getWorkTime(day);
|
|
float workTime = assignment->getTotalWorkTime(mission);
|
|
if (mission->profession == employee->profession
|
|
&& Employee::MaxWorkTime >= dailyWorkTime
|
|
&& Employee::MaxWorkTimeWeek >= workTime)
|
|
{
|
|
//std::cout << "Adding mission " << mission->id << "\n";
|
|
assignment->addMission(mission);
|
|
remainingMissions->erase(remainingMissions->begin() + index);
|
|
index--;
|
|
}
|
|
}
|
|
}
|
|
|
|
solution->addAssignment(assignment);
|
|
}
|
|
|
|
return solution;
|
|
|
|
}
|