我将有一个符号数组(256个ascii符号)和它们的频率数组(一些符号出现零次)。使用计数排序进行排序以及什么灵魂将需要更多的代码行(代码将在汇编中编写,tasm)是否更可取的复杂性明智。
如果您的输入很长(字符串或缓冲区明显长于 256),则计数排序应该非常好。
使用计数排序进行排序是否更可取
的复杂性
它当然很容易实现,并且具有O(1)的复杂性。 如果大输入是可能的或常见的,计数排序非常好。
但是,如果小输入很常见,则计数排序仍然需要花时间清除整个计数数组并再次扫描它,并且对于较小的输入,此成本不会降低。
根据 CPU(例如,清除计数数组的快速内存集),具有 256 个符号的计数排序可能适用于小至 64 个符号的输入。 你提到了 TASM,所以你专门谈论的是 x86,可能还有 x86-16。 现代x86具有非常快的内存集,使用SSE存储或rep stosd
。 (256 或 512 字节(对于 16 位计数器)足够大,使用rep stos
并不是一个糟糕的主意;启动开销大部分是摊销的,因此它与矢量循环的速度接近。
在 64 个元素以下,我不确定 qsort 或 mergesort 是否会做得更好。 低于 16 个左右的元素(作为 qsort/merge-sort 的基本情况),您可能希望 InsertionSort 提高性能。
在带有 SSSE3 的现代 x86(用于 pshufb)上,您可以使用 SSE2 pminub/pmaxub 作为具有字节粒度的排序网络中的比较器(是的,这些指令在 16 位模式下工作)。 请参阅使用 SIMD 寄存器和说明启用 32 位元素排序算法中的指令级并行性,以及快速寄存器内字节排序?
或者使用 SIMD 进行部分排序,以便减少插入排序的交换。 也许只是一些加载,pminub/pmaxub,然后存储,没有太多或任何洗牌。
以及什么解决方案需要更多代码行
在 asm 中,源代码行是最无用的度量。 (并非每一行都组合成一个指令;有些是标签或指令)。
指令计数有时是相关的,但有些指令比其他指令慢,如何排序它们以及一个指令的输入是否取决于另一个指令的输出很重要。
如果你不关心性能,而是代码大小,那么你需要查看机器代码的字节数。 x86 指令的长度是可变的。
如果您只关心代码大小而不关心性能,则可以考虑冒泡排序或跳转排序。 (没有提前检查,只需始终循环最大次数)。 在 19 字节的 x86-32 机器代码中查看滑稽缓慢的 JumpDown 排序。 只需再多几字节的代码,它就可以在不使用xchg
-with-mem(隐式lock
前缀)的情况下进行交换。 一个更"正常"的气泡排序实现如下所示(8 位整数的 TASM)。
但是你也可以用几个字节的代码来实现插入排序,它通常表现良好(与其他 O(n^2) 算法相比,如气泡或选择)