Skip to content

Commit

Permalink
Implemented HopcroftKarpAlgorithm
Browse files Browse the repository at this point in the history
  • Loading branch information
rowanG077 committed Nov 14, 2018
1 parent ebee28b commit 6e61564
Show file tree
Hide file tree
Showing 10 changed files with 336 additions and 18 deletions.
2 changes: 1 addition & 1 deletion src/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# build code as object library for easier linking to tests
add_library(${PROJECT_NAME}_obj OBJECT
Box.cpp Parser.cpp Algorithm.cpp)
Box.cpp Parser.cpp KuhnAlgorithm.cpp HopcroftKarpAlgorithm.cpp)

add_executable(${PROJECT_NAME}
main.cpp
Expand Down
145 changes: 145 additions & 0 deletions src/HopcroftKarpAlgorithm.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
#include "HopcroftKarpAlgorithm.hpp"

#include <algorithm>
#include <queue>

/**
* @brief Contains code for the boxnesting problem
*/
namespace BoxNesting
{
HopcroftKarpAlgorithm::HopcroftKarpAlgorithm(const std::vector<Box>& boxes)
{
// In the boxes case left and right vertices are the same amount since
// they are the amount of boxes
this->leftVerticesCount = static_cast<int16_t>(boxes.size());
this->rightVerticesCount = this->leftVerticesCount;

this->NILL = this->leftVerticesCount;

this->pairsLeft = std::vector<int16_t>(this->leftVerticesCount + 1, this->NILL);
this->pairsRight = std::vector<int16_t>(this->rightVerticesCount + 1, this->NILL);
this->distances = std::vector<int16_t>(this->leftVerticesCount + 1, 0);

this->graph.resize(this->leftVerticesCount);

for (int16_t i = 0; i < this->leftVerticesCount; ++i) {
this->graph[i].reserve(this->rightVerticesCount);
for (int16_t j = 0; j < this->rightVerticesCount; ++j) {
if (boxes[i] < boxes[j]) {
this->graph[i].emplace_back(j);
}
}
this->graph[i].shrink_to_fit();
}
}

int16_t HopcroftKarpAlgorithm::runAlgorithm() const
{
// We have the same amount of visible boxes as there
// are vertices in the left part of the bipartite graph
// for each valid match (i.e. a box can nest inside another box)
// we remove a visible box
int16_t visibleBoxes = this->leftVerticesCount;

while (bfs())
{
// Find a free vertex
for (int16_t i = 0; i < leftVerticesCount; ++i)
{
if (pairsLeft[i] == this->NILL && this->dfs(i))
{
--visibleBoxes;
}
}
}

return visibleBoxes;
}

bool HopcroftKarpAlgorithm::dfs(int16_t vertex) const
{
if (vertex == this->NILL)
{
return true;
}

auto sz = this->graph[vertex].size();
for (int16_t j = 0; j < sz; ++j)
{
// Follow the distances set by BFS
if (this->distances[this->pairsRight[j]] == this->distances[vertex] + 1)
{
// If dfs for pair of v also returns
// true
if (dfs(this->pairsRight[j]))
{
this->pairsRight[j] = vertex;
this->pairsLeft[vertex] = j;
return true;
}
}
}

// If there is no augmenting path beginning with u.
this->distances[vertex] = this->INFINITE;
return false;
}

bool HopcroftKarpAlgorithm::bfs() const
{
std::queue<int16_t> queue;

// First layer of vertices (set distance as 0)
for (int16_t i = 0; i < this->leftVerticesCount; ++i)
{
// If this is a free vertex, add it to queue
if (this->pairsLeft[i] == this->NILL)
{
this->distances[i] = 0;
queue.push(i);
}
// Else set distance as infinite
// so that this vertex
// is considered next time
else
{
this->distances[i] = this->INFINITE;
}
}

// Initialize distance to NIL as infinite
this->distances[this->NILL] = this->INFINITE;

// the queue can only contain vertices off the left part of the bipartite graph
while (!queue.empty())
{
// Dequeue a vertex
auto i = queue.front();
queue.pop();

// If this node is not NIL and can provide a shorter path to NIL
if (this->distances[i] < this->distances[this->NILL])
{
// Get all adjacent vertices of the dequeued vertex u
auto sz = this->graph[i].size();
for (int16_t j = 0; j < sz; ++j)
{
// If pair of v is not considered so far
// (v, pairV[V]) is not yet explored edge.
if (this->distances[this->pairsRight[j]] == this->INFINITE)
{
// Consider the pair and add it to queue
this->distances[this->pairsRight[j]] = this->distances[i] + 1;
queue.push(this->pairsRight[j]);
}
}
}
}

// If we could come back to NIL using alternating path of distinct
// vertices then there is an augmenting path
return this->distances[this->NILL] != this->INFINITE;
}

} // namespace BoxNesting
97 changes: 97 additions & 0 deletions src/HopcroftKarpAlgorithm.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
#pragma once

#include "Box.hpp"

#include <cstdint>
#include <limits>
#include <vector>

/**
* @brief Contains code for the boxnesting problem
*/
namespace BoxNesting
{
/**
* @brief class that contains the boxnesting algorithm
*/
class HopcroftKarpAlgorithm
{
public:
/**
* @brief Deleted default constructor
*/
HopcroftKarpAlgorithm() = delete;

/**
* @brief Construct a new Algorithm object
*
* @param boxes The boxes to generate the internal graph for
*/
explicit HopcroftKarpAlgorithm(const std::vector<Box>& boxes);

/**
* @brief function that runs the boxNesting algorithm and returns the number
* of visible boxes
*
* @param boxes the set of boxes to be nested
* @return the number of visible boxes after running the nesting
* algorithm
*/
[[nodiscard]] int16_t runAlgorithm() const;

private:
bool bfs() const;
bool dfs(int16_t vertex) const;

private:
/**
* @brief adjacencyList of left vertices generated from the boxes
* First index on vector is a vertex on the left side of the bipartite graph
* and the vector on that index contains indices of vertex on the right side of
* of the graph where an edge exists between them.
*/
std::vector<std::vector<int16_t>> graph;

/**
* @brief The amount of vertices in the left side of the bipartite graph
*/
int16_t leftVerticesCount;

/**
* @brief The amount of vertices in the left side of the bipartite graph
*/
int16_t rightVerticesCount;

/**
* @brief The index of the NILL node in the left part of the bipartite graph
*/
int16_t NILL; // NOLINT

/**
* @brief Used as marker for infinite distance
*/
int16_t INFINITE = std::numeric_limits<int16_t>::max(); // NOLINT

/**
* @brief Contains whether vertices on the left side of the bipartite graph are
* currently part of a match. Where the index is the vertex index and the
* the value is the index of the vertex in the right side of the graph that
* is matched to it. -1 indicates no match
*/
mutable std::vector<int16_t> pairsLeft;

/**
* @brief Contains whether vertices on the right side of the bipartite graph are
* currently part of a match. Where the index is the vertex index and the
* the value is the index of the vertex in the left side of the graph that
* is matched to it. -1 indicates no match
*/
mutable std::vector<int16_t> pairsRight;

/**
* @brief Stores distances of left vertices
*/
mutable std::vector<int16_t> distances;
};

} // namespace BoxNesting
8 changes: 4 additions & 4 deletions src/Algorithm.cpp → src/KuhnAlgorithm.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#include "Algorithm.hpp"
#include "KuhnAlgorithm.hpp"

#include <algorithm>

Expand All @@ -7,7 +7,7 @@
*/
namespace BoxNesting
{
Algorithm::Algorithm(const std::vector<Box>& boxes)
KuhnAlgorithm::KuhnAlgorithm(const std::vector<Box>& boxes)
{
// In the boxes case left and right vertices are the same amount since
// they are the amount of boxes
Expand All @@ -30,7 +30,7 @@ Algorithm::Algorithm(const std::vector<Box>& boxes)
}
}

int16_t Algorithm::runAlgorithm() const
int16_t KuhnAlgorithm::runAlgorithm() const
{
bool pathFound;
do {
Expand Down Expand Up @@ -58,7 +58,7 @@ int16_t Algorithm::runAlgorithm() const
return visibleBoxes;
}

bool Algorithm::kuhn(int16_t vertex) const
bool KuhnAlgorithm::kuhn(int16_t vertex) const
{
if (this->used[vertex]) {
return false;
Expand Down
6 changes: 3 additions & 3 deletions src/Algorithm.hpp → src/KuhnAlgorithm.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,20 +13,20 @@ namespace BoxNesting
/**
* @brief class that contains the boxnesting algorithm
*/
class Algorithm
class KuhnAlgorithm
{
public:
/**
* @brief Deleted default constructor
*/
Algorithm() = delete;
KuhnAlgorithm() = delete;

/**
* @brief Construct a new Algorithm object
*
* @param boxes The boxes to generate the internal graph for
*/
explicit Algorithm(const std::vector<Box>& boxes);
explicit KuhnAlgorithm(const std::vector<Box>& boxes);

/**
* @brief function that runs the boxNesting algorithm and returns the number
Expand Down
5 changes: 3 additions & 2 deletions src/main.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include "Algorithm.hpp"
#include "HopcroftKarpAlgorithm.hpp"
#include "KuhnAlgorithm.hpp"
#include "Parser.hpp"
#include "version.hpp"

Expand Down Expand Up @@ -73,7 +74,7 @@ int main(int argc, char** argv)
return EXIT_SUCCESS;
}

BoxNesting::Algorithm boxNestingAlgorithm(BoxNesting::Parser::getBoxes(std::cin));
BoxNesting::HopcroftKarpAlgorithm boxNestingAlgorithm(BoxNesting::Parser::getBoxes(std::cin));

std::cout << boxNestingAlgorithm.runAlgorithm() << std::endl;

Expand Down
2 changes: 1 addition & 1 deletion src/version.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
/** Defines the project author */
#define BOXNESTING_AUTHOR "Ties Klappe & Rowan Goemans"
/** Defines the human-readable project version string (e.g. a.b.c.d-123+) */
#define BOXNESTING_VERSION "1.0.0-a56672a+"
#define BOXNESTING_VERSION "1.0.0-ebee28b+"
/** Defines the project major version number */
#define BOXNESTING_VERSION_MAJOR 1
/** Defines the project major version number */
Expand Down
2 changes: 1 addition & 1 deletion test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ add_library(test_obj OBJECT

# add all tests on this level separately
foreach(file
Box Parser Algorithm)
Box Parser KuhnAlgorithm HopcroftKarpAlgorithm)
add_executable("test_${file}"
"${file}.cpp"
$<TARGET_OBJECTS:${PROJECT_NAME}_obj>
Expand Down
Loading

0 comments on commit 6e61564

Please sign in to comment.