用图案分割矢量
Splitting a vector by a pattern
我有一个向量"a",它包含大量数据,应该分为两个单独的向量"b"answers"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;
矢量"a"中数据的布局如下:
bbbbccccbbbbccccbbbbcccc
前4个字节应该进入向量"b",接下来的4个字节进入向量"c",等等
我可以迭代我的数据,并将每个元素push_back(或插入(到相应的向量中(基于它们在向量"a"中的索引(。然而,我尝试了一下,结果很慢。
C++中是否有更高性能的方法来实现这一点?
尝试预先分配要使用的内存以避免复制。假设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);
如果你对缓存友好,你可以比ChronoTrigger的更新答案快很多。
请记住,向量有一点,您希望在一次迭代中迭代尽可能多的元素,对它们只做一件事。
只要你不介意打乱你原来的vector<T> a
,这个解决方案就会起作用:
假设1
您的元素在数组中均匀分布,步长为K。在您的示例中,K=4:BBBBCCCCBBBBCCCC
假设2
向量的大小是K的倍数。在您的示例中,N=24=6*4。
假设3
该算法不需要是稳定的。也就是说,元素在b
中的相对排序不需要与它们在a
中时相同。与c
相同。
(我确实实现了这个的稳定版本……你不想使用它(
方法
- 用两个迭代器在向量
a
上迭代,第一个从0开始,第二个从N开始 - 将迭代器中的元素交换为K个元素
- 重复,直到迭代器相遇
BBBBCCCCBBBBCCCC
变为CCCCCCCCBBBBBBBB
用外行的话来说,做以下事情:
- 一开始维护
c
的元素 - 创建向量
b
作为a
的后半部分 - "a"现在是"c">
这可能看起来是更多的工作,但因为我们对记忆非常友好,所以在实践中它会更快(在我自己的实验中需要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.2T[十分之二的时间!]
该代码的一个额外好处是,它可以处理没有相等数量的元素要分发的情况:
BBBBCCCCBBBB
而链接答案只能处理B
和C
元素数量相等的情况。
编辑
根据sp2danny的评论,我还实现了上述算法的稳定版本,但它非常慢,你永远不想使用它,因为它必须迭代数组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
}
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和a开头之间的距离除以2来决定数据在B阵列中的位置。
- 如何在C++中从字符串中分割字符
- C++映射分割错误(核心转储)
- C++为线程工作动态地分割例程
- 由cin中的字符串中未捕获空白引起的分割错误
- 删除映射和分割错误中的一个过去结束元素
- 在指向函数中读取变量时出现分割错误
- 在链表中的第 n 位插入显示分割错误
- 较高值 n 的分割错误(例如 n=999997)
- 是否可以制作没有内部分割的cgal 3d多多马因网格?
- 尝试通过memcpy复制大尺寸浮点向量时的分割错误
- 分割错误:向量中的擦除功能
- 向量向量的分割错误
- 我在C++中编写了一个方法来打印树类的预序,但它显示了分割错误
- C ++分割错误,为什么使用"long long"我没有得到答案?
- 在尝试使用递归查找集合子集的总数时,我遇到了分割错误
- 分割错误 11:尝试使用 cin 输入 B[1] 时
- 集合布局上的 Qt 分割错误
- 高达20亿的筛子会产生分割错误
- 对向量使用推回函数时的分割错误
- 如何在使用OpenCV C++应用K均值后提取分割图像