From 26f1a0bd5a6beeccd2c45ee5a2a7442312dc1252 Mon Sep 17 00:00:00 2001 From: TriantaTV Date: Tue, 25 Jul 2023 22:59:09 -0500 Subject: [PATCH] Initial commit --- CMakeLists.txt | 16 +++++++ Readme.md | 24 +++++++++++ src/CMakeLists.txt | 5 +++ src/knapsack.cpp | 102 ++++++++++++++++++++++++++++++++++++++++++++ src/knapsack.hpp | 38 +++++++++++++++++ src/main.cpp | 13 ++++++ test/CMakeLists.txt | 8 ++++ test/test1.txt | 5 +++ test/test2.txt | 4 ++ test/test3.txt | 4 ++ test/testing.cpp | 90 ++++++++++++++++++++++++++++++++++++++ 11 files changed, 309 insertions(+) create mode 100644 CMakeLists.txt create mode 100644 Readme.md create mode 100644 src/CMakeLists.txt create mode 100644 src/knapsack.cpp create mode 100644 src/knapsack.hpp create mode 100644 src/main.cpp create mode 100644 test/CMakeLists.txt create mode 100644 test/test1.txt create mode 100644 test/test2.txt create mode 100644 test/test3.txt create mode 100644 test/testing.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..d63a41f --- /dev/null +++ b/CMakeLists.txt @@ -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() + diff --git a/Readme.md b/Readme.md new file mode 100644 index 0000000..9700001 --- /dev/null +++ b/Readme.md @@ -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 + +## Options +The second argument of the program is used as the input file. + +EX: + + build/bin/knapsack file/to/use.txt + diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000..334a136 --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,5 @@ +add_executable(knapsack + ./main.cpp + ./knapsack.cpp +) +target_include_directories(knapsack PUBLIC ${CMAKE_CURRENT_LIST_DIR}) diff --git a/src/knapsack.cpp b/src/knapsack.cpp new file mode 100644 index 0000000..947904a --- /dev/null +++ b/src/knapsack.cpp @@ -0,0 +1,102 @@ +#include "knapsack.hpp" +#include +#include +#include +#include + +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; + } +} + diff --git a/src/knapsack.hpp b/src/knapsack.hpp new file mode 100644 index 0000000..3dc3f07 --- /dev/null +++ b/src/knapsack.hpp @@ -0,0 +1,38 @@ +#ifndef KNAPSACK_HPP +#define KNAPSACK_HPP + +#include + +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 testChoices); + void _TestKnapsack(void); + private: + std::vector choices; + std::vector solution; + int limit; + int itemCount; + }; +} + +#endif diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..b36144f --- /dev/null +++ b/src/main.cpp @@ -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; +} + diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt new file mode 100644 index 0000000..71386cd --- /dev/null +++ b/test/CMakeLists.txt @@ -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) diff --git a/test/test1.txt b/test/test1.txt new file mode 100644 index 0000000..64656be --- /dev/null +++ b/test/test1.txt @@ -0,0 +1,5 @@ +4 6 +2 4 +1 5 +2 1 +3 2 diff --git a/test/test2.txt b/test/test2.txt new file mode 100644 index 0000000..b2a0634 --- /dev/null +++ b/test/test2.txt @@ -0,0 +1,4 @@ +3 4 +4 1 +5 2 +1 3 diff --git a/test/test3.txt b/test/test3.txt new file mode 100644 index 0000000..30a364b --- /dev/null +++ b/test/test3.txt @@ -0,0 +1,4 @@ +3 3 +4 1 +5 2 +6 3 diff --git a/test/testing.cpp b/test/testing.cpp new file mode 100644 index 0000000..12c155d --- /dev/null +++ b/test/testing.cpp @@ -0,0 +1,90 @@ +#include +#include +#include +#include "knapsack.hpp" + +// Function is defined here for testing +namespace knapsack +{ + void User::_TestFileReadIn(std::vector 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 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(); +}