考虑以下函数median
:
real_t median(const std::initializer_list<real_t> vars) {
real_t tmp[15];
const unsigned x = vars.size() / 2;
if (x & 1) {
std::partial_sort_copy(vars.begin(), vars.end(), &tmp[0], &tmp[x]);
return tmp[x];
}
const unsigned y = x + 1;
std::partial_sort_copy(vars.begin(), vars.end(), &tmp[0], &tmp[y]);
return (tmp[x] + tmp[y]) / 2;
}
我正在使用部分排序来降低复杂性,因为我只需要对列表的一半进行排序。
此外,我假设std::partial_sort_copy
比std::partial_sort
或std::nth_element
更快,因为排序算法(It1!= It2)不需要改组。我的假设是否正确?
注意:假设real_t
可能是double
,所以请不要批评使用除法。
NBB:我使用-pedantic
,vars
已知不超过15个元素。
答案 0 :(得分:9)
使用以下代码
#include <chrono>
#include <iostream>
#include <thread>
#include <string>
#include <array>
#include <algorithm>
volatile int answer;
const int size = 15;
std::array<std::array<int, size>, 0x100> fresh_data;
std::array<std::array<int, size>, 0x100> data;
void naive(int n) {
auto & a = data[n];
std::sort(a.begin(), a.end());
answer = a[size / 2];
}
void fancy(int n) {
auto & a = data[n];
std::partial_sort(a.begin(), a.begin() + (size / 2 + 1), a.end());
answer = a[size / 2 ];
}
void ghoul(int n) {
auto & a = data[n];
std::array<int, size / 2 + 1> temp;
std::partial_sort_copy(a.begin(), a.end(), temp.begin(), temp.end());
answer = temp[size / 2];
}
void nthel(int n) {
auto & a = data[n];
std::nth_element(a.begin(), a.begin() + size / 2, a.end());
answer = a[size / 2];
}
void gen_data() {
for (auto & a : fresh_data)
for (auto & b : a)
b = rand();
}
void regen_data() {
data = fresh_data;
}
template <typename T>
void test(T f, std::string n) {
regen_data();
auto a = std::chrono::high_resolution_clock::now();
for (auto i = 0; i < 10000; ++i)
for (auto i = 0; i < 0x100; ++i)
f(i);
auto b = std::chrono::high_resolution_clock::now();
std::cout << n << ": " << std::chrono::duration_cast<std::chrono::milliseconds>(b - a).count() << std::endl;
}
int main() {
gen_data();
test(naive, " std::sort");
test(fancy, " std::partial_sort");
test(ghoul, "std::partial_sort_copy");
test(nthel, " std::nth_element");
}
我得到以下结果:
std::sort: 141
std::partial_sort: 359
std::partial_sort_copy: 831
std::nth_element: 149
在AMD Phenom II x4 2.5GHz上使用64位版本模式的Visual Studio 2013进行测试。
答案 1 :(得分:6)
如果我可以选择,我会选择Partial Quicksort。
但是如果你只需要比较这两个......那么部分排序对部分排序复制更好。在这里,您有关于这两种方法的更多信息:
在这里,您还可以找到Partial Quicksort的算法代码示例 - 它是在C和matlab中实现的:
答案 2 :(得分:1)
您是否测试过代码?
std::partial_sort_copy(vars.begin(), vars.end(), &tmp[0], &tmp[x]);
不会将任何内容复制到tmp[x]
,因为&tmp[x]
被视为半开放范围的结束(即,它刚刚超过最后一个有效元素)。因此,您的return
语句访问不确定或默认构造的数组元素。
尝试以下方法:
real_t median(const std::initializer_list<real_t> vars)
{
real_t tmp[15];
size_t siz = vars.size();
if ((siz == 0) || (15 < siz)) return 0; // or throw some sort of exception or ???
const unsigned x = vars.size() / 2;
std::partial_sort_copy(vars.begin(), vars.end(), &tmp[0], &tmp[x+1]);
if (siz % 2 == 0) {
return (tmp[x-1] + tmp[x]) / 2;
}
return tmp[x];
}
请注意,如果将initializer_list
作为数据源,则nth_element
或partial_sort
等就地修改算法将无效,因为初始化列表不能修改(参数是否标记为const
- initializer_list
中的迭代器是const
限定的。因此,必须通过在调用算法之前复制列表,或者使用执行复制作为其工作一部分的算法变体(例如partial_sort_copy()
)来使用标准算法函数来查找中值。