SlideShare a Scribd company logo
/**
* @mainpage
* @anchor mainpage
* @brief
* @details
* @copyright Russell John Childs, PhD, 2016
* @author Russell John Childs, PhD
* @date 2016-03-06
*
* This file contains classes: BurglarProblem
* Problem statement: A burglar wishses to maximise the value of goods stolen
* from N houses, subject to the constraint that they can only visit houses
* that are not neighbours.
*
* Solution: Dyanmic programming with complexity O(n) through the fololowing
* recurrence relation:
*
* optimum(i) = max(optimum(i-1)-w(i-1)+w(i), optimum(i-2)+w(i)), where w(i) is
* the weighting for house i and optimum(i) is the optimum solution for the 1st
* i houses.
*
* Algorithm tested against exhaustive search using binary number 1 to 2^(N-1)
* where 1-bts are houses visited, 0-bits are houses skipped and neighbours are
* illegal: E.g. 10010, but not 100110
*
* Compiled and verified under Visual Studio 13
*
* Documentation: Doxygen comments for interfaces, normal for impl.
*
* Usage: After compiling, run. The binary will send:
* n
* (1) test results to stdout
*
* The file unit_test.hpp is required for the tests and must be requested from
* author.
*
* @file dynamic_programming_burglar_problem.cpp
* @see
* @ref mainpage
*/
#include <vector>
#include <utility>
#include <random>
#include <bitset>
#include<algorithm>
#include "unit_test.hpp"
//Unit test framework is written for Linux. This #define ports it to Visual Studio 2013
#define __PRETTY_FUNCTION__ __FUNCSIG__
/**
* addtogroup BurglarProblem
* @{
*/
namespace BurglarProblem
{
class Burglar
{
/**
* This class solves the following problem:
* A burglar wishses to maximise the value of goods stolen
* from N houses, subject to the constraint that they can only visit houses
* that are not neighbours.
*
* It uses Dynamic Programming to achieve O(n) complexity using the recurrence
* relation:
*
* optimum(i) = max(optimum(i-1)-w(i-1)+w(i), optimum(i-2)+w(i)), where w(i) is
* the weighting for house i and optimum(i) is the optimum solution for the 1st
* i houses.
*
*/
private:
/**
* Initialiser function that makes all weights positive, sets optimum(0) and
* optimum(1), house list for optimum(0) and house list for optimum(1)
*/
void init(void)
{
//Make all weights positive
unsigned i = 0;
for (auto weight : m_weights)
{
m_weights[i] = std::abs(m_weights[i]);
++i;
}
//Store current and previous max for recurrence relation
auto min_max = std::minmax(m_weights[0], m_weights[1]);
m_max_found[0] = min_max.first;
m_max_found[1] = min_max.second;
//Store current and previous best list of houses for recurrence relation
m_houses[0].clear();
m_houses[0].push_back(m_weights[0] > m_weights[1]);
m_houses[1].clear();
m_houses[1].push_back(m_weights[1] > m_weights[0]);
}
public:
/**
* @param weights {const std::vector<double>&} - new list of weights
*/
Burglar(const std::vector<double>& weights) :
m_weights(weights),
m_which_list(false)
{
init();
}
/**
* dtor
*/
~Burglar(void)
{
}
/**
* This function resets the list of weights used.
* @param weights {const std::vector<double>&} - new list of weights
*
* @return {Burglar& } - *this
*
* Usage my_burgalr_object(new_list).solve();
*/
Burglar& reset(const std::vector<double>& weights)
{
m_weights = weights;
init();
return *this;
}
/**
* This function finds the optimum set of houses to visit, using:
*
* optimum(i) = max(optimum(i-1)-w(i-1)+w(i), optimum(i-2)+w(i)), where w(i) is
* the weighting for house i and optimum(i) is the optimum solution for the 1st
* i houses.
*/
void solve(void)
{
auto size = m_weights.size();
//Loop over houses
for (unsigned i = 2; i < size; ++i)
{
//Get last house in current optimum list
unsigned last = m_houses[1].back();
//Check house has value
if (m_weights[i] > 0)
{
//If is not a neighbour of current house
if (i > (last + 1))
{
//Add house value to current value
m_max_found[1] += m_weights[i];
//set prev optimum to current
m_houses[0] = m_houses[1];
//Add house to current optimum
m_houses[1].push_back(i);
}
else
{
//house is a neighbour, find best way to add it:
//(Skip, replace current house, add to prev optimum)
enum Choose{ do_nothing = 0, replace = 1, add = 2 } choose
= do_nothing;
double tmp_max = m_max_found[1];
//Get value if we replace current house in optimum(i-1)
double tmp = m_max_found[1] + (m_weights[i] - m_weights[i - 1]);
if (tmp > tmp_max)
{
tmp_max = tmp;
choose = replace;
}
//Get value if we add house to previous optimum(i-2)
tmp = m_max_found[0] + m_weights[i];
if ((tmp > tmp_max) && (i != (m_houses[0].back()+1)))
{
tmp_max = tmp;
choose = add;
}
//Set new vals for optimum(i-1), optimum(i)
m_max_found[0] = m_max_found[1];
m_max_found[1] = tmp_max;
//Replace optimum(i) house with new house
if (choose == replace)
{
m_houses[0] = m_houses[1];
m_houses[1].back() = i;
}
//Add new house to optimum(i-1)
else if (choose == add)
{
std::vector<unsigned> tmp_list = m_houses[0];
m_houses[0] = m_houses[1];
m_houses[1] = tmp_list;
m_houses[1].push_back(i);
}
}
}
}
}
/**
* This function return optimum value of goods stolen and houses to visit
*
* @return {std::pair<double,std::reference_wrapper<std::vector<unsigned>>>}
* - {optimum value, list of houses}
*/
std::pair<double, std::reference_wrapper<std::vector<unsigned>>>
get_result(void)
{
//Return optimum value and corresponding house list
return std::pair<double, std::reference_wrapper<std::vector<unsigned>>>
(m_max_found[1], m_houses[1]);
}
//private:
std::vector<double> m_weights;
bool m_which_list;
double m_max_found[2];
std::vector<unsigned> m_houses[2];
};
}
/**
* @}
*/
/**
* addtogroup Tests
* @{
*/
namespace Tests
{
/**
* Wrapper class for std::vector converting {a, b, c, ...} to "a b c ..."
*/
struct PrintVector
{
PrintVector(const std::vector<unsigned>& vec) :
m_vec(vec)
{
}
std::string str()
{
std::stringstream ss;
for (auto elem : m_vec)
{
ss << elem << " ";
}
return ss.str();
}
std::vector<unsigned> m_vec;
};
/**
* This function compares algorithm against exhaustive search for best
* solution using: binary number 1 to 2^(N-1)
* where 1-bts are houses visited, 0-bits are houses skipped and neighbours are
* illegal: E.g. 10010, but not 100110
*/
void tests(void)
{
using namespace UnitTest;
using namespace BurglarProblem;
//Print test banner
Verify<> banner("Burglar solver");
typedef std::vector<unsigned> uv;
//Two houses with 1st being the optimal choice
Burglar test(std::vector<double>{3, 1});
test.solve();
std::string msg_arg("std::vector<double>{3, 1}");
VERIFY(std::string("Input weights=") + msg_arg,
test.get_result().first) == 3;
VERIFY(std::string("Input weights=") +
msg_arg, PrintVector(test.get_result().second.get()).str()) ==
PrintVector(uv{ 0 }).str();
//Two houses with 2nd being the optimal choice
test.reset(std::vector<double>{2, 4}).solve();
msg_arg = "std::vector<double>{2, 4})";
VERIFY(std::string("Input weights=") + msg_arg,
test.get_result().first) == 4;
VERIFY(std::string("Input weights=") +
msg_arg, PrintVector(test.get_result().second.get()).str()) ==
PrintVector(uv{ 1 }).str();
//Three houses with 1st and 3rd being the optimal choice
test.reset(std::vector<double>{4, 9, 6}).solve();
msg_arg = "std::vector<double>{4, 9, 6}";
VERIFY(std::string("Input weights=") + msg_arg,
test.get_result().first) == 10;
VERIFY(std::string("Input weights=") +
msg_arg, PrintVector(test.get_result().second.get()).str()) ==
PrintVector(uv{ 0,2 }).str();
//Three houses with 2nd being the optimal choice
test.reset(std::vector<double>{5, 13, 7});
msg_arg = "std::vector<double>{5, 13, 7}";
VERIFY(std::string("Input weights=") + msg_arg,
test.get_result().first) == 13;
VERIFY(std::string("Input weights=") +
msg_arg, PrintVector(test.get_result().second.get()).str()) ==
PrintVector(uv{ 1 }).str();
//Test proof of recurrence relation. Fourth house not optimal
test.reset(std::vector<double>{5, 10, 7, 1}).solve();
msg_arg = "std::vector<double>{5, 10, 7, 1}";
VERIFY(std::string("Input weights=") + msg_arg,
test.get_result().first) == 12;
VERIFY(std::string("Input weights=") +
msg_arg, PrintVector(test.get_result().second.get()).str()) ==
PrintVector(uv{ 0, 2 }).str();
//Test proof of recurrence relation. Fourth house optimal
test.reset(std::vector<double>{5, 10, 7, 3}).solve();
msg_arg = "std::vector<double>{5, 10, 7, 3}";
VERIFY(std::string("Input weights=") + msg_arg,
test.get_result().first) == 13;
VERIFY(std::string("Input weights=") +
msg_arg, PrintVector(test.get_result().second.get()).str()) ==
PrintVector(uv{ 1, 3 }).str();
unsigned run = 0;
auto exhaustive = [&]()
{
//Test by exhaustive enumeration. Enumeration subject to constraints
//took me a while to deduce. Idea is to use a binary number from
//1 to 2^N, where "1" means visit house and "0" means skip. Discard
//any binary number that violates the constraints (visits 2 neighbours)
//I am not 100% confident this is right and welcome a code review.
//(1) 20 houses with random weights (if you change this, ensure n<64)
const unsigned n = 20;
std::vector<double> random_weights;
std::random_device dev;
//std::mt19937 generator(dev());
std::mt19937 generator(run);
std::uniform_int_distribution<> distribution(1, 1000000);
for (int i = 0; i < n; ++i)
{
random_weights.push_back(distribution(generator));
}
//(2) Generate a binary number from 1 to 2^n
//and treat as house1=visit/skip, house2=visit/skip ...
double exhaustive_search_max = 0;
std::vector<unsigned> exahustive_search_house_list;
for (long long unsigned bin = 0; bin < ((1 << n) - 1); ++bin)
{
//Reverse to represent house1=0/1, house2=0/1, ...
//Happily bitset does this anyway.
std::bitset<n> houses(bin);
//Loop over bits until we find two neighbours
unsigned prev_pos = 0;
bool is_neighbour = false;
unsigned j = 0;
while ((is_neighbour == false) && j < n)
{
is_neighbour = (j != 0) && (houses[prev_pos] && houses[j]);
prev_pos = j;
++j;
}
//if we haven't found any neighbours, we have a legal permutation
if (is_neighbour == false)
{
//Get global maximum and houses corresponding to global max
double tmp = 0;
unsigned house_number = 0;
for (auto weight : random_weights)
{
tmp += (weight * houses[house_number]);
++house_number;
}
//If new global max found
if (tmp > exhaustive_search_max)
{
//Update global max
exhaustive_search_max = tmp;
//update house list
exahustive_search_house_list.clear();
for (unsigned index = 0; index < n; ++index)
{
if (houses[index])
{
exahustive_search_house_list.push_back(index);
}
}
}
}
}
//(3) Validate algorithm agaisnt exhaustive search
test.reset(random_weights).solve();
msg_arg = "Exhaustive search with randomw weights: ";
VERIFY(msg_arg, test.get_result().first) == exhaustive_search_max;
VERIFY(msg_arg, PrintVector(uv(test.get_result().second.get())).str()) ==
PrintVector(exahustive_search_house_list).str();
};
//Run 20 exhasutive checks
for (unsigned i = 0; i < 20; ++i)
{
run = i;
std::cout << std::endl << "Run=" << run << std::endl;
exhaustive();
}
}
}
/**
* @}
*/
int main(void)
{
using namespace Tests;
using namespace UnitTest;
//This struct pauses at the end of tests to print out results
struct BreakPointAfterMainExits
{
BreakPointAfterMainExits(void)
{
static BreakPointAfterMainExits tmp;
}
~BreakPointAfterMainExits(void)
{
unsigned set_bp_here_for_test_results = 0;
}
} dummy;
//Run tests
tests();
Verify<Results> results;
}

More Related Content

What's hot (20)

PPTX
Image Recognition with Neural Network
Sajib Sen
 
PDF
Javascript & Ajax Basics
Richard Paul
 
PPTX
Groovy grails types, operators, objects
Husain Dalal
 
PDF
C++ ARRAY WITH EXAMPLES
Farhan Ab Rahman
 
PPTX
Unit 3
GOWSIKRAJAP
 
PDF
C++ TUTORIAL 8
Farhan Ab Rahman
 
PDF
Notes for C++ Programming / Object Oriented C++ Programming for MCA, BCA and ...
ssuserd6b1fd
 
PDF
c programming
Arun Umrao
 
PDF
C++ prgms 3rd unit
Ananda Kumar HN
 
PDF
Modern C++ Concurrency API
Seok-joon Yun
 
PDF
Monolith to Reactive Microservices
Reactivesummit
 
PPT
Advance features of C++
vidyamittal
 
PDF
Notes for C Programming for MCA, BCA, B. Tech CSE, ECE and MSC (CS) 4 of 5 by...
ssuserd6b1fd
 
PDF
c programming
Arun Umrao
 
PDF
C++ L08-Classes Part1
Mohammad Shaker
 
PDF
C++ L09-Classes Part2
Mohammad Shaker
 
PDF
Asssignment2
AnnamalikAnnamalik
 
PDF
Property-based testing
Dmitriy Morozov
 
PDF
The Ring programming language version 1.4 book - Part 29 of 30
Mahmoud Samir Fayed
 
Image Recognition with Neural Network
Sajib Sen
 
Javascript & Ajax Basics
Richard Paul
 
Groovy grails types, operators, objects
Husain Dalal
 
C++ ARRAY WITH EXAMPLES
Farhan Ab Rahman
 
Unit 3
GOWSIKRAJAP
 
C++ TUTORIAL 8
Farhan Ab Rahman
 
Notes for C++ Programming / Object Oriented C++ Programming for MCA, BCA and ...
ssuserd6b1fd
 
c programming
Arun Umrao
 
C++ prgms 3rd unit
Ananda Kumar HN
 
Modern C++ Concurrency API
Seok-joon Yun
 
Monolith to Reactive Microservices
Reactivesummit
 
Advance features of C++
vidyamittal
 
Notes for C Programming for MCA, BCA, B. Tech CSE, ECE and MSC (CS) 4 of 5 by...
ssuserd6b1fd
 
c programming
Arun Umrao
 
C++ L08-Classes Part1
Mohammad Shaker
 
C++ L09-Classes Part2
Mohammad Shaker
 
Asssignment2
AnnamalikAnnamalik
 
Property-based testing
Dmitriy Morozov
 
The Ring programming language version 1.4 book - Part 29 of 30
Mahmoud Samir Fayed
 

Similar to Dynamic programming burglar_problem (20)

PPTX
CPP Homework Help
C++ Homework Help
 
PPTX
14-dynamic-programming-work-methods.pptx
r6s0069
 
PPTX
Dynamic Programming in design and analysis .pptx
dimpuk1
 
DOCX
Task4output.txt 2 5 9 13 15 10 1 0 3 7 11 14 1.docx
josies1
 
PPTX
funwithalgorithms.pptx
Tess Ferrandez
 
PDF
13 - 06 Feb - Dynamic Programming
Neeldhara Misra
 
PPTX
Design & Analysis of Algorithms Assignment Help
Computer Network Assignment Help
 
PPT
Knapsack problem and Memory Function
Barani Tharan
 
PDF
Lop1
devendragiitk
 
PPTX
Greedy algo revision 2
maamir farooq
 
PPT
Lowest common ancestor
Shakil Ahmed
 
PPT
Lec38
Nikhil Chilwant
 
PPTX
Daa:Dynamic Programing
rupali_2bonde
 
PPT
DynProg_Knapsack.ppt
Ruchika Sinha
 
PDF
An Introduction to Part of C++ STL
乐群 陈
 
PPT
Dynamic Programming for 4th sem cse students
DeepakGowda357858
 
PPT
Knapsack problem
Vikas Sharma
 
PPTX
programminghomeworkhelp.com_Advanced Algorithms Homework Help.pptx
Programming Homework Help
 
PPT
Advanced Search Techniques
Shakil Ahmed
 
CPP Homework Help
C++ Homework Help
 
14-dynamic-programming-work-methods.pptx
r6s0069
 
Dynamic Programming in design and analysis .pptx
dimpuk1
 
Task4output.txt 2 5 9 13 15 10 1 0 3 7 11 14 1.docx
josies1
 
funwithalgorithms.pptx
Tess Ferrandez
 
13 - 06 Feb - Dynamic Programming
Neeldhara Misra
 
Design & Analysis of Algorithms Assignment Help
Computer Network Assignment Help
 
Knapsack problem and Memory Function
Barani Tharan
 
Greedy algo revision 2
maamir farooq
 
Lowest common ancestor
Shakil Ahmed
 
Daa:Dynamic Programing
rupali_2bonde
 
DynProg_Knapsack.ppt
Ruchika Sinha
 
An Introduction to Part of C++ STL
乐群 陈
 
Dynamic Programming for 4th sem cse students
DeepakGowda357858
 
Knapsack problem
Vikas Sharma
 
programminghomeworkhelp.com_Advanced Algorithms Homework Help.pptx
Programming Homework Help
 
Advanced Search Techniques
Shakil Ahmed
 
Ad

More from Russell Childs (20)

PDF
spinor_quantum_simulator_user_guide_.pdf
Russell Childs
 
PDF
String searching o_n
Russell Childs
 
PDF
String searching o_n
Russell Childs
 
PDF
String searching o_n
Russell Childs
 
PDF
String searching
Russell Childs
 
PDF
Permute
Russell Childs
 
PDF
Permute
Russell Childs
 
PDF
Feature extraction using adiabatic theorem
Russell Childs
 
PDF
Feature extraction using adiabatic theorem
Russell Childs
 
PDF
Wavelets_and_multiresolution_in_two_pages
Russell Childs
 
PDF
Relativity 2
Russell Childs
 
PDF
Recursion to iteration automation.
Russell Childs
 
PDF
Dirac demo (quantum mechanics with C++). Please note: There is a problem with...
Russell Childs
 
PDF
Shared_memory_hash_table
Russell Childs
 
PDF
Full resume dr_russell_john_childs_2016
Russell Childs
 
PDF
Simple shared mutex UML
Russell Childs
 
PDF
Design pattern to avoid downcasting
Russell Childs
 
PDF
Interview uml design
Russell Childs
 
PDF
Interview C++11 code
Russell Childs
 
PDF
Full_resume_Dr_Russell_John_Childs
Russell Childs
 
spinor_quantum_simulator_user_guide_.pdf
Russell Childs
 
String searching o_n
Russell Childs
 
String searching o_n
Russell Childs
 
String searching o_n
Russell Childs
 
String searching
Russell Childs
 
Feature extraction using adiabatic theorem
Russell Childs
 
Feature extraction using adiabatic theorem
Russell Childs
 
Wavelets_and_multiresolution_in_two_pages
Russell Childs
 
Relativity 2
Russell Childs
 
Recursion to iteration automation.
Russell Childs
 
Dirac demo (quantum mechanics with C++). Please note: There is a problem with...
Russell Childs
 
Shared_memory_hash_table
Russell Childs
 
Full resume dr_russell_john_childs_2016
Russell Childs
 
Simple shared mutex UML
Russell Childs
 
Design pattern to avoid downcasting
Russell Childs
 
Interview uml design
Russell Childs
 
Interview C++11 code
Russell Childs
 
Full_resume_Dr_Russell_John_Childs
Russell Childs
 
Ad

Recently uploaded (20)

PPTX
WYSIWYG Web Builder Crack 2025 – Free Download Full Version with License Key
HyperPc soft
 
PPTX
Quality on Autopilot: Scaling Testing in Uyuni
Oscar Barrios Torrero
 
PDF
Powering GIS with FME and VertiGIS - Peak of Data & AI 2025
Safe Software
 
PPTX
Perfecting XM Cloud for Multisite Setup.pptx
Ahmed Okour
 
PDF
GridView,Recycler view, API, SQLITE& NetworkRequest.pdf
Nabin Dhakal
 
PPTX
Equipment Management Software BIS Safety UK.pptx
BIS Safety Software
 
PDF
Beyond Binaries: Understanding Diversity and Allyship in a Global Workplace -...
Imma Valls Bernaus
 
PDF
Mobile CMMS Solutions Empowering the Frontline Workforce
CryotosCMMSSoftware
 
PPTX
Writing Better Code - Helping Developers make Decisions.pptx
Lorraine Steyn
 
PPTX
NeuroStrata: Harnessing Neuro-Symbolic Paradigms for Improved Testability and...
Ivan Ruchkin
 
PPTX
CONCEPT OF PROGRAMMING in language .pptx
tamim41
 
PPTX
ERP - FICO Presentation BY BSL BOKARO STEEL LIMITED.pptx
ravisranjan
 
PDF
2025年 Linux 核心專題: 探討 sched_ext 及機器學習.pdf
Eric Chou
 
PDF
Efficient, Automated Claims Processing Software for Insurers
Insurance Tech Services
 
PDF
Alarm in Android-Scheduling Timed Tasks Using AlarmManager in Android.pdf
Nabin Dhakal
 
PDF
LPS25 - Operationalizing MLOps in GEP - Terradue.pdf
terradue
 
PPTX
Feb 2021 Cohesity first pitch presentation.pptx
enginsayin1
 
PPTX
Migrating Millions of Users with Debezium, Apache Kafka, and an Acyclic Synch...
MD Sayem Ahmed
 
PPTX
How Apagen Empowered an EPC Company with Engineering ERP Software
SatishKumar2651
 
PPTX
Comprehensive Guide: Shoviv Exchange to Office 365 Migration Tool 2025
Shoviv Software
 
WYSIWYG Web Builder Crack 2025 – Free Download Full Version with License Key
HyperPc soft
 
Quality on Autopilot: Scaling Testing in Uyuni
Oscar Barrios Torrero
 
Powering GIS with FME and VertiGIS - Peak of Data & AI 2025
Safe Software
 
Perfecting XM Cloud for Multisite Setup.pptx
Ahmed Okour
 
GridView,Recycler view, API, SQLITE& NetworkRequest.pdf
Nabin Dhakal
 
Equipment Management Software BIS Safety UK.pptx
BIS Safety Software
 
Beyond Binaries: Understanding Diversity and Allyship in a Global Workplace -...
Imma Valls Bernaus
 
Mobile CMMS Solutions Empowering the Frontline Workforce
CryotosCMMSSoftware
 
Writing Better Code - Helping Developers make Decisions.pptx
Lorraine Steyn
 
NeuroStrata: Harnessing Neuro-Symbolic Paradigms for Improved Testability and...
Ivan Ruchkin
 
CONCEPT OF PROGRAMMING in language .pptx
tamim41
 
ERP - FICO Presentation BY BSL BOKARO STEEL LIMITED.pptx
ravisranjan
 
2025年 Linux 核心專題: 探討 sched_ext 及機器學習.pdf
Eric Chou
 
Efficient, Automated Claims Processing Software for Insurers
Insurance Tech Services
 
Alarm in Android-Scheduling Timed Tasks Using AlarmManager in Android.pdf
Nabin Dhakal
 
LPS25 - Operationalizing MLOps in GEP - Terradue.pdf
terradue
 
Feb 2021 Cohesity first pitch presentation.pptx
enginsayin1
 
Migrating Millions of Users with Debezium, Apache Kafka, and an Acyclic Synch...
MD Sayem Ahmed
 
How Apagen Empowered an EPC Company with Engineering ERP Software
SatishKumar2651
 
Comprehensive Guide: Shoviv Exchange to Office 365 Migration Tool 2025
Shoviv Software
 

Dynamic programming burglar_problem

  • 1. /** * @mainpage * @anchor mainpage * @brief * @details * @copyright Russell John Childs, PhD, 2016 * @author Russell John Childs, PhD * @date 2016-03-06 * * This file contains classes: BurglarProblem * Problem statement: A burglar wishses to maximise the value of goods stolen * from N houses, subject to the constraint that they can only visit houses * that are not neighbours. * * Solution: Dyanmic programming with complexity O(n) through the fololowing * recurrence relation: * * optimum(i) = max(optimum(i-1)-w(i-1)+w(i), optimum(i-2)+w(i)), where w(i) is * the weighting for house i and optimum(i) is the optimum solution for the 1st * i houses. * * Algorithm tested against exhaustive search using binary number 1 to 2^(N-1) * where 1-bts are houses visited, 0-bits are houses skipped and neighbours are * illegal: E.g. 10010, but not 100110 * * Compiled and verified under Visual Studio 13 * * Documentation: Doxygen comments for interfaces, normal for impl. * * Usage: After compiling, run. The binary will send: * n * (1) test results to stdout * * The file unit_test.hpp is required for the tests and must be requested from * author. * * @file dynamic_programming_burglar_problem.cpp * @see * @ref mainpage */ #include <vector> #include <utility> #include <random> #include <bitset> #include<algorithm> #include "unit_test.hpp" //Unit test framework is written for Linux. This #define ports it to Visual Studio 2013 #define __PRETTY_FUNCTION__ __FUNCSIG__ /** * addtogroup BurglarProblem * @{ */ namespace BurglarProblem { class Burglar { /** * This class solves the following problem: * A burglar wishses to maximise the value of goods stolen * from N houses, subject to the constraint that they can only visit houses * that are not neighbours. * * It uses Dynamic Programming to achieve O(n) complexity using the recurrence * relation: * * optimum(i) = max(optimum(i-1)-w(i-1)+w(i), optimum(i-2)+w(i)), where w(i) is * the weighting for house i and optimum(i) is the optimum solution for the 1st * i houses. * */ private:
  • 2. /** * Initialiser function that makes all weights positive, sets optimum(0) and * optimum(1), house list for optimum(0) and house list for optimum(1) */ void init(void) { //Make all weights positive unsigned i = 0; for (auto weight : m_weights) { m_weights[i] = std::abs(m_weights[i]); ++i; } //Store current and previous max for recurrence relation auto min_max = std::minmax(m_weights[0], m_weights[1]); m_max_found[0] = min_max.first; m_max_found[1] = min_max.second; //Store current and previous best list of houses for recurrence relation m_houses[0].clear(); m_houses[0].push_back(m_weights[0] > m_weights[1]); m_houses[1].clear(); m_houses[1].push_back(m_weights[1] > m_weights[0]); } public: /** * @param weights {const std::vector<double>&} - new list of weights */ Burglar(const std::vector<double>& weights) : m_weights(weights), m_which_list(false) { init(); } /** * dtor */ ~Burglar(void) { } /** * This function resets the list of weights used. * @param weights {const std::vector<double>&} - new list of weights * * @return {Burglar& } - *this * * Usage my_burgalr_object(new_list).solve(); */ Burglar& reset(const std::vector<double>& weights) { m_weights = weights; init(); return *this; } /** * This function finds the optimum set of houses to visit, using: * * optimum(i) = max(optimum(i-1)-w(i-1)+w(i), optimum(i-2)+w(i)), where w(i) is * the weighting for house i and optimum(i) is the optimum solution for the 1st * i houses. */ void solve(void) { auto size = m_weights.size(); //Loop over houses for (unsigned i = 2; i < size; ++i) { //Get last house in current optimum list unsigned last = m_houses[1].back();
  • 3. //Check house has value if (m_weights[i] > 0) { //If is not a neighbour of current house if (i > (last + 1)) { //Add house value to current value m_max_found[1] += m_weights[i]; //set prev optimum to current m_houses[0] = m_houses[1]; //Add house to current optimum m_houses[1].push_back(i); } else { //house is a neighbour, find best way to add it: //(Skip, replace current house, add to prev optimum) enum Choose{ do_nothing = 0, replace = 1, add = 2 } choose = do_nothing; double tmp_max = m_max_found[1]; //Get value if we replace current house in optimum(i-1) double tmp = m_max_found[1] + (m_weights[i] - m_weights[i - 1]); if (tmp > tmp_max) { tmp_max = tmp; choose = replace; } //Get value if we add house to previous optimum(i-2) tmp = m_max_found[0] + m_weights[i]; if ((tmp > tmp_max) && (i != (m_houses[0].back()+1))) { tmp_max = tmp; choose = add; } //Set new vals for optimum(i-1), optimum(i) m_max_found[0] = m_max_found[1]; m_max_found[1] = tmp_max; //Replace optimum(i) house with new house if (choose == replace) { m_houses[0] = m_houses[1]; m_houses[1].back() = i; } //Add new house to optimum(i-1) else if (choose == add) { std::vector<unsigned> tmp_list = m_houses[0]; m_houses[0] = m_houses[1]; m_houses[1] = tmp_list; m_houses[1].push_back(i); } } } } } /** * This function return optimum value of goods stolen and houses to visit * * @return {std::pair<double,std::reference_wrapper<std::vector<unsigned>>>} * - {optimum value, list of houses} */ std::pair<double, std::reference_wrapper<std::vector<unsigned>>> get_result(void) { //Return optimum value and corresponding house list return std::pair<double, std::reference_wrapper<std::vector<unsigned>>> (m_max_found[1], m_houses[1]); }
  • 4. //private: std::vector<double> m_weights; bool m_which_list; double m_max_found[2]; std::vector<unsigned> m_houses[2]; }; } /** * @} */ /** * addtogroup Tests * @{ */ namespace Tests { /** * Wrapper class for std::vector converting {a, b, c, ...} to "a b c ..." */ struct PrintVector { PrintVector(const std::vector<unsigned>& vec) : m_vec(vec) { } std::string str() { std::stringstream ss; for (auto elem : m_vec) { ss << elem << " "; } return ss.str(); } std::vector<unsigned> m_vec; }; /** * This function compares algorithm against exhaustive search for best * solution using: binary number 1 to 2^(N-1) * where 1-bts are houses visited, 0-bits are houses skipped and neighbours are * illegal: E.g. 10010, but not 100110 */ void tests(void) { using namespace UnitTest; using namespace BurglarProblem; //Print test banner Verify<> banner("Burglar solver"); typedef std::vector<unsigned> uv; //Two houses with 1st being the optimal choice Burglar test(std::vector<double>{3, 1}); test.solve(); std::string msg_arg("std::vector<double>{3, 1}"); VERIFY(std::string("Input weights=") + msg_arg, test.get_result().first) == 3; VERIFY(std::string("Input weights=") + msg_arg, PrintVector(test.get_result().second.get()).str()) == PrintVector(uv{ 0 }).str(); //Two houses with 2nd being the optimal choice test.reset(std::vector<double>{2, 4}).solve(); msg_arg = "std::vector<double>{2, 4})"; VERIFY(std::string("Input weights=") + msg_arg, test.get_result().first) == 4; VERIFY(std::string("Input weights=") + msg_arg, PrintVector(test.get_result().second.get()).str()) == PrintVector(uv{ 1 }).str(); //Three houses with 1st and 3rd being the optimal choice
  • 5. test.reset(std::vector<double>{4, 9, 6}).solve(); msg_arg = "std::vector<double>{4, 9, 6}"; VERIFY(std::string("Input weights=") + msg_arg, test.get_result().first) == 10; VERIFY(std::string("Input weights=") + msg_arg, PrintVector(test.get_result().second.get()).str()) == PrintVector(uv{ 0,2 }).str(); //Three houses with 2nd being the optimal choice test.reset(std::vector<double>{5, 13, 7}); msg_arg = "std::vector<double>{5, 13, 7}"; VERIFY(std::string("Input weights=") + msg_arg, test.get_result().first) == 13; VERIFY(std::string("Input weights=") + msg_arg, PrintVector(test.get_result().second.get()).str()) == PrintVector(uv{ 1 }).str(); //Test proof of recurrence relation. Fourth house not optimal test.reset(std::vector<double>{5, 10, 7, 1}).solve(); msg_arg = "std::vector<double>{5, 10, 7, 1}"; VERIFY(std::string("Input weights=") + msg_arg, test.get_result().first) == 12; VERIFY(std::string("Input weights=") + msg_arg, PrintVector(test.get_result().second.get()).str()) == PrintVector(uv{ 0, 2 }).str(); //Test proof of recurrence relation. Fourth house optimal test.reset(std::vector<double>{5, 10, 7, 3}).solve(); msg_arg = "std::vector<double>{5, 10, 7, 3}"; VERIFY(std::string("Input weights=") + msg_arg, test.get_result().first) == 13; VERIFY(std::string("Input weights=") + msg_arg, PrintVector(test.get_result().second.get()).str()) == PrintVector(uv{ 1, 3 }).str(); unsigned run = 0; auto exhaustive = [&]() { //Test by exhaustive enumeration. Enumeration subject to constraints //took me a while to deduce. Idea is to use a binary number from //1 to 2^N, where "1" means visit house and "0" means skip. Discard //any binary number that violates the constraints (visits 2 neighbours) //I am not 100% confident this is right and welcome a code review. //(1) 20 houses with random weights (if you change this, ensure n<64) const unsigned n = 20; std::vector<double> random_weights; std::random_device dev; //std::mt19937 generator(dev()); std::mt19937 generator(run); std::uniform_int_distribution<> distribution(1, 1000000); for (int i = 0; i < n; ++i) { random_weights.push_back(distribution(generator)); } //(2) Generate a binary number from 1 to 2^n //and treat as house1=visit/skip, house2=visit/skip ... double exhaustive_search_max = 0; std::vector<unsigned> exahustive_search_house_list; for (long long unsigned bin = 0; bin < ((1 << n) - 1); ++bin) { //Reverse to represent house1=0/1, house2=0/1, ... //Happily bitset does this anyway. std::bitset<n> houses(bin); //Loop over bits until we find two neighbours unsigned prev_pos = 0; bool is_neighbour = false; unsigned j = 0; while ((is_neighbour == false) && j < n) { is_neighbour = (j != 0) && (houses[prev_pos] && houses[j]); prev_pos = j; ++j; } //if we haven't found any neighbours, we have a legal permutation if (is_neighbour == false)
  • 6. { //Get global maximum and houses corresponding to global max double tmp = 0; unsigned house_number = 0; for (auto weight : random_weights) { tmp += (weight * houses[house_number]); ++house_number; } //If new global max found if (tmp > exhaustive_search_max) { //Update global max exhaustive_search_max = tmp; //update house list exahustive_search_house_list.clear(); for (unsigned index = 0; index < n; ++index) { if (houses[index]) { exahustive_search_house_list.push_back(index); } } } } } //(3) Validate algorithm agaisnt exhaustive search test.reset(random_weights).solve(); msg_arg = "Exhaustive search with randomw weights: "; VERIFY(msg_arg, test.get_result().first) == exhaustive_search_max; VERIFY(msg_arg, PrintVector(uv(test.get_result().second.get())).str()) == PrintVector(exahustive_search_house_list).str(); }; //Run 20 exhasutive checks for (unsigned i = 0; i < 20; ++i) { run = i; std::cout << std::endl << "Run=" << run << std::endl; exhaustive(); } } } /** * @} */ int main(void) { using namespace Tests; using namespace UnitTest; //This struct pauses at the end of tests to print out results struct BreakPointAfterMainExits { BreakPointAfterMainExits(void) { static BreakPointAfterMainExits tmp; } ~BreakPointAfterMainExits(void) { unsigned set_bp_here_for_test_results = 0; } } dummy; //Run tests tests(); Verify<Results> results; }