我正在为嵌入式系统开发软件,我需要实现一个排序例程,而我在选择最佳解决方案时遇到了麻烦。我的要求如下:
我考虑过以下算法:
虽然答案(对于我的确切情况)可能很好,“呃,呃,这并不重要,对我们所关心的所有人使用冒泡排序”,这个答案并不是很有用。 通常,哪种排序算法对嵌入式系统有用?
答案 0 :(得分:9)
插入排序也很好,它在实践中运行速度快,稳定且就地。它与gnome排序非常相关,在实践中更快但是对于gnome排序,代码更小并且它需要更少的辅助空间(不是大O,差异在常量中)
编辑:是的,我搞砸了一下,让他们逆转了 - 我可能不应该在喝咖啡之前回答问题..之前说插入排序的代码和空间比gnome排序少
答案 1 :(得分:6)
不要为泡泡排序感到羞耻,它有它的位置。如果你的数据集很小,那么它很容易编码并且如果你做得正确就是稳定的(不要交换相同的元素)。
如果您的数据主要按照每次通过的交替方向进行排序,那么它也可能非常快。我知道你说它最初并没有接近排序,我正在谈论如果你排序然后坚持下去的可能性。在任何一种情况下,如果数据集大小很小,那么它是否完全未排序并不重要。
如果正如您在另一个答案的评论中提到的那样,您的数据集大小约为11,那么特别是的情况。如果明确设计的排序算法不足故意骇人听闻,任何算法都可以很快地处理这样的大小。
如果您的环境不能提供稳定的排序,我会根据您的约束和属性选择冒泡排序。
事实上,使用以下程序和time
实用程序,我发现用于qsort
和冒泡排序的CPU时间只有在元素计数达到10,000时才开始产生差异。
而且,即便如此,泡沫排序还不到半秒钟。除非你每秒要做很多种,否则这将是无关紧要的。
#include <stdio.h>
#include <stdlib.h>
static int data[10000];
#define SZDATA (sizeof (*data))
#define NMDATA (sizeof (data) / sizeof (*data))
int compfn (const void *a, const void *b) {
if (*((int*)a) > *((int*)b))
return 1;
if (*((int*)a) < *((int*)b))
return -1;
return 0;
}
int main (void) {
int i, tmp, swapped, count;
for (i = 0; i < NMDATA; i++)
data[i] = (i * 3) % 11;
if (0) {
qsort (data, NMDATA, SZDATA, compfn);
} else {
swapped = 1;
count = NMDATA;
while (swapped) {
swapped = 0;
for (i = 1; i < count; i++) {
if (data[i] < data[i-1]) {
tmp = data[i];
data[i] = data[i-1];
data[i-1] = tmp;
swapped = 1;
}
}
count --;
}
}
//for (i = 0; i < NMDATA; i++)
//printf ("%10d\n", data[i]);
return 0;
}
通过改变data
数组和if (0)
语句的大小,我得到了以下结果(以毫秒为单位),每种情况下有五次运行:
Data size | qsort | bubble
----------+-------+-------
100 | 61 | 76
| 76 | 76
| 77 | 61
| 61 | 60
| 61 | 61 avg qsort = 67, bubble = 67
1000 | 77 | 93
| 61 | 45
| 76 | 77
| 77 | 76
| 76 | 77 avg qsort = 73, bubble = 74
| |
10000 | 92 | 414
| 77 | 413
| 61 | 413
| 76 | 405
| 61 | 421 avg qsort = 73, bubble = 413
我怀疑由于缺少函数调用,因此使用小数据集进行更快速的气泡排序。
从中可以看出,对于较小的数据集,算法的效率通常并不重要,因为像big-O这样的东西通常与数据集变大时相关。
但是,此测试是在我的环境中完成的,您的测试可能会有很大差异。我建议在你的环境中执行相同的测量 - 实现冒泡排序,只考虑在必要时转移到更复杂的算法。
根据评论者的建议,我使用srand(42)
然后rand()
重新运行测试以填充数组元素。在这种情况下,对于冒泡排序,结果仅稍微差一点,对于10,000个元素为642对413毫秒,对于1,000个对象为82对74毫秒。
考虑到问题中的限制(小元素数,不常排序,稳定性要求,低空间复杂度),我仍然更喜欢将气泡排序简单化为任何更复杂的算法。
但是,请记住我之前的建议:您需要在自己的环境中计时。结果可能大不相同。您可以使用我提供的代码作为执行此操作的基准。说真的,如果你选择的方法花费的时间不到一秒钟,那么它可能足以满足你的目的。
答案 2 :(得分:3)
系统嵌入与否的事实并不重要。重要的是您列出的因素:代码大小限制,可用内存,所需速度和元素数量。
根据您的概述,冒泡排序是一种完全可以接受的解决方案:它小巧,可预测,易于内存,并且非常易于实现和调试。我在20世纪80年代早期看到了一个证据,即对于 n ≤11,气泡排序是时间最佳的。现代快速排序有可能略微改变,但我怀疑收支平衡可能会减少很多。 / p>
要使不稳定的排序算法稳定,请添加包含原始位置的排序键。仅当主要是平局时才参考辅助密钥。
答案 3 :(得分:2)
删除“在嵌入式系统上”并将问题更改为“通常,哪种算法有用”。接下来,试试吧!到目前为止你尝试了什么,内存消耗是什么,执行时间是什么,代码大小是多少?
嵌入式或桌面式您应该执行这些实验并询问这些问题。没有一般答案,因为没有一般要求。每种解决方案都取决于要求。一种尺寸适合所有人都不合适。真正的问题是您的要求是什么,然后您担心实施并满足这些要求。甚至是必需的,这可以用不同的方式解决吗?这种类型适合您的整体性能数字?帐篷里的其他长杆是什么?
就像在桌面上一样,对它们进行编码并尝试它们,看看你提出了什么。提及泡沫排序从来没有理由感到尴尬。如果你的要求没有性能或大小数字,泡泡排序是好的,每个人都可以阅读和理解代码,简单,可维护,不依赖于库或库的版本等。不是一件坏事可以说(如果有的话)没有性能要求)。
答案 4 :(得分:1)
Sorting in Embedded Systems,Nigel Jones关于嵌入式大师的文章值得一看。
他得出结论,shell sort是他根据代码大小和性能选择的排序算法。
答案 5 :(得分:0)
嵌入式系统的限制因目标平台和应用要求而异,通常需要在竞争因素之间进行权衡。您需要考虑的因素可能包括:
例如,您可能有一个算法平均使用的内存很少,但内存量与排序中的项目数不成线性关系。然后,您可能最好使用一个平均值使用更多内存的算法,但内存使用更有界或线性。类似地,对于执行速度,非确定性算法取决于初始顺序,而其他算法对初始顺序的依赖性较小或不变。
在这种情况下,您可能会将内存使用问题优先于执行速度。
所以答案是没有单一的答案,这取决于你的具体要求和约束。这个useful comparison table应该用于帮助您选择最合适的算法。
所有这一切,为了加速开发,总是值得尝试编译器标准库提供的标准qsort()函数。如果这对您有用,则符合您的约束条件,无需进一步的工作。