如何用NumPy向量化优化随机瓦片地图生成算法?
基于NumPy的瓦片地图生成性能优化方案
核心优化思路
放弃逐个调用check_tile的标量处理逻辑,转而用NumPy的批量数组操作完成边界瓦片的邻域统计、概率计算和子瓦片生成,彻底消除Python循环带来的性能开销。
具体实现步骤
1. 初始网格定义
用布尔数组表示地图:True为陆地,False为水域(可根据需求调整映射关系),直接通过NumPy生成初始网格:
import numpy as np # 初始化4x4全陆地网格,可选加入随机初始水域 map_grid = np.ones((4, 4), dtype=bool) map_grid[np.random.rand(4,4) < 0.1] = False
2. 批量标记边界瓦片
边界瓦片定义为邻接至少一个水域的陆地瓦片,用NumPy卷积操作批量计算所有瓦片的邻域水域数量,快速标记边界:
def get_border_tiles(grid): # 3x3邻域掩码,排除自身 kernel = np.ones((3,3), dtype=int) kernel[1,1] = 0 # 批量计算每个瓦片的邻域水域数(水域用1-grid转换为1标记) neighbor_water_count = np.convolve((1-grid).flatten(), kernel.flatten(), mode='same').reshape(grid.shape) # 生成边界瓦片掩码:陆地且邻接水域 border_mask = grid & (neighbor_water_count > 0) # 返回边界瓦片的坐标数组(每行对应(i,j)) return np.argwhere(border_mask)
3. 向量化处理边界瓦片的细分与转换
跳过逐个处理瓦片的逻辑,直接批量完成网格放大、边界瓦片的概率计算和子瓦片生成:
def upscale_and_process_borders(grid, target_size): while grid.shape[0] < target_size: current_size = grid.shape[0] # 快速生成放大后的网格:每个原瓦片拆分为2x2子瓦片,默认继承原类型 upscale_grid = np.repeat(np.repeat(grid, 2, axis=0), 2, axis=1) border_coords = get_border_tiles(grid) if len(border_coords) == 0: break # 批量获取所有边界瓦片的邻域水域数(消除循环的优化版) full_water_counts = get_full_neighbor_water_counts(grid) neighbor_water_counts = full_water_counts[tuple(border_coords.T)] # 计算子瓦片转为水域的概率(示例公式:邻接水域数/8,可自定义) convert_probs = neighbor_water_counts / 8.0 # 批量生成随机判断结果:每个边界瓦片对应4个子瓦片 rand_vals = np.random.rand(len(border_coords), 4) convert_mask = rand_vals < convert_probs[:, np.newaxis] # 批量更新放大后的网格 for idx, (i,j) in enumerate(border_coords): # 原瓦片对应的4个子瓦片坐标 upscale_pos = [ (2*i, 2*j), (2*i, 2*j+1), (2*i+1, 2*j), (2*i+1, 2*j+1) ] for k, (y,x) in enumerate(upscale_pos): if convert_mask[idx,k]: upscale_grid[y,x] = False grid = upscale_grid return grid # 纯NumPy实现的全网格邻域水域数计算(消除所有循环) def get_full_neighbor_water_counts(grid): current_size = grid.shape[0] water_counts = np.zeros(grid.shape, dtype=int) # 处理内部瓦片(3x3完整邻域) windowed = np.lib.stride_tricks.as_strided(grid, shape=(current_size-2, current_size-2, 3, 3), strides=grid.strides * 2) water_counts[1:-1,1:-1] = np.sum(1 - windowed, axis=(2,3)) # 处理四个角落 water_counts[0,0] = np.sum(1 - grid[0:2,0:2]) water_counts[0,-1] = np.sum(1 - grid[0:2,-2:]) water_counts[-1,0] = np.sum(1 - grid[-2:,0:2]) water_counts[-1,-1] = np.sum(1 - grid[-2:,-2:]) # 处理上下边缘(非角落) water_counts[0,1:-1] = np.sum(1 - grid[0:2,1:-1], axis=0) water_counts[-1,1:-1] = np.sum(1 - grid[-2:,1:-1], axis=0) # 处理左右边缘(非角落) water_counts[1:-1,0] = np.sum(1 - grid[1:-1,0:2], axis=1) water_counts[1:-1,-1] = np.sum(1 - grid[1:-1,-2:], axis=1) return water_counts
4. 测试与验证
# 生成1024x1024的地图 final_map = upscale_and_process_borders(np.ones((4,4), dtype=bool), 1024) print(f"陆地占比: {np.mean(final_map):.2%}")
性能提升核心点
- 彻底消除Python循环:所有核心计算用NumPy向量化操作完成,避开解释器的性能瓶颈
- 批量处理逻辑:一次性完成所有边界瓦片的邻域统计、概率计算和子瓦片转换,替代逐个
check_tile的冗余操作 - 高效网格放大:用
np.repeat快速生成放大网格,比手动拆分瓦片的效率提升数十倍
内容的提问来源于stack exchange,提问作者anyboundry
相关产品推荐
相关产品推荐

