I missed the session when our lecturer explained it..
I know the formulae for NCR
NCR = N! / (R! * (NR)!)
I am not following the , as no factorial is found, and some creepy recursive jobs are being done there. ,因为没有找到因子,并且那里正在进行一些令人毛骨悚然的递归工作。 Help would be really appreciated.
print macro str
lea dx,msg
mov ah,09h
int 21h
endm
.model small
.data
n dw 15
r dw 6
ncr dw 1
msg db "The NCR is : ","$"
.code
start: mov ax,@data
mov ds,ax
mov bx,n
mov cx,r
inc bx
call ncr1
print msg
mov ax,ncr
call display
mov ah,4ch
int 21h
ncr1 proc
cmp cx,00
je l1
push cx
dec cx
call ncr1
mov ax,bx
pop cx
sub ax,cx
mul ncr
div cx
mov ncr,ax
l1 : ret
ncr1 endp
display proc
aam
add ax,3030h
mov bx,ax
mov dl,bh
mov ah,02h
int 21h
mov dl,bl
mov ah,02h
int 21h
ret
display endp
end start
Okay, I see some reuse value in showing how to analyze chunks of assembler code, so here it is.
We're dealing with a subroutine here, a chunk of code that's supposed to be relatively autonomous.
So, first, let's determine the raw effect it has on the program: its inputs, outputs and effect on sp
- ie its calling convention and signature .
Which entities does it use that are not set by it?
ncr1 proc
cmp cx,00 # <-- cx
je l1
push cx
dec cx
call ncr1
mov ax,bx # <--bx
pop cx
sub ax,cx
mul ncr # <--ncr
div cx
mov ncr,ax
l1 : ret
ncr1 endp
Which entities does it modify and not restore afterwards?
ncr1 proc
cmp cx,00
je l1
push cx # cx->local_stack[-2]
dec cx # -->cx? (overridden)
call ncr1
mov ax,bx
pop cx # local_stack[-2]->cx => cx restored
# (the next step is actually needed to deduce push/pop
# correspondence so they should be done in parallel)
sub ax,cx
mul ncr # -->ax,dx
div cx
mov ncr,ax # -->ncr
l1 : ret
ncr1 endp
Only the last changes to the entities are marked (since they prevail over earlier ones).
Does it have any net effect on sp
? (numbers are current sp
relative to the return address)
ncr1 proc
cmp cx,00
je l1
push cx #-2
dec cx
call ncr1 #delta is same as self
mov ax,bx
pop cx #0
sub ax,cx
mul ncr
div cx
mov ncr,ax
l1 : ret #without an argument, only cleans the return address
ncr1 endp
It doesn't (so "same as self" is 0), and 0
in all cases at ret
confirms that it handles the local stack correctly.
Concluding, its signature is:
ncr1 (cx,bx,ncr) -> ax,dx,ncr
Where ax
and dx
are probably unused (but they are still volatile ). And the calling convention is custom pure register with one hardcoded in/out parameter.
Now, all that leaves is to track which physical - and then, conceptual - value each entity holds at any time:
ncr1 proc --initially: bx=N, cx=R (metavariables)
cmp cx,00 # if R==0:
je l1 # ret (ax,dx,ncr unchanged)
push cx #[-2]=R
dec cx #cx=R-1
call ncr1 #ncr1(N,R-1) -> ax,dx,ncr(probably the result)
mov ax,bx #ax=N
pop cx #cx=[-2]=R
sub ax,cx #ax=N-R
mul ncr #dx:ax=(N-R)*ncr = (N-R)*ncr1(N,R-1)
div cx #ax=[(N-R)*ncr1(N,R-1)]/R
mov ncr,ax #ncr=[(N-R)*ncr1(N,R-1)]/R
l1 : ret
ncr1 endp # -> ax,dx,ncr(the result, now we're sure)
Here it is: the procedure calculates (N,R) -> [(NR)*ncr1(N,R-1)]/R
where N=bx
, R=cx
and the result is ncr
(which is mutated).
Which looks suspicious: (NR)
shall be (N+1-R)
in the canonical formula as per comment62556150 . If we substitute n=N-1
, it'll become: (n+1,R) -> [(n+1-R)*ncr(n+1,R-1)]/R
which looks okay (the first argument never changes)... so the procedure actually calculates nCr(n-1,r)
.
The choice to pass n+1
must be because n
only goes into the formula as n+1
, so by this, we save cycles on increasing it each time.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.