Skip to content

Instantly share code, notes, and snippets.

@ylyhlh
Last active August 29, 2015 14:02
Show Gist options
  • Save ylyhlh/5ece6d882b4009afa7b4 to your computer and use it in GitHub Desktop.
Save ylyhlh/5ece6d882b4009afa7b4 to your computer and use it in GitHub Desktop.
Code challenged for dynamic programming. I solved using Boost's multiprecision library and test it using Boost's unit test.
//---------
//Question:
//---------
/*Assume the US dollar is available in denominations of
*$100, $50, $20, $10, $5, $1, $0.25, $0.10, $0.05 and
*$0.01. Write a function to return the number of
*different ways to exactly represent an arbitrary value
*less than $1,000.00 using any number or
*combination of these denominations.
*/
#ifndef BoostInt_h
#define BoostInt_h
/*************************************************/
#include <boost/multiprecision/gmp.hpp>
// using the multiprecision int from boost library
typedef boost::multiprecision::mpz_int BoostInt;
#define LOCAL_MAX 0
/**************************************************/
/*************************************************
#define ___USING___LONGLONG___
#define LOCAL_MAX LLONG_MAX
typedef long long BoostInt;
**************************************************/
#endif
//
// main.cpp
//
//
// Created by Liu Hao on 6/3/14.
// http://haoliu.org
// Copyright (c) 2014 liu hao. All rights reserved.
//
#include <iostream>
#include <vector>
#include "BoostInt.h"
#include "MoneyChanger.h"
// the test function of MoneyChanger class
void testMoneyChanger(double dollar, MoneyChanger* mc = NULL) {
try {
if (mc == NULL)
mc = new MoneyChanger();
BoostInt count = mc->howManyCombinations(dollar);
std::cout<<"The count of " << dollar << " is " << count << std::endl; }
catch (std::exception& e)
{
std::cout << e.what() << '\n';
}
}
int main(int argc, const char * argv[])
{
//get a new MoneyChanger
//==============
// Normal tests
//==============
//-- 0 test return 1
testMoneyChanger(0);
//-- 1 test return 243
testMoneyChanger(1);
//-- less that 5 cents return 1
testMoneyChanger(0.04);
//-- largest number test
testMoneyChanger(1000);
//==============
// Reuse tests
//==============
//-- the third one return very fast
MoneyChanger mc;
testMoneyChanger(1000, &mc);
testMoneyChanger(1300, &mc);
testMoneyChanger(1000, &mc);
//===============
//Exception tests
//===============
//-- negative teat
testMoneyChanger(-1);
//-- not whole cent test
testMoneyChanger(1.02354235);
#ifdef ___USING___LONGLONG___
//-- overflowing test, checked only when long long used
testMoneyChanger(10000, &mc);
#endif
//================
// print the table
//================
//mc.printCombinationTable();
return 0;
}
#Change this to your boost path
BOOST_INCLUDE=/usr/local/Cellar/boost/1.55.0_1/include/
BOOST_LIB=/usr/local/Cellar/boost/1.55.0_1/lib
#Flags no need to change
GMP_FLAG=gmp
BOOST_TEST_FLAG=boost_unit_test_framework
RUN_BOOST_FLAGS=-l$(GMP_FLAG)
RUN_FLAGS=
TEST_FLAGS=-l$(GMP_FLAG) -l$(BOOST_TEST_FLAG)
C_FLAGS=-std=c++11
# use main to run and get output from stdout
.PHONY: run
run: main.cpp MoneyChanger.o
$(CXX) $(C_FLAGS) -I$(BOOST_INCLUDE) -L$(BOOST_LIB) $(RUN_FLAGS) $^ -o $@
./$@
# use main to run and get output from stdout
.PHONY: runboost
runboost: main.cpp MoneyChanger.o
$(CXX) $(C_FLAGS) -I$(BOOST_INCLUDE) -L$(BOOST_LIB) $(RUN_BOOST_FLAGS) $^ -o $@
./$@
# use boost unit test to run and test
.PHONY: test
test: test.cpp MoneyChanger.o
$(CXX) $(C_FLAGS) -I$(BOOST_INCLUDE) -L$(BOOST_LIB) $(TEST_FLAGS) $^ -o $@
./$@
MoneyChanger.o:MoneyChanger.cpp
$(CXX) -c $(C_FLAGS) -I$(BOOST_INCLUDE) -L$(BOOST_LIB) $? -o $@
clean:
rm -rf *.o test run
//
// MoneyChanger.cpp
//
//
// Created by Liu Hao on 6/3/14.
// http://haoliu.org
// Copyright (c) 2014 liu hao. All rights reserved.
//
#include "MoneyChanger.h"
#include <iostream>
BoostInt MoneyChanger::howManyCombinations(double dollors) {
//change it to cents and call combination function cents
int cents = int(dollors*100);
if (cents/100.0 != dollors) {
throw MoneyChangerException("The input Money of MoneyChanger object cannot be represented by integer in cents");
}
//if overfloating, forward the exception as MoneyChangerException
try {
return howManyCombinationsInCents(cents);
} catch (std::overflow_error &e) {
std::string message = std::string("In computing for ")
+ std::to_string(dollors) + std::string(". ")
+ e.what();
throw MoneyChangerException(message);
}
}
BoostInt MoneyChanger::howManyCombinationsInCents(int cents) {
// zero fast return
if (cents == 0)
return 1;
// negative check
if (cents < 0) {
throw MoneyChangerException("The input Money of MoneyChanger object is negative");
}
// vectore memory check
if (numOfCombinations.size() < cents + 1) {
try {
numOfCombinations.resize(cents + 1);
} catch (std::bad_alloc& ba) {
throw MoneyChangerException("bad_alloc caught in howManyCombinationsInCents");
}
}
//find last calculated cents
int startCents = maxCentsAvailavle / SKIP_STEP * SKIP_STEP;
// begin the computing
// Loop over cents
for (int p = startCents; p <= cents; p += SKIP_STEP) {
// get new space
try {
numOfCombinations[p].resize(NUM_OF_DENOMINATIONS + 1);
} catch (std::bad_alloc& ba) {
throw MoneyChangerException("bad_alloc caught in howManyCombinationsInCents");
}
numOfCombinations[p][1] = 1;
// Loop over number of denominations to use
for (int q = 2; q <= NUM_OF_DENOMINATIONS; ++q) {
int lastDenomination = denominations[q - 1];
// do dynamic programming bottom-up here
if (lastDenomination < p)
numOfCombinations[p][q] = safeADD(numOfCombinations[p-lastDenomination][q], numOfCombinations[p][q - 1]);
else if(lastDenomination == p)
numOfCombinations[p][q] = safeADD(1, numOfCombinations[p][q - 1]);
else
numOfCombinations[p][q] = numOfCombinations[p][q-1];
}
// TODO: here can save some memory if needed
// set next (SKIP_STEP - 1) elements point to same vectors
for (int offset = 1; offset < SKIP_STEP ; ++offset)
numOfCombinations[p+offset] = numOfCombinations[p];
}
maxCentsAvailavle = maxCentsAvailavle < cents ? cents : maxCentsAvailavle;
return numOfCombinations[cents][NUM_OF_DENOMINATIONS];
}
void MoneyChanger::printCombinationTable() {
for (int p = 0; p <=maxCentsAvailavle; p+=1) {
for (int q = 0; q <= NUM_OF_DENOMINATIONS; ++q)
std::cout <<numOfCombinations[p][q] <<",";
std::cout<<std::endl;
}
}
//
// MoneyChanger.h
//
//
// Created by Liu Hao on 6/3/14.
// http://haoliu.org
// Copyright (c) 2014 liu hao. All rights reserved.
//
#ifndef __MoneyChanger__
#define __MoneyChanger__
#define NUM_OF_DENOMINATIONS 10
#define DENOMINATIONS {1,5,10,25,100,500,1000,2000,5000,10000}
#define SKIP_STEP 5
#include "BoostInt.h"
#include <exception>
#include <string>
#include <vector>
// The exception for MoneyChanger
class MoneyChangerException: public std::exception
{
std::string message;
public:
MoneyChangerException(std::string message) {
this->message = "[Exception] in MoneyChanger: ";
this->message += message;
}
virtual const char* what() const throw()
{
return message.c_str();
}
};
//MoneyChanger to get the number of combinations
class MoneyChanger {
// Denominations in cents
std::vector<int> denominations;
// numOfCombinations[n][k] store the number of
// cimmbinations for n cents with first k denominations
std::vector<std::vector<BoostInt> > numOfCombinations;
// max number of cents have been calculated
int maxCentsAvailavle;
// use for safty adding
BoostInt safeADD(BoostInt x, BoostInt y){
#ifdef ___USING___LONGLONG___
if( x < LOCAL_MAX - y)
return x + y;
else
throw std::overflow_error("Overflowing in computation");
#else
return x + y;
#endif
}
public:
MoneyChanger(){
//init attributes
maxCentsAvailavle = 0;
denominations = std::vector<int>(DENOMINATIONS);
// malloc the space for 0 cent, and calloc will init it with 0
numOfCombinations.resize(1);
numOfCombinations[0].resize(NUM_OF_DENOMINATIONS + 1);
numOfCombinations[0] = {0};
}
// return the number of combination of given 'dollars'
BoostInt howManyCombinations(double dollors);
// return the number of combination of given 'cents'
BoostInt howManyCombinationsInCents(int cents);
void printCombinationTable();
};
#endif /* defined(__MoneyChanger__) */
//
// test.cpp
//
//
// Created by Liu Hao on 6/3/14.
// http://haoliu.org
// Copyright (c) 2014 liu hao. All rights reserved.
//
#define BOOST_TEST_DYN_LINK
#define BOOST_TEST_MODULE Hello
#include <boost/test/unit_test.hpp>
#include "BoostInt.h"
#include "MoneyChanger.h"
//-- 1 dollar test return 1
BOOST_AUTO_TEST_CASE(oneDollar)
{
MoneyChanger mc;
BOOST_CHECK(mc.howManyCombinations(1) == 243);
}
//-- 0 dollar test return 1
BOOST_AUTO_TEST_CASE(zeroDollar)
{
MoneyChanger mc;
BOOST_CHECK(mc.howManyCombinations(0) == 1);
}
//-- less that 5 cents return 1
BOOST_AUTO_TEST_CASE(onlyOneCent)
{
MoneyChanger mc;
BOOST_CHECK(mc.howManyCombinations(0.04) == 1);
}
//-- negative teat
BOOST_AUTO_TEST_CASE(negativeException)
{
MoneyChanger mc;
BOOST_CHECK_THROW(mc.howManyCombinations(-1), MoneyChangerException);
}
//-- not whole cent test
BOOST_AUTO_TEST_CASE(notWholeCentException)
{
MoneyChanger mc;
BOOST_CHECK_THROW(mc.howManyCombinations(1.02354235), MoneyChangerException);
}
#ifdef ___USING___LONGLONG___
//-- overflowing test, checked only when long long used
BOOST_AUTO_TEST_CASE(overflowingException)
{
MoneyChanger mc;
BOOST_CHECK_THROW(mc.howManyCombinations(10000), MoneyChangerException);
}
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment