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