assembly - 排列生成问题

assembly - Issue with permutations generation

我正在尝试使用 MSVC 程序集 8086 内联列出前 N 个自然数 (N <= 6) 的所有排列。我无法更改 C 程序的任何内容,我只能管理汇编代码。 所以这就是我所做的(并且使用 N <= 3),使用字典算法:

#include <stdio.h>
int main() {
// Variabili
int N=4;    // N <= 6
int Perm[4326];    // permutations array: the dimension is sufficient for N <= 6
int Num;     // At the end it must contains the number of generated permutations
// Assembler block
__asm
{
    XOR EAX, EAX    ; Reset EAX (permutations counter)
    MOV ECX, N        ; Put N into ECX
    CMP ECX, 1        ; Compare ECX and 1
    JBE Piccolo        ; If ECX <= 1 go to Piccolo (particular cases 1 and 0)
    DEC ECX        ; Decrement ECX (so ECX = N - 1)
    MOV EAX, N    ; Put N into EAX
    MaxPerm:
        MUL ECX            ; Do EDX:EAX = EAX * ECX
        LOOP MaxPerm    ; Loop to find permutations number
    MOV Num, EAX    ; Move EAX (permutations number) into Num
    MOV EAX, 1        ; Set EAX (permutations number) to 1
    MOV EBX, 0        ; Reset EBX (EBX = 0)
    MOV ECX, 1        ; Reset ECX
    CicloIniziale:
        DEC ECX                    ; Decrement ECX (to compare it with N)
        CMP ECX, N                ; Compare ECX and N
        JE Permutazione            ; If ECX == N the first permutation has been written, skip to the generation of the next ones
        INC ECX                    ; Else increment ECX
        MOV Perm[4*EBX], ECX    ; Insert ECX into the Perm array
        INC ECX                    ; Increment ECX
        INC EBX                    ; Increment EBX
        JMP CicloIniziale        ; Continue the loop
    Permutazione:
        XOR EAX, EAX    ; Local index
        XOR EBX, EBX    ; Previous permutation index
        MOV ECX, N        ; Current permutation index
        MOV EDI, 1        ; Value of k
        MOV ESI, 1        ; New position of k
        
        CicloPermutazione:
            CMP EAX, N                ; Compare ECX and EAX
            JE ResetPerm            ; If the end of the permutation has been reached, go to ResetPerm
            MOV EDX, Perm[4*EBX]    ; Otherwise save the element of the previous permutation in position EBX in EDX
            CMP EDX, EDI            ; Compare EDX and EDI
            JE ValoreUguale            ; If they are equal, then go to ValoreUguale
            CMP EAX, ESI            ; Otherwise compare EBX and ESI
            JE InserisciElSpostato    ; If it's time to move the item, go to InserisciElSpostato
            MOV Perm[4*ECX], EDX    ; Returns the value of the previous permutation to the new permutation
            ContinuaCiclo:
                INC EBX                    ; Increment the previous permutation index
                INC EAX                    ; Increment the local index
                INC ECX                    ; Increment the new permutation index
                JMP CicloPermutazione    ; Continue the loop
        
            InserisciElSpostato:
                MOV Perm[4*ECX], EDI    ; Put EDI in the new permutation
                MOV EDX, N                ; EDX = N
                DEC EDX                    ; EDX -= 1
                CMP ESI, EDX            ; Compare ESI and EDX
                JE ResettaK                ; If ESI == EDX go to ResettaK
                JMP ContinuaCiclo        ; Continue the loop
            
            ValoreUguale:
                CMP EAX, 0                ; Confronta EAX e 0
                JZ PrimaPos                ; If EAX == 0 (first position) go to PrimaPos
                MOV EDX, Perm[4*EBX+4]    ; Else save the element into position EBX - 1
                MOV Perm[4*ECX], EDX    ; Insert EDX in the new permutation
                JMP ContinuaCiclo        ; Continue the loop
                
                PrimaPos:
                    MOV EDX, Perm[4*EBX+4]    ; Save into EDX the element in position ECX + 1
                    MOV Perm[4*ECX], EDX    ; Insert EDX into the new permutation
                    JMP ContinuaCiclo        ; Continue the loop
            ResettaK:
                XOR ESI, ESI            ; Reset ESI
                MOV EDI, Perm[4*EDI]    ; EDI = Perm[EDI]
                JMP ContinuaCiclo        ; Continue the loop
            ResettaKN:
                CMP EDI, N            ; Compare EDI and N
                JNE ContinuaCiclo    ; if EDI != N then continue the loop
                JMP ResettaK        ; Else go to ResettaK
        ResetPerm:
            MOV EAX, Num            ; EAX = Num
            MUL DWORD PTR N            ; EAX *= N (Elements of number of all the permutations)
            CMP EAX, ECX            ; Compare EAX and ECX
            JE Fine                    ; If EAX == ECX (permutations done) then finish the program
            XOR EAX, EAX            ; Otherwise reset EAX
            INC ESI                    ; Increment k index
            JMP CicloPermutazione    ; Continue the loop
    Piccolo:            ; If N = 0 or N = 1, the only permutation that can exists is made by only 1 element
        MOV Num, 1
        MOV Perm[0], ECX
        
    Fine:
}
// Print
{
    int i,j,k;
    printf("%d permutations of the first %d natural numbers\n", Num, N);
    for (i=k=0;i<Num;i++)
    {
        for (j=0;j<N;j++)
        {
            printf("%3d",Perm[k++]);
        }
        printf("\n");
    }
}
return 0;
}

问题是这个程序只适用于 N <= 3 而我没有发现问题! 这些是我得到的输出:

N = 3 及以下工作正常:

6 permutations of the first 3 natural numbers
1  2  3
2  1  3
2  3  1
3  2  1
3  1  2
1  3  2

N = 4(同5和6):

24 permutations of the first 4 natural numbers
1  2  3  4
2  1  3  4
2  3  1  4
2  3  4  1
3  2  4  1
3  4  2  1
3  4  1  2
4  3  1  2
4  1  3  2
4  1  2  3
1  4  2  3
1  2  4  3
1  2  3  4
1  3  3  4
1  3  2  4
1  3  4  2
1  4  4  2
1  4  3  2
1  4  2  3
1  2  2  3
1  2  4  3
1  2  3  4
1  3  3  4
1  3  2  4

正如您所看到的,在第一个循环的最后一个排列之后,接下来的排列就乱七八糟了...

你能帮帮我吗? 谢谢

您写了几行代码,其中代码与随附的注释不匹配。如果我是你,我会怀疑在那里发现可能的错误。

CMP EAX, N             ; Compare ECX and EAX
CMP EAX, ESI           ; Otherwise compare EBX and ESI
MOV EDX, Perm[4*EBX+4] ; Else save the element into position EBX - 1
MOV EDX, Perm[4*EBX+4] ; Save into EDX the element in position ECX + 1

这将是与评论匹配的代码:

cmp ecx, eax
cmp ebx, esi
mov edx, Perm[4*ebx - 4]  ; 'save' == 'save into EDX' (*)
mov edx, Perm[4*ecx + 4]

我并不是说这些会解决问题,因为也许您的注释是错误的而您的代码是正确的。

(*) 而不是非常混乱的短语“save into EDX”,最好写成“load EDX with/from[=39” =]".


您写了很多多余的评论
以下是其中的一小部分:

MOV EAX, N   ; Put N into EAX
MOV EBX, 0   ; Reset EBX (EBX = 0)
CMP ECX, 1   ; Compare ECX and 1
INC ECX      ; Increment ECX
INC EBX      ; Increment EBX
MOV EDX, N   ; EDX = N
DEC EDX      ; EDX -= 1
CMP ESI, EDX ; Compare ESI and EDX
JE ResettaK  ; If ESI == EDX go to ResettaK

帮自己一个忙,不要再写这些多余的评论了。它们使任何人(包括您自己)(尝试)理解代码变得更加困难。

最近的一篇博客 post 讨论了编写代码注释的主题:https://Whosebug.blog/2021/07/05/best-practices-for-writing-code-comments/

最后,我设法编写了一个 C 程序,它使用 C++ STD 库的 next_permutation 函数的算法来完成这项工作。然后,我在 Godbolt(在线编译器)的帮助下将 C 代码翻译成汇编。

所以:

  1. 使用next_permutation函数编写C程序:https://godbolt.org/z/7avGaWfha
  2. 展开main中的所有函数:https://godbolt.org/z/7z9MGWefK
  3. 最后在Godbolt的大力帮助下,翻译成汇编