Initial commit

This commit is contained in:
TriantaTV 2023-07-25 22:59:09 -05:00
commit 26f1a0bd5a
11 changed files with 309 additions and 0 deletions

16
CMakeLists.txt Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View File

@ -0,0 +1,5 @@
4 6
2 4
1 5
2 1
3 2

4
test/test2.txt Normal file
View File

@ -0,0 +1,4 @@
3 4
4 1
5 2
1 3

4
test/test3.txt Normal file
View File

@ -0,0 +1,4 @@
3 3
4 1
5 2
6 3

90
test/testing.cpp Normal file
View 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();
}