# -*- coding: utf-8 -*-
"""
生成遍历顺序对比说明文档 (docx)
"""
importos
importmatplotlib
matplotlib.use('Agg')
matplotlib.rcParams['font.sans-serif'] = ['Microsoft YaHei', 'SimSun', 'KaiTi']
matplotlib.rcParams['axes.unicode_minus'] =False
importmatplotlib.pyplotasplt
importmatplotlib.patchesasmpatches
frommatplotlib.patchesimportFancyBboxPatch
import numpy asnp
from docx import Document
from docx.shared import Inches, Pt, Cm, RGBColor
from docx.enum.text import WD_ALIGN_PARAGRAPH
from docx.enum.table import WD_TABLE_ALIGNMENT
# ========== 配置 ==========
ROWS, COLS=4, 5
OUT_DIR=r'C:\Users\yl\Desktop'
IMG_DIR=os.path.join(OUT_DIR, '_temp_imgs')
os.makedirs(IMG_DIR, exist_ok=True)
# ========== 画图工具函数 ==========
defdraw_grid_with_arrows(ax, order, title, subtitle, order_name,
arrow_color='#E74C3C', cell_bg='#F0F3F4',
highlight_color='#FADBD8', text_color='#2C3E50'):
"""绘制带箭头和序号的网格图"""
ax.set_xlim(-0.1, COLS-0.05)
ax.set_ylim(-0.1, ROWS-0.05)
ax.set_aspect('equal')
ax.axis('off')
# 标题
ax.text(COLS/2, ROWS+0.5, title, fontsize=14, fontweight='bold',
ha='center', va='center', color=text_color,
bbox=dict(boxstyle='round,pad=0.3', facecolor='#EBF5FB', alpha=0.8))
ax.text(COLS/2, ROWS+0.15, subtitle, fontsize=10,
ha='center', va='center', color='#7F8C8D')
# 绘制单元格
forrinrange(ROWS):
forcinrange(COLS):
idx=order[r, c]
# 根据顺序深浅着色
alpha=0.3+0.7* (idx/ (ROWS*COLS-1))
color=plt.cm.RdYlGn_r(alpha)
rect=FancyBboxPatch((c, ROWS-1-r), 1, 1,
boxstyle="square,pad=0",
facecolor=color, edgecolor='#BDC3C7',
linewidth=1.5)
ax.add_patch(rect)
# 序号文字
ax.text(c+0.5, ROWS-1-r+0.5, str(idx+1),
ha='center', va='center', fontsize=11, fontweight='bold',
color='#2C3E50')
# 绘制箭头连线
order_list= []
forrinrange(ROWS):
forcinrange(COLS):
order_list.append((c, ROWS-1-r))
foriinrange(len(order_list) -1):
x1, y1=order_list[i]
x2, y2=order_list[i+1]
ax.annotate('', xy=(x2+0.5, y2+0.5), xytext=(x1+0.5, y1+0.5),
arrowprops=dict(arrowstyle='->', color=arrow_color,
lw=1.5, alpha=0.6))
# 添加顺序名称标签
ax.text(0.05, -0.4, f'→ {order_name}', fontsize=9, fontweight='bold',
color=arrow_color, transform=ax.transData)
defdraw_comparison_grid(ax, order, title, order_name, arrow_color):
"""绘制对比图(两个网格并排中的单个)"""
ax.set_xlim(-0.1, COLS-0.05)
ax.set_ylim(-0.1, ROWS-0.05)
ax.set_aspect('equal')
ax.axis('off')
ax.text(COLS/2, ROWS+0.3, title, fontsize=11, fontweight='bold',
ha='center', va='center', color='#2C3E50')
forrinrange(ROWS):
forcinrange(COLS):
idx=order[r, c]
alpha=0.3+0.7* (idx/ (ROWS*COLS-1))
color=plt.cm.RdYlGn_r(alpha)
rect=FancyBboxPatch((c, ROWS-1-r), 1, 1,
boxstyle="square,pad=0",
facecolor=color, edgecolor='#BDC3C7',
linewidth=1)
ax.add_patch(rect)
ax.text(c+0.5, ROWS-1-r+0.5, str(idx+1),
ha='center', va='center', fontsize=9, fontweight='bold',
color='#2C3E50')
order_list= []
forrinrange(ROWS):
forcinrange(COLS):
order_list.append((c, ROWS-1-r))
foriinrange(len(order_list) -1):
x1, y1=order_list[i]
x2, y2=order_list[i+1]
ax.annotate('', xy=(x2+0.5, y2+0.5), xytext=(x1+0.5, y1+0.5),
arrowprops=dict(arrowstyle='->', color=arrow_color,
lw=1, alpha=0.5))
# ========== 生成图片 ==========
# 1. 从左到右,从上到下(行优先)
order_row=np.zeros((ROWS, COLS), dtype=int)
forrinrange(ROWS):
forcinrange(COLS):
order_row[r, c] =r*COLS+c
fig1, ax1=plt.subplots(1, 1, figsize=(8, 7), dpi=150)
draw_grid_with_arrows(ax1, order_row,
'方式一:从左到右 → 从上到下',
'(行优先 / Row-Major Order)',
'行优先遍历')
fig1.savefig(os.path.join(IMG_DIR, 'row_major.png'), bbox_inches='tight', pad_inches=0.3)
plt.close(fig1)
# 2. 从上到下,从左到右(列优先)
order_col=np.zeros((ROWS, COLS), dtype=int)
forcinrange(COLS):
forrinrange(ROWS):
order_col[r, c] =c*ROWS+r
fig2, ax2=plt.subplots(1, 1, figsize=(8, 7), dpi=150)
draw_grid_with_arrows(ax2, order_col,
'方式二:从上到下 ↓ 从左到右',
'(列优先 / Column-Major Order)',
'列优先遍历',
arrow_color='#3498DB')
fig2.savefig(os.path.join(IMG_DIR, 'col_major.png'), bbox_inches='tight', pad_inches=0.3)
plt.close(fig2)
# 3. 对比图
fig3, axes=plt.subplots(1, 2, figsize=(14, 7), dpi=150)
draw_comparison_grid(axes[0], order_row,
'从左到右,从上到下\n(行优先)',
'行优先', '#E74C3C')
draw_comparison_grid(axes[1], order_col,
'从上到下,从左到右\n(列优先)',
'列优先', '#3498DB')
fig3.suptitle('两种遍历顺序对比', fontsize=16, fontweight='bold', y=1.02)
fig3.savefig(os.path.join(IMG_DIR, 'comparison.png'), bbox_inches='tight', pad_inches=0.3)
plt.close(fig3)
# ========== 生成 Word 文档 ==========
doc= Document()
# 页面设置
forsectionindoc.sections:
section.page_width = Cm(21)
section.page_height = Cm(29.7)
section.left_margin = Cm(2.5)
section.right_margin = Cm(2.5)
section.top_margin = Cm(2)
section.bottom_margin = Cm(2)
# 标题
title=doc.add_heading('二维网格遍历顺序对比说明', level=0)
title.alignment = WD_ALIGN_PARAGRAPH.CENTER
doc.add_paragraph('') # 空行
# ===== 一、概述 =====
doc.add_heading('一、概述', level=1)
p=doc.add_paragraph()
p.add_run('在二维网格(矩阵、栅格、图像像素等)中遍历所有单元格时,有两种最基本的遍历顺序:')
t=doc.add_table(rows=2, cols=2)
t.style ='Light Grid Accent 1'
t.alignment = WD_TABLE_ALIGNMENT.CENTER
t.cell(0, 0).text ='方式'
t.cell(0, 1).text ='说明'
t.cell(1, 0).text ='从左到右,从上到下'
t.cell(1, 1).text ='先遍历完当前行的所有列,再换到下一行(行优先 / Row-Major)'
# 设置表格字体
forrowint.rows:
forcellinrow.cells:
forparagraphincell.paragraphs:
forruninparagraph.runs:
run.font.size = Pt(11)
doc.add_paragraph('')
# ===== 二、详细说明 =====
doc.add_heading('二、方式一:从左到右,从上到下(行优先)', level=1)
doc.add_paragraph('遍历规则:')
p=doc.add_paragraph('每一行内,从左向右依次访问每个单元格', style='List Bullet')
p=doc.add_paragraph('当前行访问完毕后,换到下一行,继续从左到右', style='List Bullet')
p=doc.add_paragraph('所有行从上到下依次处理', style='List Bullet')
doc.add_paragraph('访问顺序公式:')
p=doc.add_paragraph()
run=p.add_run('index = row × 列数 + col')
run.font.name ='Consolas'
run.font.size = Pt(11)
run.font.color.rgb = RGBColor(0x2C, 0x3E, 0x50)
doc.add_paragraph('')
doc.add_paragraph('图示:')
doc.add_picture(os.path.join(IMG_DIR, 'row_major.png'), width=Cm(16))
doc.add_paragraph('')
doc.add_heading('三、方式二:从上到下,从左到右(列优先)', level=1)
doc.add_paragraph('遍历规则:')
p=doc.add_paragraph('每一列内,从上向下依次访问每个单元格', style='List Bullet')
p=doc.add_paragraph('当前列访问完毕后,换到下一列,继续从上到下', style='List Bullet')
p=doc.add_paragraph('所有列从左到右依次处理', style='List Bullet')
doc.add_paragraph('访问顺序公式:')
p=doc.add_paragraph()
run=p.add_run('index = col × 行数 + row')
run.font.name ='Consolas'
run.font.size = Pt(11)
run.font.color.rgb = RGBColor(0x2C, 0x3E, 0x50)
doc.add_paragraph('')
doc.add_paragraph('图示:')
doc.add_picture(os.path.join(IMG_DIR, 'col_major.png'), width=Cm(16))
# ===== 四、对比 =====
doc.add_heading('四、对比总结', level=1)
doc.add_paragraph('')
doc.add_paragraph('两种遍历顺序的直观对比:')
doc.add_paragraph('')
doc.add_picture(os.path.join(IMG_DIR, 'comparison.png'), width=Cm(16))
doc.add_paragraph('')
# 对比表格
t2=doc.add_table(rows=6, cols=3)
t2.style ='Light Grid Accent 1'
t2.alignment = WD_TABLE_ALIGNMENT.CENTER
headers= ['对比项', '从左到右 → 从上到下(行优先)', '从上到下 ↓ 从左到右(列优先)']
data= [
['遍历方向', '行内水平移动为主', '列内垂直移动为主'],
['内存访问', '行内连续访问,缓存友好', '列内跳跃访问,缓存不友好'],
['适用场景', 'C/C++/Python 数组、图像处理', 'Fortran/MATLAB 数组、GIS 栅格'],
['循环嵌套', '外层 for row,内层 for col', '外层 for col,内层 for row'],
['别名', 'Row-Major Order', 'Column-Major Order'],
]
forj, hinenumerate(headers):
cell=t2.cell(0, j)
cell.text =h
forpincell.paragraphs:
forruninp.runs:
run.font.bold =True
run.font.size = Pt(10)
fori, row_datainenumerate(data):
forj, valinenumerate(row_data):
cell=t2.cell(i+1, j)
cell.text =val
forpincell.paragraphs:
forruninp.runs:
run.font.size = Pt(10)
doc.add_paragraph('')
# ===== 五、示例代码 =====
doc.add_heading('五、代码示例', level=1)
doc.add_heading('5.1 行优先(从左到右,从上到下)', level=2)
code1="""# C / Python 风格 — 行优先
for row in range(ROWS): # 外层:行(从上到下)
for col in range(COLS): # 内层:列(从左到右)
visit(grid[row][col])
# 访问顺序:(0,0)→(0,1)→(0,2)→...→(1,0)→(1,1)→..."""
p=doc.add_paragraph()
run=p.add_run(code1)
run.font.name ='Consolas'
run.font.size = Pt(9)
run.font.color.rgb = RGBColor(0x2C, 0x3E, 0x50)
doc.add_heading('5.2 列优先(从上到下,从左到右)', level=2)
code2="""# 列优先遍历
for col in range(COLS): # 外层:列(从左到右)
for row in range(ROWS): # 内层:行(从上到下)
visit(grid[row][col])
# 访问顺序:(0,0)→(1,0)→(2,0)→...→(0,1)→(1,1)→..."""
p=doc.add_paragraph()
run=p.add_run(code2)
run.font.name ='Consolas'
run.font.size = Pt(9)
run.font.color.rgb = RGBColor(0x2C, 0x3E, 0x50)
# ===== 六、注意事项 =====
doc.add_heading('六、注意事项', level=1)
notes= [
'行优先是 C、C++、Python(NumPy 默认)等语言的数组存储方式',
'列优先是 Fortran、MATLAB、R 等语言的数组存储方式',
'在 GIS 栅格数据处理中,两种遍历方式都常见,需注意数据格式要求的存储顺序',
'行优先遍历时,相邻访问的内存地址连续,CPU 缓存命中率更高,性能通常更好',
'选择哪种遍历方式取决于具体应用场景和数据存储格式',
]
fornoteinnotes:
doc.add_paragraph(note, style='List Bullet')
# 保存
out_path=os.path.join(OUT_DIR, '遍历顺序对比说明_新.docx')
doc.save(out_path)
print(f'Document saved: {out_path}')
# 清理临时图片
importshutil
shutil.rmtree(IMG_DIR)
print('Temp images cleaned up.')