用代码渲染出的曼德布罗集合和老师给出的示意图不一致,我也不明白为什么
这篇帖子直接延续自几天前的 这篇帖子。
我来快速说明前提:
我们在DOSBox里使用带8086套件和MASM的环境,手头有一个关于汇编语言的小练习。我们本来要在屏幕(DOSBox的屏幕)上打印出Mandelbrot集的近似图,要求如下:
不允许使用堆栈,使用scale=64的定点数,屏幕上的每个格子(25行 x 80列)被赋予一个复数点 c = c_x + i * c_y,使得 c_x = -144 + col * 3,以及 c_y = -72 + row * 6,其中 0 <= col <= 79,和 0 <= row <= 24。
对于屏幕上的每个格子,我们递归计算
z_{n+1} = z_{n} ^ 2 + c
这可以简化为:
x_new = x ^ 2 / 64 - y ^ 2 / 64 + c_x
以及
y_new = x * y / 32 + c_y
并且我们要对每个点执行最多16次迭代。
我们的跳出条件是 x^2/64 + y^2/64 > 4*64。
- 如果在1-3次迭代中跳出,则打印012Eh。
- 如果在4-7次迭代中跳出,打印042Ah
- 如果在8-15次迭代中跳出,打印0EDBh
- 如果在16次迭代后仍未跳出,打印0ADBh。
现在老师添加了一张应该长成的样子的图片:
我更新了我的代码,现在得到的图像与老师的图片类似(但不是完全一样):
我用白色标出了所有与老师的图像不同的格子,总共有30个格子(轴对称,因此左右各15个差异格子,我将继续把它们称为15处差异)。
自那以后已经超过三天没有进展,老师的回答也没帮上忙,他的最新回答是说循环的伪代码应该长成下面这样:
x = 0
y = 0
iter = 0
while iter < 16 {
x2 = (x*x) / 64
y2 = (y*y) / 64
x_new = x2 - y2 + cx
xy = (x*y) / 64
y_new = 2*xy + cy
x = x_new
y = y_new
iter = iter + 1
if x*x/64 + y*y/64 > 256 {
draw according to iter
break
}
}
if iter == 16 and did not escape:
draw green
但我严格按这个执行,仍然得到错误的图片。
以下是上图的代码:
; CODE #1
.model small
.stack 100h
.data
; We store our variables in memory because the 8086 processor
; does not have enough registers to hold everything at once.
c_x dw 0
c_y dw 0
x dw 0
y dw 0
x_sqr dw 0
y_sqr dw 0
row_cnt dw 0
col_cnt dw 0
iter dw 0
.code
START:
; clearing the dos screen
mov ax, 0003h
int 10h
; writing to the data segment our screen coordinates for the horizontal axis and setting di=0
mov ax, @data
mov ds, ax
mov ax, 0B800h
mov es, ax
mov di, 0
; setting up the row loop
mov c_y, -72
mov row_cnt, 25
ROW_LOOP:
; setting up the colum loop
mov c_x, -144
mov col_cnt, 80
COL_LOOP:
; initial values of x,y from z_0
mov x, 0
mov y, 0
mov iter, 0
MANDEL_LOOP:
; escape condition
cmp iter, 16
je MANDEL_SET
; calculating x^2/64
mov ax, x
imul x ; DX:AX = x * x
mov bx, 64
idiv bx ; AX = (DX:AX) / 64
mov x_sqr, ax
; calculating y^2/64
mov ax, y
imul y ; DX:AX = y * y
mov bx, 64
idiv bx ; AX = (DX:AX) / 64
mov y_sqr, ax
; calc saving y_new = (xy) / 32 + c_y
mov ax, x
imul y ; DX:AX = x * y
mov bx, 32
idiv bx ; AX = (DX:AX) / 32
add ax, c_y
mov y, ax
; calc saving x_new = x_sqr - y_sqr + c_x
mov ax, x_sqr
sub ax, y_sqr
add ax, c_x
mov x, ax
inc iter
; escape condition ; back to line 63
mov ax, x
imul x
mov cx, 64
idiv cx
mov bx, ax ; bx now has x^2/64
mov ax, y
imul y
idiv cx ; ax now has y^2/64
add ax, bx
cmp ax, 256
jg DRAW_CELL
jmp MANDEL_LOOP
MANDEL_SET:
; it survived 16 iterations so we draw the solid green block.
mov ax, 0ADBh
jmp WRITE_VRAM
WRITE_VRAM:
; printing to the screen the char
mov es:[di], ax
add di, 2 ; move forward 2 bytes in video memory
; moving to next colum
add c_x, 3
dec col_cnt
jz COL_DONE
jmp COL_LOOP
COL_DONE:
; moving to next row
add c_y, 6
dec row_cnt
jz ROW_DONE
jmp ROW_LOOP
ROW_DONE:
jmp EXIT
DRAW_CELL:
; assigning the printed char based of iter value
cmp iter, 3
jle COL_1_3
cmp iter, 7
jle COL_4_7
; if it didnt jump, iter is 8-15
mov ax, 0EDBh
jmp WRITE_VRAM
COL_1_3:
mov ax, 012Eh
jmp WRITE_VRAM
COL_4_7:
mov ax, 042Ah
jmp WRITE_VRAM
EXIT:
; moving the command line to the bottom and exiting the program
mov ah, 02h
mov bh, 00h
mov dh, 18h
mov dl, 00h
int 10h
mov ah, 04Ch
int 21h
END START
当我把伪代码改成在循环末尾放置inc迭代、并在计算之后立即放置跳出条件时
x^2/64 and y^2/64
我得到的就是我之前生成的图像(图像2)。
我甚至做了一个小的Excel表手动计算来帮助我调试,但它只是与我的代码一致,没有给出任何洞见:
在这个表格里你可以看到在每一次迭代中,对于那些与我的代码(下方)生成的图像和老师图像不同的特定C 对值,所有分量的数值。
在列中有根据这个Excel表计算出的字符,code char是我代码(下方)生成的字符,real char是老师的字符(如第一张图所示)。
这是我的完整代码,请注意,即使只有一个格子和老师不同,在作业中也应记为零,因为这是自动评分。
; CODE #2
.model small
.stack 100h
.data
; We store our variables in memory because the 8086 processor
; does not have enough registers to hold everything at once.
c_x dw 0
c_y dw 0
x dw 0
y dw 0
x_sqr dw 0
y_sqr dw 0
row_cnt dw 0
col_cnt dw 0
iter dw 0
.code
START:
; clearing the dos screen
mov ax, 0003h
int 10h
; writing to the data segment our screen coordinates for the horizontal axis and setting di=0
mov ax, @data
mov ds, ax
mov ax, 0B800h
mov es, ax
mov di, 0
; setting up the row loop
mov c_y, -72
mov row_cnt, 25
ROW_LOOP:
; setting up the colum loop
mov c_x, -144
mov col_cnt, 80
COL_LOOP:
; initial values of x,y from z_0
mov x, 0
mov y, 0
mov iter, 0
MANDEL_LOOP:
; escape condition
cmp iter, 16
je MANDEL_SET
; calculating x^2/64
mov ax, x
imul x ; DX:AX = x * x
mov bx, 64
idiv bx ; AX = (DX:AX) / 64
mov x_sqr, ax
; calculating y^2/64
mov ax, y
imul y ; DX:AX = y * y
mov bx, 64
idiv bx ; AX = (DX:AX) / 64
mov y_sqr, ax
; escape condition ; back to line 63
mov ax, x_sqr
add ax, y_sqr
cmp ax, 256
jg DRAW_CELL
; calc saving y_new = (xy) / 32 + c_y
mov ax, x
imul y ; DX:AX = x * y
mov bx, 32
idiv bx ; AX = (DX:AX) / 32
add ax, c_y
mov y, ax
; calc saving x_new = x_sqr - y_sqr + c_x
mov ax, x_sqr
sub ax, y_sqr
add ax, c_x
mov x, ax
inc iter
jmp MANDEL_LOOP
MANDEL_SET:
; it survived 16 iterations so we draw the solid green block.
mov ax, 0ADBh
jmp WRITE_VRAM
WRITE_VRAM:
; printing to the screen the char
mov es:[di], ax
add di, 2 ; move forward 2 bytes in video memory
; moving to next colum
add c_x, 3
dec col_cnt
jz COL_DONE
jmp COL_LOOP
COL_DONE:
; moving to next row
add c_y, 6
dec row_cnt
jz ROW_DONE
jmp ROW_LOOP
ROW_DONE:
jmp EXIT
DRAW_CELL:
; assigning the printed char based of iter value
cmp iter, 3
jle COL_1_3
cmp iter, 7
jle COL_4_7
; if it didnt jump, iter is 8-15
mov ax, 0EDBh
jmp WRITE_VRAM
COL_1_3:
mov ax, 012Eh
jmp WRITE_VRAM
COL_4_7:
mov ax, 042Ah
jmp WRITE_VRAM
EXIT:
; moving the command line to the bottom and exiting the program
mov ah, 02h
mov bh, 00h
mov dh, 18h
mov dl, 00h
int 10h
mov ah, 04Ch
int 21h
END START
我真的迷失了,不知道到底少了什么。
我能用来解释这件事的方式有两种:(a) 老师使用了一段本身就存在某些固有错误的代码,从而生成了他的图像,(b) 我的代码缺少一些重要的、我还不掌握的东西,看起来我班上大多数同学都处在同一个境况里(大家得到的都差不多,但并非完全一致的图像),而老师坚称图像是正确的,我们只是需要再努力。
解决方案
现在老师好心提供了那段伪代码,问题就变成了一个简单的任务:正确实现它。你根本没有做到这一点!你对初稿过于死板地照搬,甚至没有应用我在 第一个回答 中提到的一些要点。
我认为一个关键差异在于:
xy = (x*y) / 64
y_new = 2*xy + cy
你在这里一直把捷径走向 (x*y) / 32 + cy:
下面是我的新版本,以及一张新的截图:
ORG 256 ; DOS .COM program (NASM style)
mov ax, 0003h
int 10h
mov ax, 0B800h
mov es, ax
xor di, di
; setting up the row loop -72 To +72 Step 6
mov word [c_y], -72 ; [-1.125,+1.125]
mov word [row_cnt], 25
ROW_LOOP:
; setting up the column loop -144 To +93 Step 3
mov word [c_x], -144 ; [-2.250,+1.453]
mov word [col_cnt], 80
COL_LOOP:
; initial values of x,y from z_0
xor ax, ax
mov [iter], ax
mov [x], ax
mov [y], ax
mov cx, 64
MANDEL_LOOP:
; calculating x^2 and y^2
mov ax, [x]
imul ax
idiv cx
mov [xx], ax
mov ax, [y]
imul ax
idiv cx
mov [yy], ax
; BX = x^2 - y^2 + c_x
mov ax, [xx]
sub ax, [yy]
add ax, [c_x]
mov bx, ax
; AX = (x*y) / 64
; y = 2*AX + cy
; x = BX
mov ax, [x]
imul word [y]
idiv cx
add ax, ax
add ax, [c_y]
mov [y], ax
mov [x], bx
inc word [iter]
; calculating x^2 + y^2
mov ax, [y]
imul ax
idiv cx
mov bx, ax
mov ax, [x]
imul ax
idiv cx
add ax, bx
; escape condition
jc DRAW_PIXEL
cmp ax, 4*64
ja DRAW_PIXEL
cmp word [iter], 16
jb MANDEL_LOOP
DRAW_PIXEL:
; assigning the printed char based of iter value
mov ax, 092Eh
cmp word [iter], 3
jbe WRITE_VRAM
mov ax, 042Ah
cmp word [iter], 7
jbe WRITE_VRAM
mov ax, 0EDBh
cmp word [iter], 15
jbe WRITE_VRAM
mov ah, 0Ah
WRITE_VRAM:
stosw
; moving to next colum
add word [c_x], 3
dec word [col_cnt]
jz COL_DONE
jmp COL_LOOP
COL_DONE:
; moving to next row
add word [c_y], 6
dec word [row_cnt]
jz ROW_DONE
jmp ROW_LOOP
ROW_DONE:
EXIT:
; moving the command line to the bottom and exiting the program
mov ah, 02h
mov bh, 00h
mov dh, 18h
mov dl, 00h
int 10h
mov ah, 00h
int 16h
mov ah, 4Ch
int 21h
c_x: dw 0
c_y: dw 0
x: dw 0
y: dw 0
xx: dw 0
yy: dw 0
row_cnt: dw 0
col_cnt: dw 0
iter: dw 0





