Initial commit
This commit is contained in:
commit
26f1a0bd5a
16
CMakeLists.txt
Normal file
16
CMakeLists.txt
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.10)
|
||||||
|
|
||||||
|
project(
|
||||||
|
knapsack
|
||||||
|
LANGUAGES CXX)
|
||||||
|
|
||||||
|
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
|
||||||
|
|
||||||
|
include_directories(${knapsack_SOURCE_DIR}/src)
|
||||||
|
|
||||||
|
add_subdirectory(src)
|
||||||
|
|
||||||
|
if("${CMAKE_BUILD_TYPE}" STREQUAL "Debug")
|
||||||
|
add_subdirectory(test)
|
||||||
|
endif()
|
||||||
|
|
24
Readme.md
Normal file
24
Readme.md
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
# Knapsack
|
||||||
|
|
||||||
|
### Author: Gregory Crawford
|
||||||
|
|
||||||
|
## Compiling the Project
|
||||||
|
In order to compile the project, simply run these two commands:
|
||||||
|
|
||||||
|
cmake -B build -S .
|
||||||
|
cmake --build build
|
||||||
|
|
||||||
|
## Running the Project
|
||||||
|
The program should now be compiled at ./build/bin/knapsack
|
||||||
|
|
||||||
|
Simply run the program using:
|
||||||
|
|
||||||
|
build/bin/knapsack <file>
|
||||||
|
|
||||||
|
## Options
|
||||||
|
The second argument of the program is used as the input file.
|
||||||
|
|
||||||
|
EX:
|
||||||
|
|
||||||
|
build/bin/knapsack file/to/use.txt
|
||||||
|
|
5
src/CMakeLists.txt
Normal file
5
src/CMakeLists.txt
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
add_executable(knapsack
|
||||||
|
./main.cpp
|
||||||
|
./knapsack.cpp
|
||||||
|
)
|
||||||
|
target_include_directories(knapsack PUBLIC ${CMAKE_CURRENT_LIST_DIR})
|
102
src/knapsack.cpp
Normal file
102
src/knapsack.cpp
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
#include "knapsack.hpp"
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <iostream>
|
||||||
|
#include <fstream>
|
||||||
|
|
||||||
|
namespace knapsack
|
||||||
|
{
|
||||||
|
|
||||||
|
void CheckArgumentAmount(int argc)
|
||||||
|
{
|
||||||
|
if (argc == 2) { return; }
|
||||||
|
std::cerr << "usage: knapsack path/to/file.txt" << std::endl;
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SortByItemNumber(PossibleObject* lhs, PossibleObject* rhs)
|
||||||
|
{
|
||||||
|
return lhs->itemNumber < rhs->itemNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
void User::ReadNewFile(const char* inputFilePath)
|
||||||
|
{
|
||||||
|
// Open file
|
||||||
|
std::ifstream inputFile(inputFilePath);
|
||||||
|
if (!inputFile.is_open())
|
||||||
|
{
|
||||||
|
std::cerr << "Error opening file: " << inputFilePath << std::endl;
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
// Read in number of items
|
||||||
|
inputFile >> itemCount;
|
||||||
|
// Read in backpack limit
|
||||||
|
inputFile >> limit;
|
||||||
|
// Read in weight of item, then value of item for all items
|
||||||
|
int toBeStoredWeight, toBeStoredValue;
|
||||||
|
for (int i = 0; inputFile >> toBeStoredWeight >> toBeStoredValue; ++i)
|
||||||
|
{ choices.emplace_back(toBeStoredWeight, toBeStoredValue, i); }
|
||||||
|
}
|
||||||
|
|
||||||
|
void User::InputSafety(void)
|
||||||
|
{
|
||||||
|
if (itemCount == choices.size()) { return; }
|
||||||
|
std::cout << "Warning! The given object amount differs from amount read in!";
|
||||||
|
std::cout << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void User::RunKnapsack(void)
|
||||||
|
{
|
||||||
|
int knapsackMatrix[itemCount + 1][limit + 1];
|
||||||
|
|
||||||
|
// Build knapsack matrix
|
||||||
|
for (int i = 0; i <= itemCount; i++)
|
||||||
|
{
|
||||||
|
for (int w = 0; w <= limit; w++)
|
||||||
|
{
|
||||||
|
if (i == 0 || w == 0) { knapsackMatrix[i][w] = 0; }
|
||||||
|
else if (choices.at(i - 1).weight <= w)
|
||||||
|
{
|
||||||
|
knapsackMatrix[i][w] = std::max(choices.at(i - 1).value +
|
||||||
|
knapsackMatrix[i - 1][w - choices.at(i - 1).weight],
|
||||||
|
knapsackMatrix[i - 1][w]);
|
||||||
|
}
|
||||||
|
else { knapsackMatrix[i][w] = knapsackMatrix[i - 1][w]; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store knapsack result
|
||||||
|
int bestTotal = knapsackMatrix[itemCount][limit];
|
||||||
|
int w = limit;
|
||||||
|
for (int i = itemCount; i > 0 && bestTotal > 0; i--)
|
||||||
|
{
|
||||||
|
if (bestTotal == knapsackMatrix[i - 1][w]) { continue; }
|
||||||
|
solution.push_back(&choices.at(i - 1));
|
||||||
|
bestTotal -= choices.at(i - 1).value;
|
||||||
|
w -= choices.at(i - 1).weight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void User::PrintResult(void)
|
||||||
|
{
|
||||||
|
std::sort(solution.begin(), solution.end(), SortByItemNumber);
|
||||||
|
std::cout << "Items used (starting from 1):" << std::endl << '\t';
|
||||||
|
for (auto it : solution) { std::cout << it->itemNumber + 1 << ' '; }
|
||||||
|
std::cout << std::endl;
|
||||||
|
std::cout << "Resulting value: " << GetSolutionValue() << std::endl;
|
||||||
|
}
|
||||||
|
int User::GetSolutionValue(void)
|
||||||
|
{
|
||||||
|
int totalValue = 0;
|
||||||
|
for (auto it : solution) { totalValue += it->value; }
|
||||||
|
return totalValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
PossibleObject::PossibleObject(int inputWeight, int inputValue, int i)
|
||||||
|
{
|
||||||
|
weight = inputWeight;
|
||||||
|
value = inputValue;
|
||||||
|
itemNumber = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
38
src/knapsack.hpp
Normal file
38
src/knapsack.hpp
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
#ifndef KNAPSACK_HPP
|
||||||
|
#define KNAPSACK_HPP
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace knapsack
|
||||||
|
{
|
||||||
|
void CheckArgumentAmount(int argc);
|
||||||
|
|
||||||
|
struct PossibleObject
|
||||||
|
{
|
||||||
|
PossibleObject(int inputWeight, int inputValue, int i);
|
||||||
|
int itemNumber;
|
||||||
|
int weight;
|
||||||
|
int value;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool SortByItemNumber(PossibleObject* lhs, PossibleObject* rhs);
|
||||||
|
|
||||||
|
class User
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void ReadNewFile(const char* inputFilePath);
|
||||||
|
void InputSafety(void);
|
||||||
|
void RunKnapsack(void);
|
||||||
|
void PrintResult(void);
|
||||||
|
int GetSolutionValue(void);
|
||||||
|
void _TestFileReadIn(std::vector<PossibleObject> testChoices);
|
||||||
|
void _TestKnapsack(void);
|
||||||
|
private:
|
||||||
|
std::vector<PossibleObject> choices;
|
||||||
|
std::vector<PossibleObject*> solution;
|
||||||
|
int limit;
|
||||||
|
int itemCount;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
13
src/main.cpp
Normal file
13
src/main.cpp
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
#include "knapsack.hpp"
|
||||||
|
|
||||||
|
int main(int argc, char* argv[])
|
||||||
|
{
|
||||||
|
knapsack::CheckArgumentAmount(argc);
|
||||||
|
knapsack::User newUser;
|
||||||
|
newUser.ReadNewFile(argv[1]);
|
||||||
|
newUser.InputSafety();
|
||||||
|
newUser.RunKnapsack();
|
||||||
|
newUser.PrintResult();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
8
test/CMakeLists.txt
Normal file
8
test/CMakeLists.txt
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
find_package(unity REQUIRED)
|
||||||
|
include_directories("${CMAKE_CURRENT_SOURCE_DIR}")
|
||||||
|
add_executable(testing
|
||||||
|
./testing.cpp
|
||||||
|
../src/knapsack.cpp
|
||||||
|
)
|
||||||
|
set_target_properties(testing PROPERTIES LINKER_LANGUAGE CXX)
|
||||||
|
target_link_libraries(testing unity)
|
5
test/test1.txt
Normal file
5
test/test1.txt
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
4 6
|
||||||
|
2 4
|
||||||
|
1 5
|
||||||
|
2 1
|
||||||
|
3 2
|
4
test/test2.txt
Normal file
4
test/test2.txt
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
3 4
|
||||||
|
4 1
|
||||||
|
5 2
|
||||||
|
1 3
|
4
test/test3.txt
Normal file
4
test/test3.txt
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
3 3
|
||||||
|
4 1
|
||||||
|
5 2
|
||||||
|
6 3
|
90
test/testing.cpp
Normal file
90
test/testing.cpp
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
#include <unity/unity.h>
|
||||||
|
#include <unity/unity_internals.h>
|
||||||
|
#include <vector>
|
||||||
|
#include "knapsack.hpp"
|
||||||
|
|
||||||
|
// Function is defined here for testing
|
||||||
|
namespace knapsack
|
||||||
|
{
|
||||||
|
void User::_TestFileReadIn(std::vector<PossibleObject> testChoices)
|
||||||
|
{
|
||||||
|
TEST_ASSERT_EQUAL_INT(4, itemCount);
|
||||||
|
TEST_ASSERT_EQUAL_INT(6, limit);
|
||||||
|
for (int i = 0; i < itemCount; ++i)
|
||||||
|
{
|
||||||
|
TEST_ASSERT_EQUAL_INT(testChoices.at(i).weight, choices.at(i).weight);
|
||||||
|
TEST_ASSERT_EQUAL_INT(testChoices.at(i).value, choices.at(i).value);
|
||||||
|
TEST_ASSERT_EQUAL_INT(testChoices.at(i).itemNumber, choices.at(i).itemNumber);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void User::_TestKnapsack(void)
|
||||||
|
{
|
||||||
|
RunKnapsack();
|
||||||
|
TEST_ASSERT_NOT_EQUAL(0, solution.size());
|
||||||
|
int resultValue = 0;
|
||||||
|
for (auto object : solution)
|
||||||
|
resultValue += object->value;
|
||||||
|
TEST_ASSERT_EQUAL_INT(11, resultValue);
|
||||||
|
TEST_ASSERT_EQUAL_INT(3, solution.size());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void setUp(void)
|
||||||
|
{
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
void tearDown(void)
|
||||||
|
{
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_User_should_ReadInDataCorrectly(void)
|
||||||
|
{
|
||||||
|
knapsack::User testUser;
|
||||||
|
const char* testFilename = "test/test1.txt";
|
||||||
|
testUser.ReadNewFile(testFilename);
|
||||||
|
std::vector<knapsack::PossibleObject> testChoices;
|
||||||
|
testChoices.emplace_back(2, 4, 0);
|
||||||
|
testChoices.emplace_back(1, 5, 1);
|
||||||
|
testChoices.emplace_back(2, 1, 2);
|
||||||
|
testChoices.emplace_back(3, 2, 3);
|
||||||
|
testUser._TestFileReadIn(testChoices);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_KnapsackAlgorithm_should_PassTest1(void)
|
||||||
|
{
|
||||||
|
knapsack::User testUser;
|
||||||
|
const char* testFilename = "test/test1.txt";
|
||||||
|
testUser.ReadNewFile(testFilename);
|
||||||
|
testUser._TestKnapsack();
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_KnapsackAlgorithm_should_PassTest2(void)
|
||||||
|
{
|
||||||
|
knapsack::User testUser;
|
||||||
|
const char* testFilename = "test/test2.txt";
|
||||||
|
testUser.ReadNewFile(testFilename);
|
||||||
|
testUser.RunKnapsack();
|
||||||
|
TEST_ASSERT_EQUAL_INT(3, testUser.GetSolutionValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_KnapsackAlgorithm_should_PassTest3(void)
|
||||||
|
{
|
||||||
|
knapsack::User testUser;
|
||||||
|
const char* testFilename = "test/test3.txt";
|
||||||
|
testUser.ReadNewFile(testFilename);
|
||||||
|
testUser.RunKnapsack();
|
||||||
|
TEST_ASSERT_EQUAL_INT(0, testUser.GetSolutionValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
UNITY_BEGIN();
|
||||||
|
RUN_TEST(test_User_should_ReadInDataCorrectly);
|
||||||
|
RUN_TEST(test_KnapsackAlgorithm_should_PassTest1);
|
||||||
|
RUN_TEST(test_KnapsackAlgorithm_should_PassTest2);
|
||||||
|
RUN_TEST(test_KnapsackAlgorithm_should_PassTest3);
|
||||||
|
return UNITY_END();
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user