【问题标题】:What's wrong with my assembly Bubble Sort?我的程序集冒泡排序出了什么问题?
【发布时间】:2020-08-07 15:16:29
【问题描述】:

我正在使用基本伪代码/大纲在汇编中实现冒泡排序:

for i in array
    if array[i] >= array[i+1]
        exchange array[i], array[i+1]

我的 ASM 代码:

BubbleSort PROC
    mov     EBX, LENGTHOF myArr
    .REPEAT
        mov     ESI, 0
        mov     ECX, LENGTHOF myArr
        .REPEAT
            mov     EAX, [myArr + ESI]
            .IF EAX >= [myArr + ESI + TYPE myArr]       ; If (myArr[i] < myArr[i + 1])
                xchg    EAX, [myArr + ESI + TYPE myArr]
                mov     [myArr + ESI], EAX
            .ENDIF

            add     ESI, TYPE myArr
            dec     ECX
        .UNTIL ECX == 0
    dec     EBX
    .UNTIL EBX == 0
    ret
BubbleSort ENDP

当我向我的教授展示我的实现时,他说它“有点”像冒泡排序或“某种”冒泡排序。在告诉我们分配任务时,他说我们应该从阵列的后面开始,然后从后向前移动。然而,我是从前面开始,从前到后移动。

我觉得我走在正确的轨道上,代码可以正常工作,但我想正确地做。

有人知道我在哪里搞砸了吗?

【问题讨论】:

  • 他认为你应该从后到前有什么特别的原因吗?如果您查看 wikipedia 上的普通伪代码实现,它的内循环是从前到后的,而它的外循环继续进行,直到不再发生交换。
  • @Michael 我不太确定,如果我没记错的话,他说这会比从前到后更快。

标签: assembly x86 masm bubble-sort


【解决方案1】:

假设您的代码有效,它肯定冒泡排序。向数组的任一端冒泡都可以,因此省略优化,例如使用外部循环计数器 (EBX) 作为内部循环的上限。

简单或幼稚并不能阻止它采用冒泡排序。 (如果有的话,简单和幼稚是冒泡排序的全部要点。)


另请参阅Bubble Sort: An Archaeological Algorithmic Analysis,了解有关冒泡排序历史的更多信息,以及相关 JumpDown 排序的一些讨论,以及您是否使用布尔值作为早期输出。

它呈现的 BubbleSort 版本从j=n-1 到1(含)运行外循环,因此内循环可以将其用作上限。但是内部循环和你的一样,从 k=0 到 j,有条件地将 A[j] 与 A[j+1] 交换,从而将元素冒泡到数组的末尾。

on Wikipedia 版本也向上冒泡,从 1 到 n-1(包括)运行 i。 Wiki 实际上有 2 个版本:第一个像你一样天真,第二个在循环内递减 n。 (两个 Wiki 版本都使用布尔变量来记录它是否进行了任何交换,使算法复杂化并破坏了冒泡排序的唯一目的/优势,即非常简单并且代码很少。(例如about 20 bytes of x86 machine code,尽管这已经过大量优化大小超过速度。更正常的实现与 InsertionSort 的大小相似。)


您的代码使用 MASM 宏,最终会浪费指令重做检查(我假设 .UNTIL ECX == 0 没有利用 dec ecx 已经根据 ECX 非零设置标志这一事实)。但它确实有很好的 do{}while() 循环结构,这对于 asm 来说是惯用的。


顺便说一句,您的伪代码缺少外循环。这只是 BubbleSort 的一次传递。但是,是的,迭代 n 次将对 n 元素数组进行排序。

【讨论】:

  • 感谢您提供的信息!我们被告知在这个练习中使用 .REPEAT 和 .IF 而不是 cmp。
  • @Small_Kitten:好的,没关系。我的意思是你根本不需要cmp。 dec ecx / jnz top of loop 是在 do{}while(--ecx) 循环底部执行的正常操作。隐藏这样的低效率是使用 MASM 高级构造的一个缺点。一旦您对 asm 有了更多的经验并了解了 FLAGS 的工作原理,那么在没有它的情况下编写循环同样容易。
  • 啊,我现在明白你的意思了,也谢谢你!我想我已经在一些例子中看到了这种实现。汇编与其他语言非常不同,但到目前为止我很喜欢它。
  • @Small_Kitten:酷。如果您想了解更多,我在Why are loops always compiled into "do...while" style (tail jump)? 上写了一个答案。另请参阅 stackoverflow.com/tags/x86/info 获取更多文档、手册等链接,以及 How to remove "noise" from GCC/clang assembly output? 获取编译器为您制作示例 asm。 (一般原则适用于 MSVC 和 GCC)。
  • @Small_Kitten:顺便说一句,通常在 Stack Overflow 上询问有关 asm 的作业问题的人要么讨厌它,要么完全迷失了(否则他们只会通过单-使用调试器单步执行)。因此,当有人提出真正的问题时,这是一个很好的改变。无论如何,C 与 asm 并没有那不同。一旦您了解了 asm,您就可以考虑在编写 C 时 CPU 可以有效地做什么,以及您希望代码如何编译。您甚至可以使用 do{}while 循环编写结构类似于 asm 的 C,但通常编译器是好的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-11-11
  • 2020-12-19
  • 2017-03-02
  • 2015-09-12
  • 2022-07-16
  • 1970-01-01
相关资源
最近更新 更多