用代码渲染出的曼德布罗集合和老师给出的示意图不一致,我也不明白为什么

编程语言 2026-07-09

这篇帖子直接延续自几天前的 这篇帖子

我来快速说明前提:

我们在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

基于伪代码的 Mandelbrot

站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章