You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

汇编实现M×N矩阵左上到右下全路径生成:旋转门算法相关问题及代码优化请求

Hey Javier, great questions and nice work diving into assembly for this combinatorial path problem! Let’s break down your questions one by one, then look at some optimizations for your code.

1. Faster Path Counting Methods

The combinatorial formula you’re using—calculating the binomial coefficient (C((m-1)+(n-1), m-1))—is already the optimal way to count paths. This is because the number of paths is exactly this coefficient: you need (m-1) downward moves and (n-1) rightward moves, and the problem reduces to choosing positions for one type of move among all steps.

To make this calculation faster in assembly (or any language), you can compute the coefficient iteratively instead of using factorials (which can cause overflow quickly). For (C(a, b)) where (a = (m-1)+(n-1)) and (b = min(m-1, n-1)), use the product formula:

C(a, b) = product from k=1 to b of (a - b + k) / k

This avoids computing large intermediate factorials and keeps numbers smaller, which is better for 32-bit systems. There’s no algorithm faster than this for path counting, since the result is inherently tied to this combinatorial value.

2. Rotating Door Algorithm Complexity & Performance

Your initial complexity estimate isn’t quite right—let’s clarify:

  • Time Complexity: The rotating door algorithm generates each combination in amortized O(1) time. Each iteration only modifies 1 or 2 elements in the index array, so the total time to generate all (K = C(m+n-2, m-1)) paths is (O(K)). There’s no extra per-iteration factor for matrix dimensions beyond the initial setup.
  • Space Complexity: You only need an array to store the indices of the chosen moves (size (min(m-1, n-1))), so this is (O(min(m, n))), which matches your original space estimate.

Comparing to other path-generation methods:

  • The (O(2^{w \times h})) claim you saw is incorrect for path generation—that’s the complexity for enumerating all subsets of matrix cells, not paths. Most valid path-generation algorithms (like backtracking) also run in (O(K)) time, but the rotating door algorithm has a much smaller constant factor. It’s iterative (no recursive stack overhead), uses minimal memory, and avoids redundant checks, making it faster in practice—especially perfect for assembly, where overhead adds up quickly.

3. Rotating Door Algorithm Origins & Similar Methods

The rotating door algorithm was first introduced by Herbert S. Wilf and Albert Nijenhuis in their book Combinatorial Algorithms (published in 1978). It’s part of a family of efficient combinatorial generation algorithms.

For similar algorithms to generate combinations or permutations, check out:

  • Lexicographic combination generation (simpler to implement, but slightly slower than rotating door)
  • Johnson-Trotter algorithm (generates permutations with adjacent swaps)
  • Gray-code based combination generators (minimizes changes between consecutive combinations, like rotating door)
    These are all covered in classic combinatorics texts like Concrete Mathematics or specialized combinatorial algorithm references.

Assembly Code Optimization Suggestions

Your 32-bit FASM code is a solid start! Here are some tweaks to make it more efficient and robust:

  • Fix Overflow Issues: Your current code stores the path as a binary value in eax, but for matrices larger than 17x17 (since 16+16=32 steps), this will overflow a 32-bit register. Instead, use a byte array to store each step (0 for down, 1 for right) to support larger matrices.
  • Simplify Rotating Door Logic: The odd/even branch split can be replaced with the standard rotating door check, which is more uniform and reduces branch mispredictions. The standard algorithm checks whether the current index is at its "lower bound" or "upper bound" relative to its neighbors, rather than relying on matrix width parity.
  • Optimize Memory Access: Cache frequently used values (like path_matrix.w and path_matrix.h) in registers at the start, instead of reloading them from memory every time. For example, store w-1 in ebx and h-1 in ecx once at initialization.
  • Replace loop with Faster Loops: The loop instruction is relatively slow on modern CPUs. Replace your initialize loop with a dec ecx / jnz pair for better performance.
  • Use 64-bit Counting: For matrices larger than ~16x16, the path count will exceed 32-bit integer limits. Change count from dd to dq (64-bit) and use print_eax64 (or implement 64-bit printing) to handle large numbers.
  • Clarify Variable Names: Your MAT struct uses h for X coordinate and w for Y coordinate, which is counterintuitive. Rename them to rows (vertical size) and cols (horizontal size) to avoid confusion.

Here’s your original code for reference:

format PE console entry start
include 'win32a.inc'
; ===============================================
struct MAT
h db ? ; X coordinate.
w db ? ; Y coordinate.
ends
; ===============================================
section '.bss' readable writeable
; Declare the uninitialized table in memory:
path_matrix MAT ?
count dd ?
indices db path_matrix.w - 1 dup ?
end_indices:
; ===============================================
section '.text' code readable executable
start:
call read_hex
mov [path_matrix.h], al ; down will be 0
call read_hex
mov [path_matrix.w], al ; right will be 1
dec eax
mov ecx, eax
initialize:
mov ebx, ecx
dec ebx
mov byte[indices+ecx], bl
loop initialize
movzx ebx, [path_matrix.h]
dec ebx
add ebx, eax
mov byte[indices+eax+1], bl
mov eax, ebx
print_combination:
inc [count]
movzx ebx, [end_indices - indices]
dec ebx
xor eax, eax
print_loop:
xor esi, esi
inc esi
mov cl, byte[indices + ebx ]
shl esi, cl
xor eax, esi
dec ebx
cmp ebx, 0
jnz print_loop
call print_eax_binary
branch:
lea edi, [indices +1]
movzx eax, [path_matrix.w]
; check if withd is eaven, if true matrix is odd (w -1)
shr eax, 1
jnc odd
eaven:
movzx eax, byte[edi]
cmp eax, 0
jle eaven_indice
dec eax
mov byte[edi], al
jmp print_combination
eaven_indice:
inc edi
try_to_increase:
movzx ebx, byte[edi]
inc ebx
cmp bl, [edi+1]
jl increase
lea ecx, [edi-indices+1]
cmp cl, [path_matrix.w]
jl increase_indice
jmp fin
increase:
mov byte[edi], bl
dec ebx
mov byte[edi-1], bl
jmp print_combination
odd:
movzx eax, byte[edi]
inc eax
cmp al, [edi+1]
jge increase_indice
mov byte[edi], al
jmp print_combination
increase_indice:
inc edi
try_decrease:
lea eax, [edi - indices]
cmp byte[edi], al
jl eaven_indice
decrease:
movzx ebx, byte[edi-1]
mov byte[edi], bl
sub eax, 2
mov byte[edi-1], al
jmp print_combination
fin:
mov eax, [count]
call print_eax
; Exit the process:
push 0
call [ExitProcess]
include 'training.inc'

内容的提问来源于stack exchange,提问作者Javier Munoz

火山引擎 最新活动