问题
I have a std::vector<int>
with contiguous shuffled values from 0 to N and want to swap, as efficiently as possible, each value with its position in the vector.
Example:
v[6] = 3;
becomes
v[3] = 6;
This is a simple problem, but I do not know how to handle it in order to make it trivial and, above all, very fast. Thank you very much for your suggestions.
回答1:
Given N
at compile time and given the array contains each index in [0,N)
exactly once,
it's relatively straight forward (as long as it doesn't have to be in-place, as mentioned in the comments above) :
Construct a new array so that v'[n] = find_index(v, n)
and assign it to the old one.
Here I used variadic templates with std::index_sequence
to roll it into a single assignment:
template<typename T, std::size_t N>
std::size_t find_index(const std::array<T,N>& arr, std::size_t index) {
return static_cast<std::size_t>(std::distance(arr.begin(), std::find(arr.begin(), arr.end(), index)));
}
template<typename T, std::size_t N, std::size_t... Index>
void swap_index_value(std::array<T,N>& arr, std::index_sequence<Index...> seq){
arr = { find_index(arr, Index)... };
}
template<typename Integer, std::size_t N>
void swap_index_value(std::array<Integer,N>& arr) {
swap_index_value(arr, std::make_index_sequence<N>{});
}
The complexity of this is does not look great though. Calling find_index(arr, n)
for each n in [0,N)
will take N * (N+1) / 2 comparisons total (std::sort
would only take N * log(N)).
However, since we know each index is present in the array, we could just fill out an array of indices as we walk over the original array, and assuming T is an integral type we can skip some std::size_t <-> T conversions, too:
template<typename T, std::size_t N>
void swap_index_value(std::array<T,N>& arr){
std::array<T, N> indices;
for (T i = 0; i < N; ++i)
indices[arr[i]] = i;
arr = indices;
}
We're still using twice the space and doing some randomly ordered writes to our array, but essentially we're down to 2*N assignments, and the code is simpler than before.
Alternatively, we could also std::sort
if we keep a copy to do lookups in:
template<typename T, std::size_t N>
void swap_index_value(std::array<T,N>& arr){
std::sort(arr.begin(), arr.end(), [copy = arr](const T& lhs, const T& rhs) {
return copy[lhs] < copy[rhs];
});
}
First version here, second version here, std::sort version here
Benchmarking which one is faster is left as an exercise to the reader ;)
来源:https://stackoverflow.com/questions/32896226/swap-values-and-indexes-of-a-vector