我已经实现了一个有效的partition
函数,它的行为类似于Clojure中同名的有用工具。但是,我的很慢。虽然Clojure函数可以在几毫秒内完全分割一百万个元素,但即使使用50K元素也需要几秒钟。 (注意:我没有比较我的Clojure中可用的惰性函数调用,如评论中所述;我在谈论完全实现懒惰不适用)。
它和它的助手是:
template <typename T>
std::vector<T> take(int size, const std::vector<T>& coll){
if (size<0) return std::vector<T>();
auto sized = size > coll.size() ? coll.size() : size;
typename std::vector<T>::const_iterator first = coll.begin();
typename std::vector<T>::const_iterator last = first + sized;
return std::vector<T>(first,last);
}
template <typename T>
std::vector<T> drop(int size, const std::vector<T>& coll){
if (size<0) return std::vector<T>();
auto sized = size > coll.size() ? coll.size() : size;
typename std::vector<T>::const_iterator first = coll.begin()+sized;
typename std::vector<T>::const_iterator last = coll.end();
return std::vector<T>(first,last);
}
template <typename T>
std::vector<std::vector<T>> partition(int size, int step, const std::vector<T>& coll, bool showPartialEnd=false){
std::vector<std::vector<T>> ret;
ret.reserve(coll.size());
if (size<1||step<1) return ret;
std::vector<T> temp;
std::vector<T> remain=coll;
auto building=true;
do {
temp=std::move(take(size, remain));
building=(showPartialEnd?(temp.size()>0):(temp.size()==size));
if (building==true){
ret.push_back(temp);
remain=std::move(drop(step, remain));
}
} while (building==true);
return ret;
}
我喜欢关于我可以在哪里优化它的一些指示,所以它表现得更快。我尝试保留大小,因此push_back
不必每次都分配,但这没有区别(实际上我已经为大多数用例分配了大小)。我也认为showPartialEnd?
条件可能是一个障碍,因为它并不是真的需要在每个循环中发生,而是分裂成两个循环而只需要一次迭代条件来选择循环没有区别。
以下是一个使用示例:
void examples(){
auto x=range(30);
auto y=partition(3, 2, x);
std::for_each(y.begin(), y.end(), printVector<int>);
}
输出:
0 1 2
2 3 4
4 5 6
6 7 8
8 9 10
10 11 12
12 13 14
14 15 16
16 17 18
18 19 20
20 21 22
22 23 24
24 25 26
26 27 28
答案 0 :(得分:1)
性能问题的原因是您的drop
函数将算法转换为O(n^2)
而非线性时间,因为它必须不断创建包含所有元素的新remain
容器那个天堂还没有被分割出来。
我仍然试图了解代码的目的,以便提供有关如何修复/改进版本的建议。
完全未经测试(现在时间紧张),但我认为这应该是接近的:
std::vector<std::vector<T>> partition(int size, int step, const std::vector<T>& coll, bool showPartialEnd=false)
{
std::vector<std::vector<T>> ret;
if (size<1||step<1) return ret;
ret.reserve(coll.size() / step + 1);
std::vector<T>::const_iterator iter = coll.begin();
for(; ; iter += step)
{
// If you have enough elements left for an entire chunk, push it.
if((coll.end() - iter) >= size)
{
ret.push_back(std::vector<T>(iter, iter + size));
}
else if(showPartialEnd)
{
ret.push_back(std::vector<T>(iter, coll.end()));
break; // Remove if you want *all* partial ends instead of just the first one.
}
else break;
}
return ret;
}