I'd like to improve the performance of PickPotatoes in the below code by using move instead of copy, but I can't figure out how to do that with insert and a boost::variant. In my actual use case, parsing the data takes about 75% of the time, and the real version of PickPotatoes takes about 25%, due to some slow copies. By improving PickPotatoes I should be able to get that down. Is it possible to move something out of a boost::variant and improve PickPotatoes?
#include <map>
#include "boost/variant.hpp"
#include <string>
#include <vector>
#include <functional>
struct tuber
{
int z;
std::vector<double> r;
};
int getZ(const tuber& t)
{
return t.z;
}
boost::variant<std::string, tuber> GrowPotato()
{
int z = std::rand() / (RAND_MAX / 10);
if (z < 2)
{
return "BAD POTATO";
}
else
{
tuber ret;
ret.z = z;
ret.r.resize(10000);
for (int i = 0;i < 10000;++i)
{
ret.r[i] = std::rand() / (RAND_MAX / 50);
}
return ret;
}
}
std::vector<boost::variant<std::string,tuber>> GrowPotatoes(int n)
{
std::vector<boost::variant<std::string, tuber>> ret;
ret.resize(n);
for (int i = 0; i < n; ++i)
{
ret[i] = GrowPotato();
}
return ret;
}
//could make this more efficient.
std::pair<std::vector<std::string>,std::multimap<int, tuber>> PickPotatoes(std::vector <boost::variant<std::string, tuber>> result)
{
std::pair<std::vector<std::string>,std::multimap<int,tuber>> ret;
int numTypTwo = 0;
for (const auto& item : result)
{
numTypTwo += item.which();
}
ret.first.resize(result.size() - numTypTwo);
int fstSpot = 0;
for (int i = 0; i < result.size();++i)
{
if (result[i].which())
{
ret.second.insert(std::pair<int, tuber>(getZ(boost::get<tuber>(result[i])), boost::get<tuber>(result[i])));
}
else
{
ret.first[fstSpot++] = std::move(boost::get<std::string>(result[i]));
}
}
return ret;
}
int main()
{
std::srand(0);
std::vector<boost::variant<std::string, tuber>> q= GrowPotatoes(5000);
std::pair<std::vector<std::string>, std::multimap<int, tuber>> z = PickPotatoes(q);
return 0;
}
The simplest win would be to move the parameter value:
std::pair<std::vector<std::string>, std::multimap<int, tuber>> z = PickPotatoes(std::move(q));
Indeed, it wins 14% of performance, roughly on my benchmarks. The rest heavily depends on what it all means, how it's going to be used.
Focus on reducing allocations (use a non-nodebased container if you can, e.g. boost::flat_multimap, sort explicitly, use string_view, parse into the desired datastructure instead of intermediate).
BONUS
I was able to shave off about 30% using:
std::pair<std::vector<std::string>, std::multimap<int, tuber> >
PickPotatoes(std::vector<boost::variant<std::string, tuber> >&& result) {
std::pair<std::vector<std::string>, std::multimap<int, tuber> > ret;
ret.first.reserve(result.size());
struct Vis {
using result_type = void;
void operator()(std::string& s) const {
first.emplace_back(std::move(s));
}
void operator()(tuber& tbr) const {
second.emplace(tbr.z, std::move(tbr));
}
std::vector<std::string>& first;
std::multimap<int, tuber>& second;
} visitor { ret.first, ret.second };
for (auto& element : result) {
boost::apply_visitor(visitor, element);
}
return ret;
}
Using emplace, avoiding repeated get<>, avoiding the loop to get the first size etc.
Related
I am writing a class for generating bitmask from a table of predefined element strings:
const std::unordered_map<std::string, int> flagMap
{ { "bananas", 0x1 }, { "apples", 0x2 }, { "oranges", 0x4 }, { "pears", 0x8 }};
int fruitMask(std::string & fruitName)
{
if(flagMap.count(fruitName) > 0)
return flagMap.at(fruitName);
else
return 0;
}
int fruitMask(const char * fruitName)
{
if(flagMap.count(fruitName) > 0)
return flagMap.at(fruitName);
else
return 0;
}
int fruitMask(std::vector<std::string> fruitNames)
{
int result = 0;
for(auto it=fruitNames.begin(); it!=fruitNames.end(); ++it)
{
if(flagMap.count(*it) > 0)
result = result | flagMap.at(*it);
}
return result;
}
int fruitMask(std::initializer_list<const char*> fruitNames)
{
int result = 0;
for(auto it=fruitNames.begin(); it!=fruitNames.end(); ++it)
{
if(flagMap.count(*it) > 0)
result = result | flagMap.at(*it);
}
return result;
}
When the code using these functions call the const char* or the std::initializer_list<const char*> versions of fruitMask, is there any way to make it work at compile time?
For instance:
constexpr int mask = flagMask({"oranges", "bananas"});
This will not compile because flagMask() is not constexpr, is there any way to make this work? This would require a constexpr unordered_map, I do not even know if this is possible.
Compile time strings were discussed here
Maybe not an answer per se but (hopefully) a helpful hint.
Its not only about unordered map in your case but about your keys, const char* does not really mean compile-time string in general case, would you consider changing key type? Lets consider enum-ed keys (and very not optimal quadratic search, sorry):
#include <utility>
enum class keys : char
{
foo,
bar,
baz,
lol
};
static constexpr std::pair<keys, int> flagMap[] = {
{keys::foo, 42},
{keys::bar, 24},
{keys::baz, 100500},
{keys::lol, 15234}
};
static constexpr int sum(std::initializer_list<keys> target)
{
int res{0};
for (auto key: target) {
for (int i = 0; i < 4; ++i)
{
res += (flagMap[i].first == key) ? flagMap[i].second : 0;
}
}
return res;
}
int main()
{
return sum({keys::foo, keys::baz});
}
Demo yields just
mov eax, 100542
ret
at -O1 and up
Let's say we have a function odd which is a bool(int) function. I'd like to execute this function in parallel but with different parameter (differ numbers).
bool odd(int i) { return (((i&1)==1)?true:false); }
Here's the code I'm trying to use (which works but has a wart).
std::size_t num = 256;
std::vector<bool> results(num);
std::vector<std::function<bool(int)>> funcs(num);
std::vector<std::packaged_task<bool(int)>> tasks(num);
std::vector<std::future<bool>> futures(num);
std::vector<std::thread> threads(num);
for (std::size_t i = 0; i < num; i++) {
results[i] = false;
funcs[i] = std::bind(odd, static_cast<int>(i));
tasks[i] = std::packaged_task<bool(int)>(funcs[i]);
futures[i] = tasks[i].get_future();
threads[i] = std::thread(std::move(tasks[i]),0); // args ignored
}
for (std::size_t i = 0; i < num; i++) {
results[i] = futures[i].get();
threads[i].join();
}
for (std::size_t i = 0; i < num; i++) {
printf("odd(%d)=%s\n", i, (results[i]?"true":"false"));
}
I'd like to get rid of the arguments to the thread creation, as they are dependent on the argument types of the function bool(int). I'd like to make a function template of this code and be able to make a massive parallel function executor.
template <typename _returnType, typename ..._argTypes>
void exec_and_collect(std::vector<_returnType>& results,
std::vector<std::function<_returnType(_argTypes...)>> funcs) {
std::size_t numTasks = (funcs.size() > results.size() ? results.size() : funcs.size());
std::vector<std::packaged_task<_returnType(_argTypes...)>> tasks(numTasks);
std::vector<std::future<_returnType>> futures(numTasks);
std::vector<std::thread> threads(numTasks);
for (std::size_t h = 0; h < numTasks; h++) {
tasks[h] = std::packaged_task<_returnType(_argTypes...)>(funcs[h]);
futures[h] = tasks[h].get_future();
threads[h] = std::thread(std::move(tasks[h]), 0); // zero is a wart
}
// threads are now running, collect results
for (std::size_t h = 0; h < numTasks; h++) {
results[h] = futures[h].get();
threads[h].join();
}
}
Then called like this:
std::size_t num = 8;
std::vector<bool> results(num);
std::vector<std::function<bool(int)>> funcs(num);
for (std::size_t i = 0; i < num; i++) {
funcs[i] = std::bind(odd, static_cast<int>(i));
}
exec_and_collect<bool,int>(results, funcs);
I'd to remove the zero in the std::thread(std::move(task), 0); line since it's completely ignored by the thread. If I do completely remove it, the compiler can't find the arguments to pass to the thread create and it fails.
You could just not be micromanaging/control freak in the generic code. Just take any task returntype() and let the caller handle the binding of arguments:
Live On Coliru
#include <thread>
#include <future>
#include <iostream>
#include <vector>
#include <functional>
bool odd(int i) { return (((i&1)==1)?true:false); }
template <typename _returnType>
void exec_and_collect(std::vector<_returnType>& results,
std::vector<std::function<_returnType()>> funcs
) {
std::size_t numTasks = std::min(funcs.size(), results.size());
std::vector<std::packaged_task<_returnType()>> tasks(numTasks);
std::vector<std::future<_returnType>> futures(numTasks);
std::vector<std::thread> threads(numTasks);
for (std::size_t h = 0; h < numTasks; h++) {
tasks[h] = std::packaged_task<_returnType()>(funcs[h]);
futures[h] = tasks[h].get_future();
threads[h] = std::thread(std::move(tasks[h]));
}
// threads are now running, collect results
for (std::size_t h = 0; h < numTasks; h++) {
results[h] = futures[h].get();
threads[h].join();
}
}
int main() {
std::size_t num = 8;
std::vector<bool> results(num);
std::vector<std::function<bool()>> funcs(num);
for (std::size_t i = 0; i < num; i++) {
funcs[i] = std::bind(odd, static_cast<int>(i));
}
exec_and_collect<bool>(results, funcs);
}
Note this is a quick job, I've seen quite a few things that are overly specific here still.
In particular all the temporary collections are just paper weight (you even move each tasks[h] out of the vector even before moving to the next task, so why keep a vector of dead bits?)
There's no scheduling at all; you just create new threads willy nilly. That's not gonna scale (also, you want pluggable pooling models; see the Executor specifications and Boost Async's implementation of these)
UPDATE
A somewhat more cleaned up version that demonstrates what unneeded dependencies can be shed:
no temporary vectors of packaged tasks/threads
no assumption/requirement to have std::function<> wrapped tasks (this removes dynamic allocations and virtual dispatch internally in the implementation)
no requirement that the results must be in a vector (in fact, you can collect them anywhere you want using a custom output iterator)
move-awareness (this is arguably a "complicated" part of the code seeing that there is no std::move_transform, so go the extra mile using std::make_move_iterator
Live On Coliru
#include <thread>
#include <future>
#include <iostream>
#include <vector>
#include <algorithm>
#include <boost/range.hpp>
bool odd(int i) { return (((i&1)==1)?true:false); }
template <typename Range, typename OutIt>
void exec_and_collect(OutIt results, Range&& tasks) {
using namespace std;
using T = typename boost::range_value<Range>::type;
using R = decltype(declval<T>()());
auto tb = std::make_move_iterator(boost::begin(tasks)),
te = std::make_move_iterator(boost::end(tasks));
vector<future<R>> futures;
transform(
tb, te,
back_inserter(futures), [](auto&& t) {
std::packaged_task<R()> task(std::forward<decltype(t)>(t));
auto future = task.get_future();
thread(std::move(task)).detach();
return future;
});
// threads are now running, collect results
transform(begin(futures), end(futures), results, [](auto& fut) { return fut.get(); });
}
#include <boost/range/irange.hpp>
#include <boost/range/adaptors.hpp>
using namespace boost::adaptors;
int main() {
std::vector<bool> results;
exec_and_collect(
std::back_inserter(results),
boost::irange(0, 8) | transformed([](int i) { return [i] { return odd(i); }; })
);
std::copy(results.begin(), results.end(), std::ostream_iterator<bool>(std::cout << std::boolalpha, "; "));
}
Output
false; false; false; false; false; false; false; false;
Note that you could indeed write
exec_and_collect(
std::ostream_iterator<bool>(std::cout << std::boolalpha, "; "),
boost::irange(0, 8) | transformed([](int i) { return [i] { return odd(i); }; })
);
and do without any results container :)
I'm attempting to create an algorithm in C++ which will give me all of the possible combinations of a set of list items (input in a map format). I want to avoid duplicates and make sure to cover all possible combinations. To simplify the example, here's what the input may look like:
map<string, vector<string> > sandwichMap;
sandwichMap["bread"].push_back("wheat");
sandwichMap["bread"].push_back("white");
sandwichMap["meat"].push_back("ham");
sandwichMap["meat"].push_back("turkey");
sandwichMap["meat"].push_back("roastbeef");
sandwichMap["veggie"].push_back("lettuce");
sandwichMap["sauce"].push_back("mustard");
I'd feed this map into the algorithm, and it should spit out a vector with all of the possible combinations (using one of each key type):
wheat+ham+lettuce+mustard
wheat+turkey+lettuce+mustard
wheat+roastbeef+lettuce+mustard
white+ham+lettuce+mustard
white+turkey+lettuce+mustard
white+roastbeef+lettuce+mustard
It needs to work for any map of string vectors. So far I've tried and gotten close, but I end up with duplicate combinations and missed combinations:
sandwichList getCombinations(sandwichMap sMap)
{
locList retList;
int totalCombos = 1;
for (sandwichMapIt i = sMap.begin(); i != sMap.end(); ++i)
{
totalCombos *= i->second.size();
}
retList.resize(totalCombos);
int locCount;
for (sandwichMapIt a = sMap.begin(); a != sMap.end(); ++a)
{
locCount = 0;
for (locListIt l = a->second.begin(); l != a->second.end(); ++l)
{
for (unsigned int i = 0; i < totalCombos / a->second.size(); ++i)
{
retList[i + a->second.size() * locCount] += *l;
}
locCount++;
}
}
return retList;
}
Any help would be greatly appreciated!
Updated code:
#include <vector>
#include <map>
#include <list>
#include <iostream>
typedef std::vector<std::string> strVec;
typedef std::list<std::string> strList;
typedef std::map<std::string, strVec> sandwichMap;
int main()
{
sandwichMap sMap;
sMap["bread"].push_back("wheat");
sMap["bread"].push_back("white");
sMap["meat"].push_back("ham");
sMap["meat"].push_back("turkey");
sMap["meat"].push_back("roastbeef");
sMap["veggie"].push_back("lettuce");
sMap["sauce"].push_back("mustard");
strList finalSandwichList;
for (sandwichMap::iterator i = sMap.begin(); i != sMap.end(); ++i)
{
strList tmpSandwich;
for (strVec::iterator j = i->second.begin(); j != i->second.end(); ++j)
{
if (finalSandwichList.empty())
{
tmpSandwich.push_back(*j);
}
else
{
for (strList::iterator k = finalSandwichList.begin(); k != finalSandwichList.end(); ++k)
tmpSandwich.push_back(*k + "+" + *j);
}
}
tmpSandwich.swap(finalSandwichList);
}
for (strList::iterator i = finalSandwichList.begin(); i != finalSandwichList.end(); ++i)
{
std::cout << *i << std::endl;
}
return 0;
}
//solution
std::list<std::string> result;
for(auto i=sandwichMap.begin(); i!=sandwichMap.end(); ++i) {
std::list<std::string> new_result;
for(auto j=i->second.begin(); j!=i->second.end(); ++j) {
if(result.empty())
new_result.push_back(*j);
else
for(auto k=result.begin(); k!=result.end(); ++k)
new_result.push_back(*k + "+" + *j);
}
new_result.swap(result);
}
This should work :
#include<iostream>
#include<map>
#include<string>
#include<algorithm>
using namespace std;
map<string, vector<string>> sMap;
vector<string> add;
int sett[200], countt;
void solve(map<string, vector<string>>::iterator itt, int ct, vector<string> addd){
vector<string> tmp = itt->second;
if(ct == countt){
for(int j=0;j<addd.size();j++){
cout<<addd[j]<<" ";
}
cout<<endl;
return;
}
itt++;
for(int i=0;i<tmp.size();i++){
//cout<<tmp[i]<<" ";
addd.push_back(tmp[i]);
solve(itt, ct+1, addd);
vector<string>::iterator tempIt = addd.end();
addd.erase(tempIt--);
}
}
int main(){
sMap["bre"].push_back("wh");
sMap["bre"].push_back("whi");
sMap["me"].push_back("ham");
sMap["me"].push_back("tur");
sMap["me"].push_back("rr");
sMap["veg"].push_back("let");
sMap["sau"].push_back("mus");
countt = sMap.size();
solve(sMap.begin(), 0, add);
return 0;
}
I have used backtracking to evaluate every possible combination.
Note : it is in c++11 you might need to change some part of the code for lower version of c++
link to output : http://ideone.com/Ou2411
The code is kinda long because of the helper methods, but it does the job:
#include <vector>
#include <string>
#include <map>
#include <iostream>
using namespace std;
template <class T>
vector<T> Head(const vector<T> &v) {
return vector<T>(v.begin(), v.begin() + 1);
}
template <class T>
vector<T> Tail(const vector<T> &v) {
auto first = v.begin() + 1;
auto last = v.end();
return vector<T>(first, last);
}
template <class T>
vector<T> Concat(const vector<T> &v1, const vector<T> &v2) {
vector<T> result = v1;
result.insert(result.end(), v2.begin(), v2.end());
return result;
}
vector<vector<string>> CombineVectorWithScalar(const vector<vector<string>> &v, const string &scalar) {
vector<vector<string>> result = v;
for (unsigned i = 0; i < v.size(); i++) {
result[i].push_back(scalar);
}
return result;
}
vector<vector<string>> CombineVectorWithVector(const vector<vector<string>> &v1, const vector<string> &v2) {
if (v2.empty()) {
return vector<vector<string>>();
}
else {
auto headCombination = CombineVectorWithScalar(v1, v2.front());
auto tailCombination = CombineVectorWithVector(v1, Tail(v2));
return Concat(headCombination, tailCombination);
}
}
vector<string> GetKeys(const map<string, vector<string>> &mp) {
vector<string> keys;
for (auto it = mp.begin(); it != mp.end(); ++it) {
keys.push_back(it->first);
}
return keys;
}
vector<vector<string>> CombineMapValues(const map<string, vector<string>> &mp) {
vector<string> keys = GetKeys(mp);
vector<vector<string>> result;
auto &firstVector = mp.begin()->second;
for (auto it = firstVector.begin(); it != firstVector.end(); ++it) {
vector<string> oneElementList;
oneElementList.push_back(*it);
result.push_back(oneElementList);
}
vector<string> restOfTheKeys = Tail(keys);
for (auto it = restOfTheKeys.begin(); it != restOfTheKeys.end(); ++it) {
auto ¤tVector = mp.find(*it)->second;
result = CombineVectorWithVector(result, currentVector);
}
return result;
}
void PrintCombinations(const vector<vector<string>> & allCombinations) {
for (auto it = allCombinations.begin(); it != allCombinations.end(); ++it) {
auto currentCombination = *it;
for (auto itInner = currentCombination.begin(); itInner != currentCombination.end(); ++itInner) {
cout << *itInner << " ";
}
cout << endl;
}
}
int main() {
map<string, vector<string> > sandwichMap;
sandwichMap["bread"].push_back("wheat");
sandwichMap["bread"].push_back("white");
sandwichMap["meat"].push_back("ham");
sandwichMap["meat"].push_back("turkey");
sandwichMap["meat"].push_back("roastbeef");
sandwichMap["veggie"].push_back("lettuce");
sandwichMap["sauce"].push_back("mustard");
auto allCombinations = CombineMapValues(sandwichMap);
PrintCombinations(allCombinations);
return 0;
}
void generate_all(std::map<std::string,std::vector<std::string>>::iterator start,
std::vector<std::string::iterator> accomulator,
std::map<std::string,std::vector<std::string>>& sMap){
for (auto it=start; it!=sMap.end(); ++it){
for (auto jt=it->second.begin(); jt!=it->second.end(); jt++){
generate_all(start+1,accomulator.pus_back[jt],sMap);
}
}
if (accomulator.size() == sMap.size()){
// print accomulator
}
}
Call with generate_all(sMap.begin(),aVector,sMap);
If the map is too big to go recursively, you can always generate an equivalent iterative code.
This solution is not recursive. Basically what it does is the following:
Compute how many combinations are actually possible
Know that for each key in the map, you're going to have to add nrCombinations/nrItemsInKey of them in total.
You can see it as a tree growing, branching more and more the more keys you have visited.
If you keep track of how many there are, how spaced they are and where they start you can automatically fill all combinations.
Code
#include <vector>
#include <iostream>
#include <map>
#include <string>
int main() {
std::map<std::string, std::vector<std::string> > sandwichMap;
sandwichMap["bread"].push_back("wheat");
sandwichMap["bread"].push_back("white");
sandwichMap["meat"].push_back("ham");
sandwichMap["meat"].push_back("turkey");
sandwichMap["meat"].push_back("roastbeef");
sandwichMap["veggie"].push_back("lettuce");
sandwichMap["sauce"].push_back("mustard");
sandwichMap["sauce"].push_back("mayo");
// Compute just how many combinations there are
int combinationNr = 1;
for ( auto it : sandwichMap ) {
combinationNr *= it.second.size();
}
std::vector<std::vector<std::string>> solutions(combinationNr);
// We start with empty lists, thus we only have one cluster
int clusters = 1, clusterSize = combinationNr;
for ( auto category : sandwichMap ) {
int startIndex = 0;
int itemsNr = category.second.size();
int itemsPerCluster = clusterSize / itemsNr;
for ( auto item : category.second ) {
for ( int c = 0; c < clusters; ++c ) {
for ( int i = 0; i < itemsPerCluster; ++i ) {
// We sequentially fill each cluster with this item.
// Each fill starts offset by the quantity of items
// already added in the cluster.
solutions[startIndex+i+c*clusterSize].push_back(item);
}
}
startIndex += itemsPerCluster;
}
clusters *= itemsNr;
clusterSize = combinationNr / clusters;
}
for ( auto list : solutions ) {
for ( auto element : list ) {
std::cout << element << ", ";
}
std::cout << "\n";
}
return 0;
}
I have two lists of pointers to a data structure X, the algorithm is very simple:
It loops over the first list A and try to find the the first matching element in list B. The requirement is to have at least 50k elements in each list:
#include <iostream>
#include <memory>
#include <chrono>
#include <vector>
#include <algorithm>
#include <string>
struct X {
std::string field_1;
std::string field_2;
std::string field_3;
std::string field_4;
X(std::string f1, std::string f2, std::string f3, std::string f4)
: field_1(f1)
, field_2(f2)
, field_3(f3)
, field_4(f4)
{};
bool equal(const std::shared_ptr<X>& x) {
return (x->field_1 == field_1) &&
(x->field_2 == field_2) &&
(x->field_3 == field_3) &&
(x->field_4 == field_4);
};
X *match = nullptr;
};
typedef std::shared_ptr<X> X_ptr;
class Timer
{
public:
Timer(std::string name) : beg_(clock_::now()), name_(name) {}
~Timer() {
std::cout << "Elapsed(" << name_ << "): " << elapsed() << std::endl;
}
void reset() { beg_ = clock_::now(); }
double elapsed() const {
return std::chrono::duration_cast<second_>
(clock_::now() - beg_).count();
}
private:
typedef std::chrono::high_resolution_clock clock_;
typedef std::chrono::duration<double, std::ratio<1> > second_;
std::chrono::time_point<clock_> beg_;
std::string name_;
};
std::string random_string(size_t length)
{
auto randchar = []() -> char
{
const char charset[] =
"0123456789"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ";
const size_t max_index = (sizeof(charset) - 1);
return charset[rand() % max_index];
};
std::string str(length, 0);
std::generate_n(str.begin(), length, randchar);
return str;
}
int main()
{
Timer t("main");
std::vector <X_ptr> list_A;
std::vector <X_ptr> list_B;
const int MAX_ELEM = 50000;
list_A.reserve(MAX_ELEM);
list_B.reserve(MAX_ELEM);
{
Timer t("insert");
for (int i = 0; i < MAX_ELEM; i++) {
list_A.push_back(X_ptr(new X{ random_string(2), random_string(2), random_string(2), random_string(2) }));
list_B.push_back(X_ptr(new X{ random_string(2), random_string(2), random_string(2), random_string(2) }));
}
}
{
Timer t("match");
std::for_each(list_A.begin(), list_A.end(), [list_B](X_ptr& a) {
auto found_b = std::find_if(list_B.begin(), list_B.end(), [a](const X_ptr& b) {
return a->equal(b);
});
if (found_b != list_B.end()) {
a->match = found_b->get();
std::cout << "match OK \n";
}
});
}
}
on my machine the program is running extremly slow:
Elapsed(insert): 0.05566
Elapsed(match): 98.3739
Elapsed(main): 98.452
Would appreciate it if you can think of any other way to optimize it to run faster.
You are using vectors so each lookup into list_B takes O(n), where n is the number of elements in B. This means the total algorithm is O(m*n), if m is the number of elements in list_A. Thus if m and n a similar in size, you have a O(n^2) algorithm. That is too slow for any large n. To fix this, convert list_B into a unordered_map, (you can do this as part of this algorithm as the conversion is O(n)) where an element in the map's key is an element from list B and the value anything, say 0. You can then perform lookups into the map in O(1) time using find() on the map. Thus your algorithm becomes O(n), way better that O(n^2).
For example
std::unordered_map< X_ptr, int > value_map;
Time r t("match");
std::for_each(list_B.begin(), list_B.end(), [&](X_ptr& b) {
value_map[b] = 0;
});
std::for_each(list_A.begin(), list_A.end(), [value_map](X_ptr& a) {
auto found_b = value_map.find( a );
if ( found_b != value_map.end() )
{
a->match = found_b->first.get();
std::cout << "match OK \n";
}
});
}
Your Version:
Elapsed(insert): 0.0758608
Elapsed(match): 182.899
Elapsed(main): 182.991
New Version:
Elapsed(insert): 0.0719907
Elapsed(match): 0.0388562
Elapsed(main): 0.130884
You may use something like the following:
std::sort(list_B.begin(), list_B.end(), deref_less<X>);
{
Timer t("match");
for (const auto& a : list_A) {
auto it = std::lower_bound(list_B.begin(), list_B.end(), a, deref_less<X>);
if (it != list_B.end() && **it == *a) {
a->match = it->get();
std::cout << "match OK \n";
}
}
}
Live example.
Very simple question: is there a smart way of creating a subvector from regularly spaced elements of another vector with the STL?
In short, is it possible to write the following code with a STL algorithm:
int inc = 2;
std::vector<double> v_origin;
std::vector<double> v_dest;
for (int i = 0; i < v_origin.size(); i+= inc)
v_dest.push_back(v_origin[i]);
Like I would write in Matlab or Python something like:
v_dest = v_origin[0:inc:end];
As a general solution, you could define a stride iterator. If you use Boost.Range, then it already as a strided range adaptor.
Example:
#include <vector>
#include <iostream>
#include <boost/range/adaptors.hpp>
#include <boost/range/algorithm.hpp>
int main()
{
int inc = 2;
std::vector<double> v_origin;
std::vector<double> v_dest;
for (int i = 0; i < 10; ++ i)
v_origin.push_back(i);
boost::copy(v_origin | boost::adaptors::strided(2),
std::back_inserter(v_dest));
// ^ In Python: v_dest[] = v_origin[::2]
boost::copy(v_dest, std::ostream_iterator<double>(std::cout, ", "));
}
(Creating another answer as it's a different approach.)
If you just want to push_back a strided slice of another container, and does not intend to use that lst[a:b:c] concept anywhere else, it is probably easier to write a generic copy-like function:
template <typename InputIterator, typename OutputIterator>
void copy_strided(InputIterator begin, InputIterator end,
OutputIterator result, size_t stride)
{
assert(stride >= 1);
for (size_t i = stride; begin != end; ++ i, ++ begin)
{
if (i == stride)
{
*result = *begin;
++ result;
i = 0;
}
}
}
Usage:
#include <vector>
#include <cassert>
#include <iostream>
#include <algorithm>
#include <iterator>
int main()
{
int inc = 2;
std::vector<double> v_origin;
std::vector<double> v_dest;
for (int i = 0; i < 10; ++ i)
v_origin.push_back(i);
copy_strided(v_origin.begin(), v_origin.end(), std::back_inserter(v_dest), inc);
std::copy(v_dest.begin(), v_dest.end(), std::ostream_iterator<double>(std::cout, ", "));
}
struct RemoveNth
{
RemoveNth(int incin)
{
count = 0;
inc = incin;
}
bool operator()(double x )
{
return count++ % inc == 0;
}
int count;
int inc;
};
int main()
{
int inc = 2;
std::vector<double> v_origin;
std::vector<double> v_dest;
for ( int i = 0 ; i < 100; ++i )
v_origin.push_back( i );
v_dest = v_origin;
RemoveNth helper(3);
std::vector<double>::iterator newend =
std::remove_if (v_dest.begin() , v_dest.end(), helper);
v_dest.erase( newend , v_dest.end() );
return 0;
}
Something like above might work.
In C++11 you can use std::copy_if and instead of the separate functor you can use inline lambdas like so
template<typename T, typename U>
void copynth( T begin , T end , U dest , int n )
{
int count = 0;
std::copy_if( begin , end , dest ,
[&count,n]( double x )
{
return count++ % n == 0;
});
}
int main()
{
int inc = 2;
std::vector<double> v_origin;
std::vector<double> v_dest;
for ( int i = 0 ; i < 100; ++i )
v_origin.push_back( i );
int count = 0;
copynth( v_origin.begin() , v_origin.end() , std::back_inserter(v_dest) , 4);
return 0;
}
There does not exist anything in the standard library that is meant for this task specifically.
Below is my own generic implementation. There is a separate implementation for random access iterator and for other input iterators.
#include <iterator>
namespace detail {
template <class SourceIter, class OutIter>
void strided_copy_aux(SourceIter from, SourceIter to, OutIter out, unsigned step, std::random_access_iterator_tag)
{
SourceIter end = (to - from) / step * step + from;
for (; from < end; from += step ) {
*out = *from;
}
if (end < to) {
*out = *end;
}
}
template <class SourceIter, class OutIter>
void strided_copy_aux(SourceIter from, SourceIter to, OutIter out, unsigned step, std::input_iterator_tag)
{
while (from != to) {
*out = *from;
for (unsigned i = 0; i != step; ++i) {
++from;
if (from == to) break;
}
}
}
}
template <class SourceIter, class OutIter>
void strided_copy(SourceIter from, SourceIter to, OutIter out, unsigned step)
{
detail::strided_copy_aux(from, to, out, step, typename std::iterator_traits<SourceIter>::iterator_category());
}
Usage example: http://ideone.com/1Wmq3