交换向量的值和索引
Swap values and indexes of a vector
我有一个 std::vector<int>
具有从 0 到 N 的连续打乱值,我想尽可能高效地交换每个值及其在向量中的位置。
示例:
v[6] = 3;
变成
v[3] = 6;
这是一个简单的问题,但我不知道如何处理它才能让它变得微不足道,最重要的是,让它变得非常快。非常感谢您的建议。
在编译时给定 N
并且给定数组只包含 [0,N)
中的每个索引一次,
它相对简单(只要它不必就地,如上面的评论中所述):
构造一个新数组,使v'[n] = find_index(v, n)
赋值给旧数组
在这里,我使用带有 std::index_sequence
的可变参数模板将其合并为一个任务:
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>{});
}
虽然这看起来并不复杂。为 [0,N)
中的每个 n 调用 find_index(arr, n)
将进行 N * (N+1) / 2 次比较(std::sort
只需要 N * log(N))。
但是,由于我们知道每个索引都存在于数组中,我们可以只填写一个索引数组
当我们遍历原始数组时,假设 T 是整数类型,我们也可以跳过一些 std::size_t <-> T 转换:
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;
}
我们仍在使用 space 的两倍,并对我们的数组进行一些随机排序的写入,
但本质上我们减少了 2*N 次赋值,而且代码比以前更简单了。
或者,我们也可以 std::sort
如果我们保留一份副本以在以下位置进行查找:
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];
});
}
第一版here,
第二个版本 here,
std::sort版本here
哪个更快的基准测试留给 reader ;)
作为练习
我有一个 std::vector<int>
具有从 0 到 N 的连续打乱值,我想尽可能高效地交换每个值及其在向量中的位置。
示例:
v[6] = 3;
变成
v[3] = 6;
这是一个简单的问题,但我不知道如何处理它才能让它变得微不足道,最重要的是,让它变得非常快。非常感谢您的建议。
在编译时给定 N
并且给定数组只包含 [0,N)
中的每个索引一次,
它相对简单(只要它不必就地,如上面的评论中所述):
构造一个新数组,使v'[n] = find_index(v, n)
赋值给旧数组
在这里,我使用带有 std::index_sequence
的可变参数模板将其合并为一个任务:
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>{});
}
虽然这看起来并不复杂。为 [0,N)
中的每个 n 调用 find_index(arr, n)
将进行 N * (N+1) / 2 次比较(std::sort
只需要 N * log(N))。
但是,由于我们知道每个索引都存在于数组中,我们可以只填写一个索引数组 当我们遍历原始数组时,假设 T 是整数类型,我们也可以跳过一些 std::size_t <-> T 转换:
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;
}
我们仍在使用 space 的两倍,并对我们的数组进行一些随机排序的写入, 但本质上我们减少了 2*N 次赋值,而且代码比以前更简单了。
或者,我们也可以 std::sort
如果我们保留一份副本以在以下位置进行查找:
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];
});
}
第一版here, 第二个版本 here, std::sort版本here
哪个更快的基准测试留给 reader ;)
作为练习