假设我有vector<int>
intVec
和vector<vector<double> >
matrix
。我想对intVec
进行排序,并在C ++中相应地重新排序matrix
的第一个维度。我之前已经多次问过这个问题,但是这个案子有一个转折点。 vector<double>
复制费用很高,例如将intVec
和matrix
复制到vector<pair<int, vector<double> >
,对其进行排序并将其复制回来的效率甚至比平常低。
如果没有滚动我自己的自定义排序算法,我如何排序intVec
并在lockstep 中重新排序matrix
的第一维,而不复制matrix
的任何元素并调用{ {1}}的复制构造函数?
答案 0 :(得分:4)
vector<double>
复制费用昂贵,例如复制intVec和 矩阵到vector<pair<int, vector<double> >
,排序和 将它们复制回来的效率甚至比平常低。
获得所需优化的最简单方法是将<{1}}的元素交换到临时vector<vector<double>>
中,对其进行排序,然后将交换回原始载体中的新位置。
仍然会有超出严格必要的开销(例如构造和销毁空向量)。但是,没有复制任何向量,代码仍然与您已有的非常相似。所以,如果你的问题是复制的成本是正确的,问题就解决了。
在C ++ 11中,您可以双向移动而不是交换。我怀疑使用空向量移动和交换之间有很多性能差异,但我不确定是否存在。
答案 1 :(得分:1)
基于两个>
之间的空间,我猜你正在使用pre-C ++ 11 C ++。在C ++ 11中,std::sort
似乎尽可能地移动元素而不是复制。
您可以将自定义比较器传递给std::sort
。但是,即使你这样做,你也在做pair<int, vector<double> >
的Theta(n log n)副本。
我猜,基于不实际尝试它,您应该使用第二个{{1}对pair<int, vector<double> *>
(或pair<int, int>
,如果int
足够大)进行排序转换为获取适当的排列,然后使用int
的{{1}}成员函数应用排列,以避免复制向量内容。
答案 2 :(得分:1)
一个选项:创建一个std::vector<std::pair<int,size_t>>
,其中第一个元素是来自intVec的int,第二个元素是该元素的原始索引。然后对新矢量进行排序。然后将你的矩阵和intVec混合到对的第二个元素所指示的顺序(例如,通过一次传递,进行交换)。
答案 3 :(得分:0)
如果您不想复制vector<double>
项目向量,请为vector<double>
项创建指针或索引的向量。将其与主矢量一起排序。
然而,一点也不清楚你会获得性能提升,所以我建议你测量直接排序和智能排序,并进行比较。
示例:
#include <algorithm>
#include <vector>
#include <iostream>
using namespace std;
struct Mat
{
vector< vector< double > > items;
Mat( int const size )
: items( size, vector< double >( size ) )
{}
};
struct KeyAndData
{
int key;
vector< double > const* data;
friend bool operator<( KeyAndData const& a, KeyAndData const& b )
{
return a.key < b.key;
}
};
int main()
{
int const data[] = {3, 1, 4, 1, 5};
Mat m( 5 );
vector<int> v( 5 );
for( int i = 0; i < 5; ++i )
{
m.items[i][i] = v[i] = data[i];
}
vector< KeyAndData > sorted( 5 );
for( int i = 0; i < 5; ++i )
{
sorted[i].key = v[i];
sorted[i].data = &m.items[i];
}
sort( sorted.begin(), sorted.end() );
for( int i = 0; i < 5; ++i )
{
cout << sorted[i].key << ": ";
vector< double > const& r = *sorted[i].data;
for( int x = 0; x < 5; ++x )
{
cout << r[x] << " ";
}
cout << endl;
}
}
答案 4 :(得分:0)
显而易见的答案是将两个向量重组为一个vector<pair<int, vector<double> >
(因为数据明显紧密耦合)。
如果这不是一个选项,那么创建另一个索引向量并对其进行排序,而不是vec和矩阵。
答案 5 :(得分:0)
由于std::vector::swap
在固定时间内运行,您可以使用排序算法,该算法通过一系列交换(如快速排序)进行排序intVec
,同时在matrix
上执行相同的交换:
#include <iostream>
#include <vector>
#include <algorithm>
// Sorts intVec in [low, high) while also performing identical swaps on matrix.
void iqsort(std::vector<int> &intVec, std::vector<std::vector<double>> &matrix,
int low, int high) {
if (low >= high) return;
int pivot = intVec[low];
int nLow = low + 1;
int nHigh = high - 1;
while (nLow <= nHigh) {
if (intVec[nLow] <= pivot) {
++nLow;
} else {
std::swap(intVec[nLow], intVec[nHigh]);
std::swap(matrix[nLow], matrix[nHigh]);
--nHigh;
}
}
std::swap(intVec[low], intVec[nHigh]);
std::swap(matrix[low], matrix[nHigh]);
iqsort(intVec, matrix, low, nHigh);
iqsort(intVec, matrix, nLow, high);
}
int main() {
std::vector<int> intVec = {10, 1, 5};
std::vector<std::vector<double>> matrix = {{33.0}, {11.0}, {44.0}};
iqsort(intVec, matrix, 0, intVec.size());
// intVec is {1, 5, 10} and matrix is {{11.0}, {44.0}, {33.0}}
}
答案 6 :(得分:0)
我很确定 - 当你使用一些最新的编译器时(让我们说gcc 4.4及更高版本)---没有真正复制的东西:现在C ++标准库容器中的对象(大多数)总是被移动。因此恕我直言,没有必要担心昂贵的副本。
看一下下面的例子 - 它是在Debian下使用gcc 4.4.6编写的。正如您所看到的,在“重新排序”阶段,没有调用复制构造函数,也没有调用`operator =(...&amp; other)'。
#include <vector>
#include <iostream>
#include <iomanip>
class VeryExpensiveToCopy {
public:
explicit VeryExpensiveToCopy(long i) : id(i) { ++cnt_normal_cstr; }
// Just to be sure this is never used.
VeryExpensiveToCopy & operator=(VeryExpensiveToCopy & other) = delete;
VeryExpensiveToCopy(VeryExpensiveToCopy & other) = delete;
VeryExpensiveToCopy(VeryExpensiveToCopy && other) : id(other.id) {
++cnt_move_cstr;
}
VeryExpensiveToCopy & operator=(VeryExpensiveToCopy && other) {
id = other.id; ++cnt_op_as_move; return *this;
}
long get_id() const { return id; }
static void print_stats(std::string const & lref) {
std::cout << "[" << std::setw(20) << lref << "] Normal Cstr ["
<< cnt_normal_cstr
<< "] Move Cstr [" << cnt_move_cstr
<< "] operator=(&&) [" << cnt_op_as_move << "]" << std::endl;
}
private:
long id;
static long cnt_normal_cstr;
static long cnt_move_cstr;
static long cnt_op_as_move;
};
// Counts the number of calls.
long VeryExpensiveToCopy::cnt_normal_cstr { 0 };
long VeryExpensiveToCopy::cnt_move_cstr { 0 };
long VeryExpensiveToCopy::cnt_op_as_move { 0 };
int main() {
std::vector<VeryExpensiveToCopy> v;
VeryExpensiveToCopy::print_stats("Start");
for(auto i(0); i<100000; ++i) {
v.emplace_back(i);
}
VeryExpensiveToCopy::print_stats("After initialization");
for(auto i(0); i<100000-1; ++i) {
v[i] = std::move(v[i+1]);
}
VeryExpensiveToCopy::print_stats("After moving");
for(auto i(0); i<100000-1; ++i) {
if(v[i].get_id() != i+1) { abort(); }
}
VeryExpensiveToCopy::print_stats("After check");
return 0;
}
输出:
[ Start] Normal Cstr [0] Move Cstr [0] operator=(&&) [0]
[After initialization] Normal Cstr [100000] Move Cstr [131071] operator=(&&) [0]
[ After moving] Normal Cstr [100000] Move Cstr [131071] operator=(&&) [99999]
[ After check] Normal Cstr [100000] Move Cstr [131071] operator=(&&) [99999]