我有一个矢量' a'它包含大量数据,应分成两个单独的向量' b'和' c'。
vector<unsigned char> a; //contains a lot of data
vector<unsigned char> b; //data should be split into b and c
vector<unsigned char> c;
向量中的数据布局&#39; a&#39;如下:
bbbbccccbbbbccccbbbbcccc
前4个字节应该进入vector&#39; b&#39;,接下来的4个字节进入vector&#39; c&#39;等等。
我可以迭代我的数据并将每个元素push_back(或插入)到相应的向量中(基于它们在向量中的索引&#39; a&#39;)。但是,我尝试了这个,结果很慢。
在C ++中是否有更高效的方法来实现这一目标?
答案 0 :(得分:9)
尝试预先分配您将要使用的内存以避免复制。假设a
包含完整序列,您可以执行以下操作:
b.reserve(a.size() / 2);
c.reserve(a.size() / 2);
for (auto it = a.begin(); it < a.end(); it += 8) {
b.insert(b.end(), it, it + 4);
c.insert(c.end(), it + 4, it + 8);
}
<强>更新强>
如果您不介意修改原始向量a
,可以使用它来保留其中一个子序列并避免分配更多内存。假设a
包含完整序列:
b.reserve(a.size() / 2);
auto writer = a.begin();
for (auto reader = a.cbegin(); reader < a.cend(); reader += 8, writer += 4) {
b.insert(b.end(), reader, reader + 4);
std::copy(reader + 4, reader + 8, writer);
}
a.resize(a.size() / 2);
答案 1 :(得分:5)
如果您对缓存友好,则可以比ChronoTrigger's Updated Answer快得多。
请记住向量中的一件事,你想在一次性中迭代尽可能多的元素,对它们做一件事。
只要你不介意弄乱原来的vector<T> a
,这个解决方案就可以了:
你的元素在你的数组中均匀分布,大步为K. K = 4,例如:BBBBCCCCBBBBCCCC
矢量的大小是K的倍数。在您的示例中,N = 24 = 6 * 4.
算法不需要稳定。也就是说,b
中元素的相对排序不必与a
中的元素相对排序。与c
相同。
(我确实实现了这个的稳定版本......你不想使用它)
a
,第一个从0开始,第二个从N BBBBCCCCBBBBCCCC
变为CCCCCCCCBBBBBBBB
外行人的说法如下:
c
的元素b
作为a
看起来似乎工作量更大,但因为我们对记忆非常友好,所以实际上会更快(在我自己的实验中占1/3)
void FastSplit(std::vector<int>& a, int stride)
{
auto asize = a.size();
size_t j = asize-1;
if ((asize / stride) % 2 == 1)
{
j -= stride;
asize = asize - (asize+stride)/2; // asize now represents number of C elements
}
else
{
asize /= 2; // asize now represents number of C elements
}
for (size_t i=0; i < j; i+=stride, j-=stride)
{
for (size_t k = 0; k < stride; ++k, ++i, --j)
{
std::swap(a[i], a[j]);
}
}
在我对400万个整数的测试中,ChronoTrigger的第一个答案需要时间 T ,他的第二个答案需要时间 0.6T ,我的答案需要时间 0.2T (十分之二的时间!)
此代码的另一个好处是它处理的情况是没有相同数量的元素要分发:
BBBBCCCCBBBB
而链接的答案只能处理B
和C
元素数相等的情况。
我已经在sp2danny's comment之后实现了上述算法的稳定版本,但是它非常慢并且你永远不想使用它,因为它必须遍历数组O(n)次到执行就地交换。那时,我更喜欢ChronoTrigger的答案。
代码以防万一有人想坐一会儿(它也在链接的演示代码中):
// Slow!
void StableSwappingSplit(std::vector<int>& a, int stride)
{
auto asize = a.size();
auto starti = 0;
while(starti < asize)
{
for (size_t i=starti, j = starti+stride;j < asize-starti; i+=stride, j+=stride)
{
for (size_t k = 0; k < stride; ++k, ++i, ++j)
{
std::swap(a[i], a[j]);
}
}
starti += stride;
}
if ((asize / stride) % 2 == 1)
{
asize = asize - (asize+stride)/2; // asize now represents number of C elements
}
else
{
asize /= 2; // asize now represents number of C elements
}
//std::cout << "After swapping: \n";
//PrintVec(a);
std::vector<int> b(std::make_move_iterator(a.begin() + asize), std::make_move_iterator(a.end())); // copy second half into b
a.resize(asize);
//a is now c
}
答案 2 :(得分:0)
vector<char>b(a.size()/2),c(a.size()/2);
for(auto i=a.begin();i<a.end();i=i+8){
move(i,i+4,b.begin()+(i-a.begin())/2);
move(i+4,i+8,c.begin()+(i-a.begin())/2);
}
此处移动与复制相同。因此,我们将前半部分移动到B数组,将第二部分移动到C数组,使用i作为out索引,并使用i和开头之间的距离除以2来确定数据在b数组中的位置。