我正在寻找一种合适的算法来构建一个相对较小(最多255个元素)排序的整数数组。目标平台是基于STM32的嵌入式系统。由于内存有限,因此首选就地方法。
我知道显而易见的方法是实现和描述通常的嫌疑人(快速排序,插入sert,shell排序),但是想要问你的经历。更具体地说,我发现构建数组时的性能信息非常少 - 也就是说,不同的算法可以使用所有现有元素已经订购的事实。
编辑1: 虽然问题标记为C ++,但STL不可用。此外,排序确实发生在内循环内。为了进一步说明,我正在寻找一种特别适合以有效方式构建排序列表的算法。我假设(可能是错误的)必须有专门适合这项任务的算法。这就是问题所在。
编辑2: 当说构建一个排序列表时,我的意思是列表开始为空,并由16位整数的有界数字(最多255个)填充,这些数字没有特定的顺序。必须在存储所有元素后处理该列表。对于处理,必须对列表进行排序,优选地按降序排序。
提前致谢, ·阿尔
答案 0 :(得分:9)
如果您的问题需要:
然后你把自己画成了一个角落:你的要求拼写为“插入排序”。
无论您选择何种算法或辅助数据结构,在中间插入一个新元素都需要您通过索引向上移动较大的元素,并删除任何元素(最大的元素除外)将要求您移动较大的元素按指数下来。由于插入排序正是如此,没有任何额外的逻辑或数据结构,您也可以使用它。
唯一的例外是你的比较特别贵。如果是这种情况,那么您可以使用binary search来查找插入点(而不是在移动时将新元素与每个旧元素进行比较)。但是,您仍然需要移动大于突变点的所有元素,因此您将永远无法提高O(N)之后的性能(尽管批量数据移动应该非常快......)。
此外,您应该评估您的要求:如果您知道N < 256
,并且在对象0
中插入对象的最坏情况对您的应用程序来说足够快,那么您应该停在那里。为了节省你不需要的时间,把事情变得比必要的更复杂是毫无意义的。
另一方面,如果:
然后您需要的是priority queue,您可以使用implicit heap实现它(以内存效率,就地方式)。隐式堆操作是O(log N),通常具有良好的性能因子;即使对于N = 255
,这也会对最坏情况下的表现产生重大影响。
答案 1 :(得分:3)
我经常在微控制器环境中使用这个算法,它会使表始终排序。它没有使用二进制查找,但是如果你只想将它用于少量元素,那么搜索更大元素的循环将比二进制算法运行得更快。对于较大的数组,您可能希望进行二进制查找。
这个算法在汇编程序中也很容易实现,并且只在堆栈上使用了2个额外的int。
#define TABLE_SIZE 255
int table[TABLE_SIZE];
int tableUsed = 0;
bool AddToTable(int value)
{
int i;
if (tableUsed >= TABLE_SIZE)
return false;
// Find location to insert value
for (i = 0; i < tableUsed; i++)
if (table[i] > value)
break;
// Insert value
do
{
table[i] ^= value;
value ^= table[i];
table[i] ^= value;
} while (i++ < tableUsed);
tableUsed++;
return true;
}
答案 2 :(得分:1)
老实说,对于这样的事情,我只是做一个shell排序并称之为好。您可以将skip元素硬编码为register / asm友好格式,以防止它泄漏到堆栈中。此外,性能将非常接近O(log(n))
。那,你可以编码或几乎立即从其他地方复制它。非常小的代码/ mem足迹。这是我对GBA游戏排序角度非常非常相似的情况的选择(
答案 3 :(得分:0)
如果您要经常对已经排序或部分排序的整数数组进行排序,那么smooth sort可能是一个不错的选择。它需要O(1)辅助存储,在最坏的情况下以O(n log n)运行,并且当输入数组变得更加分类时,运行时接近O(n)。
我只是真的比较顺畅排序和快速排序。我会在这里发布一些数字,但可以使用更多数字和平滑排序的Go实现here。
1k元素的掉期和比较次数:
swaps comparisons
Quicksort on sorted: 682 15896
Smoothsort on sorted: 0 2481
Quicksort on reversed: 1122 15587
Smoothsort on reversed: 10708 26436
1k元素上的Smoothsort:
Sorted 101431 ns/op
MostlySorted 550455 ns/op
Shuffled 95635 ns/op
Reversed 730083 ns/op
对1k元素进行Quicksort:
Sorted 243270 ns/op
MostlySorted 299113 ns/op
Shuffled 39798 ns/op
Reversed 245046 ns/op
答案 4 :(得分:0)
除非您知道数据的分布,否则我会做introsort。这是quicksort,其最大深度为log(n),然后根据左侧的大小切换到heapsort或insertion sort。 (在您的情况下,总是是插入排序。
如果您知道数据的分布,可以尝试bucket sort。例如,如果它们均匀分布,介于0和2147483648之间,则计算46341ish值的每个“范围”中有多少,然后将它们重新排列到正确的“范围”。然后对每个“范围”进行排序。我解释说非常可见一个例子(值为0-15,范围为4个值)
start: 5, 11, 14, 2, 6, 9, 7, 12, 12, 7, 10, 12, 0, 10, 12, 3, 13, 10
count: There are 3 in the range of 0-3
There are 4 in the range of 4-7
There are 5 in the range of 8-11
There are 6 in the range of 12-15
bucket:[0-3] [4-7] [8-11] [12-15]
[?, ?, ?], [?, ?, ?, ?], [?, ?, ?, ?, ?], [?, ?, ?, ?, ?, ?]
[2, 0, 3], [5, 6, 7, 7], [11, 9, 10, 10, 10], [14, 12, 12, 12, 12, 13]
sort: [0, 2, 3], [5, 6, 7, 7], [9, 10, 10, 10, 11], [12, 12, 12, 12, 13, 14]
对于最后一步,您只需对每个小“桶”进行快速排序。尽管如此,这很难做到。 (我已经完成了,但这很难)Quicksort更容易编程。
答案 5 :(得分:0)
我已经为SPE上的相对较小的列表(&lt; 1000)实现了comb sorts(不是很好的嵌入式处理器,但它们有严重的限制)并且效果很好 - 内存占用少,就地排序,{ {1}}平均复杂度为n log n
最佳情况,不易出现快速排序中位选择问题,易于理解且易于实施。
我可能会看到的唯一真正的烦恼是大多数实现倾向于使用浮点运算来计算间隙大小 - 但是如果你小心的话,通常可以忽略它。
答案 6 :(得分:0)
如果你的列表更大,那么timsort会非常出色 - 它超级自然快(tm),并且对已经排序或大多数排序的数组做得非常好。 Python和Java现在都在标准库中使用timsort。
由于你有一个小数组,我可能会进行插入排序或冒泡排序(!)。泡泡排序的变体一直持续到没有相邻的值被交换在这里可能会非常好。
不要让别人告诉你各种各样的东西是相同的 - 对于大型数组有利的排序往往比那些擅长小数组的排序具有更高的常量,并且常量可能是一个大问题低的n值。这就是为什么一个好的排序算法通常会在大型中使用nlogn排序,但是为小的子列表切换到低常数,O(n ^ 2)算法。
但阵列真的是最好的选择吗?如果你正在弄乱这个数据结构,你可能会更好地使用treap或红黑树。这两个数据结构总是排序,并且几乎可以在O(logn)时间内执行任何操作(除了获取排序列表,即O(n))。 Treape比红黑树提供更好的平均操作时间,但红黑树操作持续时间具有较低的方差。这是因为treaps几乎总是比红黑树更快,但treaps很少会进行大规模的内部重组,可以尝试最终用户对大n的耐心。
正如另一张海报所提到的,你也可以考虑堆,但你说你需要总是排序的东西,所以你可能需要一个数组,树形图或红黑树。