A class that automates conversion from a C++ recursive function to an iterative function. It allow the recursive function to preserve its structure by reproducing the "call stack" on an std::stack. The examples use combinatorics to illustrate usage.
%in Midrand+277-882-255-28 abortion pills for sale in midrand
Recursion to iteration automation.
1. /**
* @mainpage
* @anchor mainpage
* @brief
* @details
* @copyright Russell John Childs, PhD, 2017
* @author Russell John Childs, PhD
* @date 2017-03-20
*
* This file contains
* class: Call
*
* Functions: recursive_combinations, iterative_combinations,
* recursive_permutations, iterative_permutations
*
*
* Problem statement:
* You have a recursive routine that needs to be replaced with iteration.
* To avoid potential error, the class "Call" may be used to replace recursion
* with iteration whilst keeping the structure of the function intact. This is
* useful for problems that are more easily solved through recurrence relations,
* which makes recursion a more natural, but problematic approach in C++.
* The class "Call" allows for direct conversion to iteration by using
* std::stack instead of a call stack, as in the following example:
*
* @code
*void iterative_perms(const std::vector<unsigned>& in, unsigned recurse)
*{
* //Args to be pushed to stack
* struct Args{unsigned recurse; std::set<unsigned>::iterator* iter;
* unsigned i;};
* //Vector of indices not yet "used up" in permuation sequence
* static std::set<unsigned> remaining(in.begin(), in.end());
* //Vector containing the permutation
* static std::vector<unsigned> indices(in.size());
* //Termination sequence
* static std::vector<unsigned> term(in.rbegin(), in.rend());
*
* auto iter = remaining.begin();
* //Iterate until recursion termination condition hit
* while (Call<Args>::done({ recurse }, [&]{return indices != term; }) )
* {
* // Create object to push and pop args for each iteration
* Call<Args> call;
* //Loop over all unused indices to generate next index in permutation
* while ( iter != remaining.end() )
* {
* //Add next index to permutation
* unsigned i = *iter;
* indices[call().recurse] = i;
* //If complete permuation then print
* if (call().recurse == in.size() - 1)
* {
* for (auto index : indices) std::cout << index << " ";
* std::cout << std::endl;
* ++iter;
* }
* //If incomplete, recurse
* else
* {
* //Ctor is done at top of "recurse"
* auto ctor = [&](){ remaining.erase(call().i);
* (*call().iter) = remaining.begin();};
* //recurse
2. * call({ call().recurse + 1, &iter, i}, ctor);
* //Dtor is done after recurse returns
* call.dtor([&call](){*call().iter =
* remaining.insert(call().i).first; ++(*call().iter); });
* }
* }
* }
*}
* @endcode
*
* Notes: The class "Call" has been partially tested using combinatorics. It is
* found to be significantly faster when iteration replaces recursion using
* "Call". There is an expected overhead and timings for a Release build show
* that using "Call" takes 50% longer than std::next_permutation for 9!
* permuations. This may be due to the overhead of using a stack or may be due
* to the algorithm I devised for generating permutations.
*
* Compiled and tested under Linux Mint, using g++ 4.8.
*
* g++ options: -O0 -g3 -Wall -O0 -fopenmp -mavx -m64 -g -Wall -c
* -fmessage-length=0 -fno-omit-frame-pointer --fast-math
* -std=c++11 -I/opt/intel/vtune_amplifier_xe_2013/include/
*
* Linker options: -Wl,--no-as-needed -fopenmp
* -L/opt/intel/vtune_amplifier_xe_2013/lib64/
*
* Cmd line options: -lpthread -latomic -littnotify -ldl
*
* Documentation: Doxygen comments for interfaces, normal for impl.
*
* @file iteration.cpp
* @see
* @ref mainpage
*/
#include <vector>
#include <set>
#include <stack>
#include <memory>
#include <iostream>
#include <functional>
#include <chrono>
#include <algorithm>
/**
* addtogroup Recursion
* @{
*/
namespace Recursion
{
/** Dummy function acting as ctor/dtor
*
*/
void default_cdtor(void){}
/**
* @tparam T - struct containing function arguments to be pushed to stack
* @tparam Ctor - a void(*)(void) func acting as ctor
* @tparam Dtor - a void(*)(void) func acting as dtor
*/
template<typename T, typename Ctor = std::function<void(void)>,
typename Dtor = std::function<void(void)> >
struct Call
{
3. /**
* Holds stack of function arguments, ctors, dtors used to implement:
*
* pre-code - executed in ctor
*
* recursive-call(args...) - pushed to std::stack
*
* post-code - executed in dtor
*/
struct Func
{
/**
* Default ctor
*/
Func() :
m_ctor(&default_cdtor),
m_dtor(&default_cdtor)
{
}
/**
* Dtor
*/
~Func()
{
}
/**
* @return A stack of function arguments, ctors, dtors
*/
static std::stack<std::unique_ptr<Func>>& stack( void )
{
static std::stack<std::unique_ptr<Func>> ret_val;
return ret_val;
}
T m_t;
Ctor m_ctor;
Dtor m_dtor;
};
/**
* Default ctor
*
*/
Call()
{
}
/**
* Dtor
*/
~Call()
{
//Call dtor, pop "call" off stack
if (m_func.stack().empty() == false)
{
m_func.stack().top()->m_dtor();
m_func.stack().pop();
}
}
/**
* @param ctor {Ctor} - stores ctor to stack
4. */
void ctor(Ctor ctor)
{
m_func.stack().top()->m_ctor = ctor;
}
/**
* @param dtor {Dtor} - stores dtor to stack
*/
void dtor(Dtor dtor)
{
m_func.stack().top()->m_dtor = dtor;
}
/**
* Dummy termination condition
*/
static bool default_done(){ return false; }
/**
* @tparam Pred - bool(*)(...) condition for recursion termination
* @param t {const T&} - struct of function args to be pushed to stack
* @param pred {Pred} - condition for recursion termination
* @return bool - true iff recursion should terminate
*/
template<typename Pred>
static bool done(const T& t, Pred pred)
{
//Push function args to stack
static bool init = true;
if (init == true)
{
Func::stack().push(std::unique_ptr<Func>(new Func));
Func::stack().top()->m_t = t;
}
//Return true iff recursion termination should occur
bool ret_val = (init == false && Func::stack().empty()==true) || pred();
init = false;
return ret_val;
}
/**
* @param t {const T& } - function args to be pushed to stack
*/
static bool done(const T& t)
{
return done(t, &default_done);
}
/**
* @param t {T} - function args to be pushed to stack
* @param ctor {std::function<void(void)> } - ctor to be pushed to stack
*/
void operator()(T t, std::function<void(void)> ctor = default_cdtor)
{
//Push "call" to stack and then execute ctor
m_func.stack().push(std::unique_ptr<Func>(new Func));
m_func.stack().top()->m_t = t;
m_func.stack().top()->m_ctor = ctor;
ctor();
}
5. /**
* @return - function args from top of stack
*/
T& operator()()
{
return m_func.stack().top()->m_t;
}
/**
* @return - true iff stack is empty
*/
static bool empty()
{
return Func::stack().empty();
}
Func m_func;
};
}
/**
* @}
*/
/**
* addtogroup Tests
* @{
*/
namespace Tests
{
/**
* @param in {const std::vector<unsigned>&} - vectors of indices
* @param k {unsigned} - k in n choose k
* @param pos {unsigned} - length of sequence so far
* @param recurse {unsigned} - recursion depth
*
* Enumerates k out of n indices recursively. This code is O(k*n_choose_k).
*/
void recursive_combinations(const std::vector<unsigned>& in, unsigned k,
unsigned pos=0, unsigned recurse=0)
{
using namespace Recursion;
//Indices of combination & termination sequence
static std::vector<unsigned> indices(k);
static std::vector<unsigned> term(in.begin()+k, in.end());
//Loop from current length of sequence to end of input vector
for (unsigned i = pos; i < in.size()-(k-recurse-1); ++i)
{
//Add current index
indices[recurse] = i;
//If complete combination then print
if (recurse == k-1 || indices == term)
{
for (auto index : indices) std::cout << index << " ";
std::cout << std::endl;
}
//If not complete then recurse
else
{
recursive_combinations(in, k, i + 1, recurse + 1);
}
}
}
6. /**
* @param in {const std::vector<unsigned>&} - vectors of indices
* @param k {unsigned} - k in n choose k
* @param pos {unsigned} - length of sequence so far
* @param recurse {unsigned} - recursion depth
*
* Enumerates k out of n indices iteratively. This code is O(k*n_choose_k).
*/
void iterative_combinations(const std::vector<unsigned>& in, unsigned k,
unsigned pos=0, unsigned recurse = 0)
{
using namespace Recursion;
//Function args that would normally be pushed to stack
struct Args{ unsigned k; unsigned pos; unsigned recurse; };
//Indices of combination
static std::vector<unsigned> indices(k);
//Loop while stack non-empty
while (Call<Args>::done({ k, pos, recurse }) == false)
{
//Create object to push args to stack and pop them at end of iteration
Call<Args> call;
//Loop from current length of sequence to end of input vector
for (unsigned i=call().pos; i < in.size()-(k - call().recurse - 1); ++i)
{
//Add current index
indices[call().recurse] = i;
//If complete combination then print
if (call().recurse == k - 1)
{
for (auto index : indices) std::cout << index << " ";
std::cout << std::endl;
}
//If not complete then recurse
else
{
call({ k, ++call().pos, call().recurse + 1 });
}
}
}
}
/**
* @param in {const std::vector<unsigned>&} - vectors of indices
* @param recurse {unsigned} - recursion depth
*
* Enumerates all permutations recursively. This code is O(n*n!).
*/
void recursive_permutations(const std::vector<unsigned>& in, unsigned recurse=0)
{
using namespace Recursion;
//Vector of indices not yet "used up" in permuation sequence
static std::set<unsigned> remaining(in.begin(), in.end());
//Vector containing the permutation
static std::vector<unsigned> indices(in.size());
//Termination sequence
static std::vector<unsigned> term(in.rbegin(), in.rend());
//Loop over all unused indices to generate next index in permutation
for (auto iter = remaining.begin(); iter != remaining.end(); ++iter)
{
//Add next index to permutation
7. unsigned i = *iter;
indices[recurse] = i;
//If complete permuation then print
if (recurse == in.size()-1 || indices == term)
{
for (auto index : indices) std::cout << index << " ";
std::cout << std::endl;
}
//If incomplete, recurse
else
{
remaining.erase(i);
recursive_permutations(in, recurse + 1);
iter = remaining.insert(i).first;
}
}
}
/**
* @param in {const std::vector<unsigned>&} - vectors of indices
* @param recurse {unsigned} - recursion depth
*
* Enumerates all permutations iteratively. This code is O(n*n!).
*/
void iterative_permutations(const std::vector<unsigned>& in, unsigned recurse=0)
{
using namespace Recursion;
//Args to be pushed to stack
struct Args{unsigned recurse; std::set<unsigned>::iterator* iter;
unsigned i;};
//Vector of indices not yet "used up" in permuation sequence
static std::set<unsigned> remaining(in.begin(), in.end());
//Vector containing the permutation
static std::vector<unsigned> indices(in.size());
//Termination sequence
static std::vector<unsigned> term(in.rbegin(), in.rend());
auto iter = remaining.begin();
//Iterate until recursion termination condition hit
while (Call<Args>::done({ recurse }, [&]{return indices != term; }) )
{
// Create object to push and pop args for each iteration
Call<Args> call;
//Loop over all unused indices to generate next index in permutation
while ( iter != remaining.end() )
{
//Add next index to permutation
unsigned i = *iter;
indices[call().recurse] = i;
//If complete permuation then print
if (call().recurse == in.size() - 1)
{
for (auto index : indices) std::cout << index << " ";
std::cout << std::endl;
++iter;
}
//If incomplete, recurse
else
{
//Ctor is done at top of "recurse"
auto ctor = [&](){ remaining.erase(call().i);
(*call().iter) = remaining.begin();};
//recurse
8. call({ call().recurse + 1, &iter, i}, ctor);
//Dtor is done after recurse returns
call.dtor([&call](){*call().iter =
remaining.insert(call().i).first; ++(*call().iter); });
}
}
}
}
}
/**
* @}
*/
int main(void)
{
using namespace Tests;
using namespace std::chrono;
bool is_test_recursive = false;
std::vector<unsigned> in({ 0, 1, 2, 3, 4, 5 });
unsigned k = 3;
std::cout << "Generating " << k << " out of " << in.size()
<< " combinations: " << std::endl;
is_test_recursive ? recursive_combinations(in,k) :
iterative_combinations(in,k);
std::vector<unsigned> recursive({ 0, 1, 2, 3});
std::vector<unsigned> iterative({ 0, 1, 2, 3, 4, 5, 6, 7, 8 });
auto vec = is_test_recursive ? recursive : iterative;
std::cout << "Generating all permutations of " << vec.size()
<< " indices using class "Call": " << std::endl;
steady_clock::time_point begin, end;
begin=steady_clock::now();
is_test_recursive ? recursive_permutations(recursive) :
iterative_permutations(iterative);
end=steady_clock::now();
auto time_for_call = duration_cast<microseconds>(end - begin).count();
std::cout << "Class "Call" took: "
<< time_for_call << std::endl;
std::cout << "Generating all permutations of " << vec.size()
<< " indices using std::next_permuation: " << std::endl;
begin=steady_clock::now();
while(std::next_permutation(iterative.begin(), iterative.end()))
{
for(auto& i : iterative) std::cout << i << " ";
std::cout << std::endl;
}
end=steady_clock::now();
std::cout << "Class "Call" took: "
<< time_for_call << std::endl;
auto next_perm = duration_cast<microseconds>(end - begin).count();
std::cout << "std::next_permuation took: "
<< next_perm<< std::endl;
std::cout << "Time for Class "Call" / time for std::next_permutation: "
<< (double)time_for_call/(double)next_perm << std::endl;
return 0;
}