Simulated Annealing algorithm | C++ code

over 5 years ago

#How whe Simulated Annealing algorithm works

The idea behind the Simulated Annealing algorithm used to solve the “More Pizza” problem is very simple: starting from a randomly chosen solution, this is further improved until a stop condition is met (e.g., the maximum score is achieved).

The description of the “More Pizza” algorithm have been provided in a previous post.

In this article, the C++ code implementation of that algorithm is provided and commented.

#The main() function

Independently of the used approach, it is expected that these steps are followed:
  1. Reading of the input parameters (input file name, specific procedure parameters, etc. ).
  2. Reading of input data and storage of the information in a proper data structure.
  3. The solving procedure.
  4. Validation of the result (optional, as it is done by Google itself)
  5. Output of the solution to a file (if not already done in the solving procedure).
A set of global variables have been defined at the very top of the script:
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <algorithm>
#include <chrono>
#include <map>
#include <set>
#include <random>
#include <memory>

using namespace std;

// Default input and output filenames 
string iFileName{ "..\a_example.in" };
string oFileName{ "..\a_example.out" };

// Max number of slices (capacity)
int32_t M{ 0 };

// Number available of pizzas
int32_t N{ 0 };

using TSol = vector<int32_t>; // it is used for both pizzas and solution
TSol pizzas{}; // storing number of slices of each pizza

// Random numbers generator
std::random_device rd;
std::mt19937 g(rd());
std::uniform_int_distribution<int32_t> rnProbValue(0, 100);
std::uniform_real_distribution<double> rnProb(0, 1);
// Number of neighbors to check
int32_t numMaxItemsToCheck{ 5u };
double T0 = 10000.0; // Default initial temperature
double ae = 0.000001; // Default reduction factor
The structure of the main() function replicates the steps listed above.
int main(int argc, char* argv[])
{
    // Reading input parameters
    for (int i = 0; i < argc; i++)
    {
        cout << "argv[" << i << "] = " << argv[i] << endl;
    }
    if (argc > 1)
    {
        iFileName = argv[1];
        oFileName = iFileName;
        oFileName.append(".out");
        if (argc >= 3)
        {
            T0 = std::atof(argv[2]);
            if (argc >= 4)
            {
                numMaxItemsToCheck = std::atoi(argv[3]);
                if (argc >= 5)
                {
                    ae = std::atof(argv[4]);
                }
            }
        }
    }
    std::cout << "input file: " << iFileName << endl;
    std::cout << "output file: " << oFileName << endl;
    std::cout << "initial temperature: " << T0 << endl;
    std::cout << "alpha exp: " << ae << endl;
    std::cout << "num neighbours: " << numMaxItemsToCheck << endl;
    // Read data from input file
    readInput();
    auto start = chrono::steady_clock::now();
    orderPizzas();
    auto end = chrono::steady_clock::now();
    cout << "Elapsed time in milliseconds : "
        << chrono::duration_cast<chrono::milliseconds>(end - start).count()
        << " ms" << endl;
    return 0;
}
The validation of the solution and the saving of the solution to a file are part of the procedure itself. In fact, it is expected that each calculated solution is also valid. In addition, if the new solution improves the current result, then it should be saved to a file too.

#Reading data from a file

The following code has been implemented to read the input file. The first two integers represent the maximum number of slices (i.e., the maximum score) and the number of pizzas respectively. The remaining N integers represent the number of each pizza.
// Read input data from a file
void readInput()
{
    ifstream iFile;
    iFile.open(iFileName);
    iFile >> M; // Maximum number of slices (i.e., max score)
    iFile >> N; // Number of pizzas
    std::cout << "Max. num. slices: " << M << endl;
    std::cout << "N. pizzas: " << N << endl;
    //init pizzas vector
    pizzas.reserve(N);
    for (int32_t i = 0; i < N; i++)
    {
        int32_t tmpPizza{};
        iFile >> tmpPizza;
        pizzas.emplace_back(tmpPizza);
    }
    iFile.close();
    pizzas.shrink_to_fit();
    std::cout << "DONE!" << endl;
}

#The solution class

I preferred to represent the solution inside a class to:
  • store the actual solution.
  • store the score of the solution.
  • save the “state” of a solution (e.g., which pizzas can be added to the current solution).
  • implement utility functions to: -- create an empty solution (i.e., having zero score). -- create a random solution. -- change the current solution in a random way (please, check mutate() function). -- calculate the score of the solution (please, check score() function). -- save the solution into a file with the required format (please, check the function saveSolutionToFile() ). -- print the current solution to screen
// Container used to store the solution
// including its "state". The state of
// a solution is required to quickly change it
// and generate a new "neighbour"
struct TSolCnt
{
    // This constructor is used to create an empty solution
    TSolCnt() : sol{}, score{0}, availablePizzas(N,0)
    {
        // Initially, all pizzas are available
        for (int i = 0; i < N; i++)
        {
            availablePizzas[i]=i;
        }
    }
    // Constructor used to make an initial random solution
    TSolCnt( std::uniform_int_distribution<>& rnPizzaID ) :
        sol(), // empty solution
        score(0), // initial score
        availablePizzas(N,0)
    {
        // Initially, all pizzas are available
        for (int i = 0; i < N; i++)
        {
            availablePizzas[i] = i;
        }
        // Pizzas that cannot be used will be stored at the end of this vector
        std::sort(availablePizzas.begin(), availablePizzas.end(),[&](int a, int b)
        {
            return (M - (score + pizzas[a])) > (M - (score + pizzas[b]));
        });
        while ( score < M &&
            ( !availablePizzas.empty() && ( M - ( score + pizzas[availablePizzas.front()]))>0 ))
        {
            auto it_end = std::find_if(availablePizzas.cbegin(),
                availablePizzas.cend(),
                [&](int idx) {
                    return (M - (score + pizzas[idx])) < 0;
                });
            const auto numElToCheck = std::distance(availablePizzas.cbegin(), it_end);
            const int32_t avIdx = rnPizzaID(g) % numElToCheck;
            const auto searchIt = std::next(availablePizzas.cbegin(), avIdx);
            const int32_t searchIdx = *searchIt;
            if ( (pizzas[searchIdx] + score) <= M)
            {
                sol.emplace_back(searchIdx);
                score += pizzas[searchIdx];
                availablePizzas.erase(searchIt);
                cout << "searchIdx: " << searchIdx << ", score: " << score << "tr";
                // Sorting indexes by our criteria in descending order
                std::sort(availablePizzas.begin(), availablePizzas.end(), [&](int a, int b)
                {
                    return (M - (score + pizzas[a])) > (M - (score + pizzas[b]));
                });
                // Sorting indexes
                std::sort(sol.begin(), sol.end());
            }
        }
    }
    TSolCnt& operator= (const TSolCnt& rhs)
    {
        score = rhs.score;
        sol.assign(rhs.sol.begin(), rhs.sol.end());
        availablePizzas = rhs.availablePizzas;
        return *this;
    };
    void mutate(
        std::uniform_int_distribution<int32_t>& rnPizzaID,
        const int32_t maxNumRemovals)
    {
        const int32_t oldSize = static_cast<int32_t>(sol.size());
        int32_t numRemovals = 0;
        do
        {
            // find a pizza to remove
            const int32_t searchIdx = sol[rnPizzaID(g) % sol.size()];
            // update set of available pizzas
            availablePizzas.emplace_back(searchIdx);
            // update solution and its score
            score -= pizzas[searchIdx];
            auto it = std::find(sol.cbegin(), sol.cend(), searchIdx);
            sol.erase(it);
            // increase counter
            ++numRemovals;
        } while (!sol.empty()
            && (sol.size() == oldSize || numRemovals < maxNumRemovals));
        // Sorting indexes by our criteria in descending order
        std::sort(availablePizzas.begin(), availablePizzas.end(), [&](int a, int b)
        {
            return (M - (score + pizzas[a])) >(M - (score + pizzas[b]));
        });
        // adding slices to fill the order
        while (score < M &&
            (!availablePizzas.empty() && ( M - ( score + pizzas[availablePizzas.front() ] ) ) >=0 ) )
        {
            const int32_t avIdx = rnPizzaID(g) % availablePizzas.size();
            const auto searchIt = std::next(availablePizzas.cbegin(), avIdx);
            const int32_t searchIdx = *searchIt;
            if ((pizzas[searchIdx] + score) <= M)
            {
                sol.emplace_back(searchIdx);
                score += pizzas[searchIdx];
                availablePizzas.erase(searchIt);
                // Sorting indexes by our criteria in descending order
                std::sort(availablePizzas.begin(), availablePizzas.end(), [&](int a, int b)
                {
                    return (M - (score + pizzas[a])) > (M - (score + pizzas[b]));
                });
                // Sorting indexes
                std::sort(sol.begin(), sol.end());
            }
        }
    }
    // Helper function to print the solution
    void print()
    {
        std::cout << "Ordering " << sol.size() << " pizzas:" << std::endl;
        int32_t score = 0;
        for (auto item : sol)
        {
            std::cout << item << " ";
            score += pizzas[item];
        }
        cout << endl;
        cout << "Score: " << score << endl;
    }
    // Save solution to a file.
    // The name of the file is SCENARIO + "_" + SCORE + ".txt"
    void saveSolutionToFile()
    {
        // print to a file
        ofstream outFile;
        outFile.open(iFileName + "_" + to_string(score) + ".out");
        outFile << sol.size() << endl;
        int x = 0;
        for (auto& item : sol)
        {
            outFile << item;
            if (x < static_cast<int32_t>(sol.size() - 1))
            {
                outFile << " ";
            }
            ++x;
        }
        outFile << endl;
        outFile.close();
    }
    TSol sol; // vector of indexes of the pizzas
    int32_t score; // score of the current solution
    // Set storing the indexes of the available pizzas
    // This vector will be always sorted in a descending order
    // by the difference <M - (score + pizzas[index])>, where index
    // refers to each value stored inside this vector.
    TSol availablePizzas;
};

#How to quickly calculate new solutions?

The Simulated Annealing is – by its nature – a slow algorithm. Despite that, it is easy to adapt the code proposed in this article to solve many NP-hard problems.
The implementation of this algorithm has been done having performance always in mind.
A bottleneck is – at each step – the choice of pizzas to replace and to add to the current solution.

For this reason, in the mutate() function, after a certain number of pizzas have been randomly removed, then only the pizzas that can effectively contribute to the solution are considered in the selection process.

For this reason, the vector of available pizzas is sorted so that the pizzas that can fit better inside the solution are placed at the front of the vector.
If no pizzas can be selected, then a score greater than the maximum value is achieved if the first at the front of the vector is chosen.

#The orderPizzas() function

As described in this post, the used algorithm is a “Simulated Annealing” procedure. Please, find the implementation of that algorithm below:

// Using smart pointers, it is straightforward and faster
// to replace local solution with a better solution.
using TSolPtr = std::shared_ptr<TSolCnt>;
void orderPizzas()
{
    const int32_t numEl{ static_cast<int32_t>( pizzas.size() ) };
    // This is used to choose randomly a pizza to order or remove
    std::uniform_int_distribution<int32_t> rnPizzaID(0, numEl - 1);
    // Counters 
    int32_t numItBetweenImprovement = 0;
    int32_t cntIters{ 0 };
    cout << "...ok! Ready to start..." << endl;
    // make local initial solution
    TSolPtr localSol = std::make_shared<TSolCnt>(rnPizzaID);
    localSol->saveSolutionToFile();
    // this is the best solution until now found... so let's save it!
    TSolPtr savedSol = std::make_shared<TSolCnt>(*localSol);
    // Print some data...
    cout << "Initial score: " << savedSol->score << "tsize: " << savedSol->sol.size() << endl;
#ifdef _DEBUG
    savedSol->print();
#endif // _DEBUG
    double temperature = T0; // initialize temperature
    while (savedSol->score<M && numItBetweenImprovement < 500'000)
    {
        // Calculating new temperature
        ++cntIters;
        temperature = T0 * exp(-ae * cntIters);
        // Calculating the number of pizzas to be replaced
        const auto maxNumRemovals = 1 + numItBetweenImprovement / 100;
        // creating new solutions from current one!
        TSolPtr bestNb = std::make_shared<TSolCnt>(); // with zero score
        for (auto i = 0; i < numMaxItemsToCheck; i++)
        {
            // Creating a copy of the local solution
            TSolPtr nb = std::make_shared<TSolCnt>(*localSol);
            if (nb)
            {
                // Mutate the new solution
                nb->mutate(rnPizzaID, maxNumRemovals);
                // Checking if the new solution is better
                if (nb->score > bestNb->score )
                {
                    bestNb.swap(nb);
                }
            }
        }
        const int32_t deltaEnergy = bestNb->score - savedSol->score;
        // If a better solution is found
        if (bestNb->score>0 && deltaEnergy > 0)
        {
            savedSol.swap(bestNb);
            localSol = std::make_shared<TSolCnt>(*savedSol);
            savedSol->saveSolutionToFile();
            cout << "New max score: " << savedSol->score << "tsize: " << savedSol->sol.size();
            cout << "tTemperature: " << temperature;
            cout << "tdelta: " << deltaEnergy;
            cout << "tnumItBetweenImprovements: " << numItBetweenImprovement << endl;
            numItBetweenImprovement = 0;
#ifdef _DEBUG
            savedSol->print();
#endif // _DEBUG
        }
        else
        {
            // check if it can be accepted
            const auto probAccept = 1.0 / (1 + exp(-deltaEnergy / temperature));
            if (rnProb(g) < probAccept) // accepted
            {
                localSol.swap(bestNb);
                cout << "Accepted lower score: " << localSol->score << "tsize: " << localSol->sol.size();
            }
            else
            {
                // Reset local solution to be equal to the best one found until now
                localSol = std::make_shared<TSolCnt>(*savedSol);
                cout << "Unaccepted lower score: " << bestNb->score << "tsize: " << bestNb->sol.size();
            }
            cout << "tTemperature: " << temperature;
            cout << "tProbAccept: " << probAccept;
            cout << "tnumIts: " << numItBetweenImprovement << "r";
            ++numItBetweenImprovement;
        }
    }
#ifdef _DEBUG
    savedSol->print();
#endif // _DEBUG
}
Every time a better solution is found, it is also saved to a file. In this way, it is possible to commit as soon as possible a new solution to the Google Hash Code Judge System.

#The console output

The algorithm has been executed for all the unsolved test scenarios, as shown in the following images.

more pizza small console output Small more pizza medium console output Medium more pizza quite big console output Quite big more pizza also big console output Also Big

#Results for each test scenario

In the following table, the average execution time for each scenario is shown:
ScenarioAverage execution time
Small< 1s
Medium< 1s
Quite Big1-2 minutes
Also Big< 1s

#The complete code

// Author: Pietro L. Carotenuto
// Website: https://www.pietrolc.com
#include "stdafx.h"
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <algorithm>
#include <chrono>
#include <map>
#include <set>
#include <random>
#include <memory>

using namespace std;

// Default input and output filenames 
string iFileName{ "..\a_example.in" };
string oFileName{ "..\a_example.out" };

// Max number of slices (capacity)
int32_t M{ 0 };
// Number available of pizzas
int32_t N{ 0 };
using TSol = vector<int32_t>; // it is used for both pizzas and solution
TSol pizzas{}; // storing number of slices of each pizza

// Random numbers generator
std::random_device rd;
std::mt19937 g(rd());
std::uniform_int_distribution<int32_t> rnProbValue(0, 100);
std::uniform_real_distribution<double> rnProb(0, 1);

// Number of neighbours to check
int32_t numMaxItemsToCheck{ 5u };
double T0 = 10000.0; // Default initial temperature
double ae = 0.000001; // Default reduction factor

// Read input data from a file
void readInput()
{
    ifstream iFile;
    iFile.open(iFileName);
    iFile >> M;
    iFile >> N;
    std::cout << "Max num slices: " << M << endl;
    std::cout << "N. pizzas: " << N << endl;
    //init pizzas vector
    pizzas.reserve(N);
    for (int32_t i = 0; i < N; i++)
    {
        int32_t tmpPizza{};
        iFile >> tmpPizza;
        pizzas.emplace_back(tmpPizza);
    }
    iFile.close();
    pizzas.shrink_to_fit();
    std::cout << "DONE!" << endl;
}
// Container used to store the solution
// including its "state". The state of
// a solution is required to quickly change it
// and generate a new "neighbour"
struct TSolCnt
{
    TSolCnt() : sol{}, score{0}, availablePizzas(N,0)
    {
        for (int i = 0; i < N; i++)
        {
            availablePizzas[i]=i;
        }
    }

    // Constructor used to make an initial solution
    TSolCnt( std::uniform_int_distribution<>& rnPizzaID ) :
        sol(), // empty solution
        score(0), // initial score
        availablePizzas(N,0)
    {
        for (int i = 0; i < N; i++)
        {
            availablePizzas[i] = i;
        }
        // Sorting indexes by our criteria in descending order
        std::sort(availablePizzas.begin(), availablePizzas.end(),[&](int a, int b)
        {
            return (M - (score + pizzas[a])) > (M - (score + pizzas[b]));
        });
        while ( score < M &&
            ( !availablePizzas.empty() && ( M - ( score + pizzas[availablePizzas.front()]))>0 ))
        {
            auto it_end = std::find_if(availablePizzas.cbegin(),
                availablePizzas.cend(),
                [&](int idx) {
                    return (M - (score + pizzas[idx])) < 0;
                });
            const auto numElToCheck = std::distance(availablePizzas.cbegin(), it_end);
            const int32_t avIdx = rnPizzaID(g) % numElToCheck;
            const auto searchIt = std::next(availablePizzas.cbegin(), avIdx);
            const int32_t searchIdx = *searchIt;
            if ( (pizzas[searchIdx] + score) <= M)
            {
                sol.emplace_back(searchIdx);
                score += pizzas[searchIdx];
                availablePizzas.erase(searchIt);
                cout << "searchIdx: " << searchIdx << ", score: " << score << "tr";
                // Sorting indexes by our criteria in descending order
                std::sort(availablePizzas.begin(), availablePizzas.end(), [&](int a, int b)
                {
                    return (M - (score + pizzas[a])) > (M - (score + pizzas[b]));
                });
                // Sorting indexes
                std::sort(sol.begin(), sol.end());
            }
        }
    }

    TSolCnt& operator= (const TSolCnt& rhs)
    {
        score = rhs.score;
        sol.assign(rhs.sol.begin(), rhs.sol.end());
        availablePizzas = rhs.availablePizzas;
        return *this;
    };

    void mutate(
        std::uniform_int_distribution<int32_t>& rnPizzaID,
        const int32_t maxNumRemovals)
    {
        const int32_t oldSize = static_cast<int32_t>(sol.size());
        int32_t numRemovals = 0;
        do
        {
            // find a pizza to remove
            const int32_t searchIdx = sol[rnPizzaID(g) % sol.size()];
            // update set of available pizzas
            availablePizzas.emplace_back(searchIdx);
            // update solution and its score
            score -= pizzas[searchIdx];
            auto it = std::find(sol.cbegin(), sol.cend(), searchIdx);
            sol.erase(it);
            // increase counter
            ++numRemovals;
        } while (!sol.empty()
            && (sol.size() == oldSize || numRemovals < maxNumRemovals));
        // Sorting indexes by our criteria in descending order
        std::sort(availablePizzas.begin(), availablePizzas.end(), [&](int a, int b)
        {
            return (M - (score + pizzas[a])) >(M - (score + pizzas[b]));
        });
        // adding slices to fill the order
        while (score < M &&
            (!availablePizzas.empty() && ( M - ( score + pizzas[availablePizzas.front() ] ) ) >=0 ) )
        {
            const int32_t avIdx = rnPizzaID(g) % availablePizzas.size();
            const auto searchIt = std::next(availablePizzas.cbegin(), avIdx);
            const int32_t searchIdx = *searchIt;
            if ((pizzas[searchIdx] + score) <= M)
            {
                sol.emplace_back(searchIdx);
                score += pizzas[searchIdx];
                availablePizzas.erase(searchIt);
                // Sorting indexes by our criteria in descending order
                std::sort(availablePizzas.begin(), availablePizzas.end(), [&](int a, int b)
                {
                    return (M - (score + pizzas[a])) > (M - (score + pizzas[b]));
                });
                // Sorting indexes
                std::sort(sol.begin(), sol.end());
            }
        }
    }

    // Helper function to print the solution
    void print()
    {
        std::cout << "Ordering " << sol.size() << " pizzas:" << std::endl;
        int32_t score = 0;
        for (auto item : sol)
        {
            std::cout << item << " ";
            score += pizzas[item];
        }
        cout << endl;
        cout << "Score: " << score << endl;
    }

    // Save solution to a file.
    // The name of the file is SCENARIO + "_" + SCORE + ".txt"
    void saveSolutionToFile()
    {
        // print to a file
        ofstream outFile;
        outFile.open(iFileName + "_" + to_string(score) + ".out");
        outFile << sol.size() << endl;
        int x = 0;
        for (auto& item : sol)
        {
            outFile << item;
            if (x < static_cast<int32_t>(sol.size() - 1))
            {
                outFile << " ";
            }
            ++x;
        }
        outFile << endl;
        outFile.close();
    }
    TSol sol; // vector of indexes of the pizzas
    int32_t score; // score of the current solution
    // Set storing the indexes of the available pizzas
    // This vector will be always sorted in a descending order
    // by the difference <M - (score + pizzas[index])>, where index
    // refers to each value stored inside this vector.
    TSol availablePizzas;
};

// Using smart pointers, it is straightforward and faster
// to replace local solution with a better solution.
using TSolPtr = std::shared_ptr<TSolCnt>;
void orderPizzas()
{
    const int32_t numEl{ static_cast<int32_t>( pizzas.size() ) };
    // This is used to choose randomly a pizza to order or remove
    std::uniform_int_distribution<int32_t> rnPizzaID(0, numEl - 1);
    // Counters 
    int32_t numItBetweenImprovement = 0;
    int32_t cntIters{ 0 };
    cout << "...ok! Ready to start..." << endl;
    // make local initial solution
    TSolPtr localSol = std::make_shared<TSolCnt>(rnPizzaID);
    localSol->saveSolutionToFile();
    // this is the best solution until now found... so let's save it!
    TSolPtr savedSol = std::make_shared<TSolCnt>(*localSol);
    // Print some data...
    cout << "Initial score: " << savedSol->score << "tsize: " << savedSol->sol.size() << endl;
#ifdef _DEBUG
    savedSol->print();
#endif // _DEBUG
    double temperature = T0; // initialize temperature
    while (savedSol->score<M && numItBetweenImprovement < 500'000)
    {
        // Calculating new temperature
        ++cntIters;
        temperature = T0 * exp(-ae * cntIters);
        // Calculating the number of pizzas to be replaced
        const auto maxNumRemovals = 1 + numItBetweenImprovement / 100;
        // creating new solutions from current one!
        TSolPtr bestNb = std::make_shared<TSolCnt>(); // with zero score
        for (auto i = 0; i < numMaxItemsToCheck; i++)
        {
            // Creating a copy of the local solution
            TSolPtr nb = std::make_shared<TSolCnt>(*localSol);
            if (nb)
            {
                // Mutate the new solution
                nb->mutate(rnPizzaID, maxNumRemovals);
                // Checking if the new solution is better
                if (nb->score > bestNb->score )
                {
                    bestNb.swap(nb);
                }
            }
        }
        const int32_t deltaEnergy = bestNb->score - savedSol->score;
        // If a better solution is found
        if (bestNb->score>0 && deltaEnergy > 0)
        {
            savedSol.swap(bestNb);
            localSol = std::make_shared<TSolCnt>(*savedSol);
            savedSol->saveSolutionToFile();
            cout << "New max score: " << savedSol->score << "\tsize: " << savedSol->sol.size();
            cout << "\tTemperature: " << temperature;
            cout << "\tdelta: " << deltaEnergy;
            cout << "\tnumItBetweenImprovements: " << numItBetweenImprovement << endl;
            numItBetweenImprovement = 0;
#ifdef _DEBUG
            savedSol->print();
#endif // _DEBUG
        }
        else
        {
            // check if it can be accepted
            const auto probAccept = 1.0 / (1 + exp(-deltaEnergy / temperature));
            if (rnProb(g) < probAccept) // accepted
            {
                localSol.swap(bestNb);
                cout << "Accepted lower score: " << localSol->score << "\tsize: " << localSol->sol.size();
            }
            else
            {
                // Reset local solution to be equal to the best one found until now
                localSol = std::make_shared<TSolCnt>(*savedSol);
                cout << "Unaccepted lower score: " << bestNb->score << "\tsize: " << bestNb->sol.size();
            }
            cout << "\tTemperature: " << temperature;
            cout << "\tProbAccept: " << probAccept;
            cout << "\tnumIts: " << numItBetweenImprovement << "\r";
            ++numItBetweenImprovement;
        }
    }

#ifdef _DEBUG
    savedSol->print();
#endif // _DEBUG

}
int main(int argc, char* argv[])
{
    // Reading algorithm parameters
    for (int i = 0; i < argc; i++)
        cout << "argv[" << i << "] = " << argv[i] << endl;
    if (argc > 1)
    {
        iFileName = argv[1];
        oFileName = iFileName;
        oFileName.append(".out");
        if (argc >= 3)
        {
            T0 = std::atof(argv[2]);
            if (argc >= 4)
            {
                numMaxItemsToCheck = std::atoi(argv[3]);
                if (argc >= 5)
                {
                    ae = std::atof(argv[4]);
                }
            }
        }
    }

    std::cout << "input file: " << iFileName << endl;
    std::cout << "output file: " << oFileName << endl;
    std::cout << "initial temperature: " << T0 << endl;
    std::cout << "alpha exp: " << ae << endl;
    std::cout << "num neighbours: " << numMaxItemsToCheck << endl;
    // Read data from input file
    readInput();
    auto start = chrono::steady_clock::now();
    orderPizzas();
    auto end = chrono::steady_clock::now();
    cout << "Elapsed time in milliseconds : "
        << chrono::duration_cast<chrono::milliseconds>(end - start).count()
        << " ms" << endl;
    return 0;
}

#New code - using OOP design - is on Github

After five years, I've found the time to refactor this original code I used for the Google HashCode challenge. I've decided to split this large piece of code into smaller classes. The complete and updated code can be found here: SimulatedAnnealingCpp repo.

Hope this helps!

Enjoyed "Simulated Annealing algorithm | C++ code"? Get more like this!

Get practical programming tips, algorithm explanations, and tech insights delivered weekly. No spam, just valuable content.

✓ No spam ever✓ Unsubscribe anytime✓ Privacy protected