我编写了一个小型java程序,它对排序算法(合并排序,选择排序)进行基准测试,并显示他们对人物对象(名称,升序)进行排序所需的时间。
该程序的C ++版本比java版本慢至少4倍。有几位开发人员说他们通过优化代码来排序java。释放模式,/ O2,64位,....我已经完成了那些调整。
我的排序算法是否有任何低效率(特别是合并排序)?
//create a subvector
template <typename T> std::vector<T> splitVec(std::vector<T> main, size_t from, size_t to) {
std::vector<T>::const_iterator first = main.begin() + from;
std::vector<T>::const_iterator last = main.begin() + to;
std::vector<T> erg(first, last);
return erg;
}
//merge sort - sorting process
template <typename T> std::vector<T> merge(std::vector<T> m1, std::vector<T> m2) {
unsigned int posA = 0, posB = 0;
std::vector<T> erg;
while (posA < m1.size() && posB < m2.size()) {
if (m1.at(posA).compareTo(m2.at(posB)) <= 0) {
erg.push_back(m1.at(posA));
posA++;
}
else {
erg.push_back(m2.at(posB));
posB++;
}
}
while (posA < m1.size()) {
erg.push_back(m1.at(posA));
posA++;
}
while (posB < m2.size()) {
erg.push_back(m2.at(posB));
posB++;
}
return erg;
}
//merge sort-split up vectors
template <typename T> std::vector<T> mergeSort(std::vector<T> pers) {
if (pers.size() > 1) {
//Split pers into two equally sized vectors
std::vector<T> p1(splitVec(pers, 0, pers.size()/2));
std::vector<T> p2(splitVec(pers, (pers.size()/2), pers.size()));
return merge(mergeSort(p1), mergeSort(p2));
}
else
return pers;
}
提前致谢
答案 0 :(得分:3)
不要传递矢量。不是通过价值,而是通过参考。传递迭代器:
template <class Iter>
void sort(Iter first, Iter last) {
...
}
sort(my_vector.begin(), my_vector.end();
要分割范围,只需计算中间值:
template <class Iter>
Iter mid(Iter first, Iter last) {
return first + (last - first) / 2;
}
这假设代码仍在对某种容器中保存的值进行排序(在原始代码中为std::vector
),因此迭代器是随机访问迭代器。
答案 1 :(得分:1)
通过引用传递向量。这应该会显着提高性能。
当您按值传递矢量时,每次都会复制它(在每个步骤中添加O(n)的复杂度)
答案 2 :(得分:1)
通过引用传递源数据而不是复制它将是一个巨大的改进。
此外,您应reserve
erg
空格,否则您在添加更多内容时会反复重新分配和复制所有元素。
答案 3 :(得分:1)
使用push_back而不是索引很慢。一次性分配工作数组或向量,并索引到该数组,可以消除所有这些递归分配。使用一对相互递归的函数消除了在合并后必须复制数据的过程。
自下而上的合并排序会稍快一些,虽然自下而上的合并排序通常是大多数库使用的(例如std :: stable_sort),但自上而下合并排序似乎是课堂教学的内容。
自顶向下合并排序的示例模板,适用于数组或向量(将向量作为指向第一个元素的指针传递)。
template <typename T>
void TopDownSplitMergeAtoA(T a[], T b[], size_t ll, size_t ee);
template <typename T>
void TopDownSplitMergeAtoB(T a[], T b[], size_t ll, size_t ee);
template <typename T>
void TopDownMerge(T a[], T b[], size_t ll, size_t rr, size_t ee);
template <typename T>
void MergeSort(T a[], size_t n) // entry function
{
if(n < 2) // if size < 2 return
return;
T *b = new T[n];
TopDownSplitMergeAtoA(a, b, 0, n);
delete[] b;
}
template <typename T>
void TopDownSplitMergeAtoA(T a[], T b[], size_t ll, size_t ee)
{
if((ee - ll) == 1) // if size == 1 return
return;
size_t rr = (ll + ee)>>1; // midpoint, start of right half
TopDownSplitMergeAtoB(a, b, ll, rr);
TopDownSplitMergeAtoB(a, b, rr, ee);
TopDownMerge(b, a, ll, rr, ee); // merge b to a
}
template <typename T>
void TopDownSplitMergeAtoB(T a[], T b[], size_t ll, size_t ee)
{
if((ee - ll) == 1){ // if size == 1 copy a to b
b[ll] = a[ll];
return;
}
size_t rr = (ll + ee)>>1; // midpoint, start of right half
TopDownSplitMergeAtoA(a, b, ll, rr);
TopDownSplitMergeAtoA(a, b, rr, ee);
TopDownMerge(a, b, ll, rr, ee); // merge a to b
}
template <typename T>
void TopDownMerge(T a[], T b[], size_t ll, size_t rr, size_t ee)
{
size_t o = ll; // b[] index
size_t l = ll; // a[] left index
size_t r = rr; // a[] right index
while(1){ // merge data
if(a[l] <= a[r]){ // if a[l] <= a[r]
b[o++] = a[l++]; // copy a[l]
if(l < rr) // if not end of left run
continue; // continue (back to while)
while(r < ee) // else copy rest of right run
b[o++] = a[r++];
break; // and return
} else { // else a[l] > a[r]
b[o++] = a[r++]; // copy a[r]
if(r < ee) // if not end of right run
continue; // continue (back to while)
while(l < rr) // else copy rest of left run
b[o++] = a[l++];
break; // and return
}
}
}
答案 4 :(得分:0)
你错过了一个非常大的优化。您按值而不是按引用传递所有向量。这意味着每个函数调用都在复制向量,效率非常低。
由于java中的所有内容都是指针,因此您的java代码不会生成所有这些副本,而这些副本应该是C ++代码中减速的主要部分。