You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Python中生成类烟雾/精灵形状的随机网格图案?

问题描述

用户希望生成类似如下的网格形状:

#  # # #  #
  #   #
        # #  
     # #
  #
   #
# # # #   #
#   # #
   #   #
         #  
     #
   #
      #

需求:在height×width的网格中随机分布x个元素,需满足:

  1. 顶部行的元素出现概率更高;
  2. 每行元素大概率靠近但偏移上一行的位置。

用户原本考虑用钟形分布实现,但希望寻求更具编程性、可读性的非数学化解决方案。


非数学化解决方案

方案一:继承式随机偏移

核心逻辑是从上到下逐行生成,每一行的元素位置依赖上一行的位置做小范围偏移,同时给顶部行更高的元素密度,完全不用数学分布公式,逻辑直白。

步骤:

  • 初始化空网格:用二维列表表示,每个位置默认是空格。
  • 顶部行优先填充:第一行直接随机生成较多元素(比如占总元素的25%,或者按行数从顶到底递减分配数量)。
  • 逐行偏移生成:从第二行开始,遍历上一行的每个元素位置,在其左右±1或±2的范围内随机选位置作为当前行的元素;同时可以随机跳过部分位置,或者新增少量随机位置,避免完全复制。
  • 补全/裁剪元素数量:如果生成的元素总数超过x,就随机删除多余的;如果不够,就在上方行优先补加。

代码示例:

import random

def generate_shape(height, width, total_elements):
    # 初始化空网格
    grid = [[' ' for _ in range(width)] for _ in range(height)]
    
    # 按行分配元素数量:顶部行最多,往下递减
    element_counts = []
    for i in range(height):
        # 顶部行占比最高,每行递减一定比例
        ratio = 1 - (i / height) * 0.7
        count = max(1, int(total_elements * ratio))
        element_counts.append(count)
    
    # 调整总数量,避免超过或不足
    total_assigned = sum(element_counts)
    if total_assigned > total_elements:
        # 从底部行开始减
        for i in range(height-1, -1, -1):
            if total_assigned <= total_elements:
                break
            element_counts[i] = max(1, element_counts[i]-1)
            total_assigned -=1
    elif total_assigned < total_elements:
        # 从顶部行开始加
        for i in range(height):
            if total_assigned >= total_elements:
                break
            element_counts[i] +=1
            total_assigned +=1
    
    # 第一行随机生成元素
    positions = random.sample(range(width), element_counts[0])
    for pos in positions:
        grid[0][pos] = '#'
    
    # 逐行偏移生成
    for row in range(1, height):
        prev_positions = [i for i, val in enumerate(grid[row-1]) if val == '#']
        current_positions = []
        
        # 基于上一行位置偏移
        for pos in prev_positions:
            # 80%概率在±1范围内偏移,20%概率随机选位置
            if random.random() < 0.8:
                new_pos = pos + random.choice([-1, 0, 1])
                new_pos = max(0, min(new_pos, width-1))
                current_positions.append(new_pos)
            else:
                current_positions.append(random.randint(0, width-1))
        
        # 补充数量(如果不够当前行需要的数量)
        while len(current_positions) < element_counts[row]:
            current_positions.append(random.randint(0, width-1))
        
        # 去重(避免同一位置重复设置)
        current_positions = list(set(current_positions))
        # 如果超过数量,随机删掉一些
        if len(current_positions) > element_counts[row]:
            current_positions = random.sample(current_positions, element_counts[row])
        
        # 设置当前行元素
        for pos in current_positions:
            grid[row][pos] = '#'
    
    # 打印网格
    for line in grid:
        print(''.join(line))

# 调用示例:生成7行10列,共15个元素
generate_shape(7, 10, 15)

方案二:路径扩散式生成

把元素分布看成从顶部起始点开始的“扩散路径”,每一行的元素是上一行元素的分支,逻辑类似树枝生长,同样不需要数学分布。

步骤:

  • 初始化空网格,顶部行随机选多个起始点(数量多于下面的行)。
  • 逐行处理:对上一行的每个元素,有较高概率(比如80%)在其上下左右小范围生成下一行的元素,同时顶部行的元素有更高的“扩散概率”;另外可以随机新增少量元素,增加随机性。
  • 控制总元素数量:如果超过x就随机删除,不够就从顶部补。

代码示例:

import random

def generate_shape_v2(height, width, total_elements):
    grid = [[' ' for _ in range(width)] for _ in range(height)]
    current_elements = 0
    
    # 顶部行先放一批元素(占总数量的30%)
    top_count = max(2, int(total_elements * 0.3))
    top_positions = random.sample(range(width), top_count)
    for pos in top_positions:
        grid[0][pos] = '#'
        current_elements +=1
    
    # 逐行扩散
    for row in range(1, height):
        prev_positions = [i for i, val in enumerate(grid[row-1]) if val == '#']
        # 顶部行的扩散概率更高,往下递减
        spread_prob = 0.9 - (row / height) * 0.4
        
        for pos in prev_positions:
            if current_elements >= total_elements:
                break
            # 按概率生成下一行的元素
            if random.random() < spread_prob:
                # 随机偏移±1或不偏移
                new_pos = pos + random.choice([-1, 0, 1])
                new_pos = max(0, min(new_pos, width-1))
                if grid[row][new_pos] == ' ':
                    grid[row][new_pos] = '#'
                    current_elements +=1
        
        # 如果当前行元素太少,随机补一些(优先上面的行)
        if current_elements < total_elements and row < height//2:
            add_count = min(2, total_elements - current_elements)
            for _ in range(add_count):
                pos = random.randint(0, width-1)
                if grid[row][pos] == ' ':
                    grid[row][pos] = '#'
                    current_elements +=1
    
    # 如果还不够,在任意行补(优先顶部)
    while current_elements < total_elements:
        row = random.randint(0, height//2)
        pos = random.randint(0, width-1)
        if grid[row][pos] == ' ':
            grid[row][pos] = '#'
            current_elements +=1
    
    # 打印网格
    for line in grid:
        print(''.join(line))

# 调用示例
generate_shape_v2(7, 10, 15)

方案优势

这两种方案都不需要理解钟形分布等数学概念,代码逻辑直观:

  • 核心都是从上到下继承位置关系,保证每行元素靠近上一行;
  • 通过行级别的数量比例/扩散概率控制顶部行的高密度;
  • 参数(比如偏移范围、概率值、数量占比)都可以直接调整,非常灵活。

内容的提问来源于stack exchange,提问作者user3310334

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 14:05:47