You can go quite a bit faster than ChronoTrigger's Updated Answer if you're friendly to the cache.
Bear in mind one thing with vectors, you want to iterate over as many elements as possible in a single go, doing exactly one thing to them.
So long as you don't mind messing with your original vector<T> a
, this solution will work:
Assumption 1
Your elements are evenly distributed about your array with a stride of K. K = 4 in your example: BBBBCCCCBBBBCCCC
Assumption 2
The size of your vector is a multiple of K. In your example, N = 24 = 6 * 4.
Assumption 3
The algorithm does not need to be stable. That is, the relative ordering of elements in b
does not need to be the same as it was while they were in a
. Same for c
.
(I did implement a stable version of this... you don't want to use it)
Approach
- Iterate over vector
a
with two iterators, the first begins at 0, the second at N
- Swap elements at iterators for K elements
- Repeat until iterators meet
BBBBCCCCBBBBCCCC
becomes CCCCCCCCBBBBBBBB
In layman's terms does the following:
- Maintain elements of
c
at the beginning
- Create vector
b
as the second half of a
- 'a' is now 'c'
It may appear to be more work, but because we're being really friendly to memory, it will in practice be much faster (take 1/3 of the time in my own experiments)
Code
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]);
}
}
Live Demo
In my test on 4 million integers, ChronoTrigger's first answer takes time T, his second answer takes time 0.6T, and my answer takes time 0.2T (two tenths the time!)
One additional benefit to this code is that it handles the case where there is not an equal number of elements to be distributed:
BBBBCCCCBBBB
Whereas the linked answer can only handle cases where there is an equal number of B
and C
elements.
Edit
I've also implemented a stable version of the above algorithm, following sp2danny's comment, however it's remarkably slow and you'd never want to use it because it has to iterate over the array O(n) times to perform the in-place swapping. At that point, I'd prefer ChronoTrigger's answer.
Code just in case someone wants to sit around for a while (it's also in the linked demo code):
// 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
}