2025, Nov 17 03:02

Генерация гладкого 2D-поля в Python: аттракторы и контролируемый белый шум

Создайте гладкое 2D-поле без резких границ: аттракторы задают структуру, белый шум добавляет текстуру. Простой пример на Python с контролируемой гладкостью.

Когда вы заполняете двумерную сетку независимыми случайными значениями, получается пятнистая текстура с высокой локальной вариативностью и почти без структуры. Для белого шума это нормально, но не для фонового поля, которое должно плавно меняться в пределах соседних областей и при этом демонстрировать глобальные различия по всей карте. Ниже показан компактный подход, позволяющий контролировать гладкость и крупномасштабные узоры без появления резких границ.

Постановка задачи

Прямолинейный способ заполнить meshgrid — независимо сэмплировать каждый пиксель. Это даёт шумную поверхность с резкими локальными отклонениями и без пространственной корреляции.

import numpy as np

u = np.linspace(-2, 2, 100)
v = np.linspace(-2, 2, 100)
U, V = np.meshgrid(u, v)
W = np.random.rand(100, 100)

Результат — классическая текстура белого шума: доминируют высокочастотные изменения, а соседние пиксели не «согласуются» друг с другом. Для поверхности в стиле карты фоновой радиации обычно нужна локальная плавность и широкие, постепенные изменения по всей области.

Почему базовый подход выглядит шумным

При независимом сэмплировании в каждом пикселе соседние значения никак не связаны между собой, поэтому у поверхности нет пространственной согласованности. Вы видите сильную локальную вариативность, но минимум структуры на больших масштабах. Если вы хотите, чтобы поле медленно менялось в малых окрестностях и при этом сохраняло глобальные отличия при отдалении, нужно ввести пространственную корреляцию, а затем при желании добавить контролируемое количество аддитивного шума.

Подход с контролируемым полем: базовый слой + аддитивный белый шум

Простой способ добавить структуру — задать гладкое базовое поле через набор аттракторов (точек, влияющих на сетку), суммировать их вклад по всей карте и затем добавить немного белого шума для мелкой текстуры. Так вы получаете прямой контроль и над крупномасштабной формой, и над локальной шероховатостью.

Идея проста. Случайно размещаем аттракторы с положительными или отрицательными весами. Для каждого пикселя суммируем влияние всех аттракторов с помощью функции, похожей на обратную зависимость от расстояния. Получившееся поле гладкое и глобально разнообразное. В завершение при необходимости добавляем настраиваемый аддитивный белый шум, чтобы внести лёгкие локальные неровности.

Реализация

import numpy as np
import matplotlib.pyplot as plt

def synth_region(side: int, jitter_amp: float):
    # Повторяемый генератор случайных чисел
    prng = np.random.default_rng(seed=14)
    # Выходное поле
    field = np.empty((side, side), dtype=float)
    # Число аттракторов
    k_anchors = int(side)
    # Координаты аттракторов
    anchor_xy = prng.integers(0, side, (k_anchors, 2), endpoint=False)
    # Веса аттракторов в диапазоне [-1, 1]
    anchor_gain = prng.random(size=k_anchors) * 2 - 1
    # Накопление влияния аттракторов для каждого пикселя
    for ix in range(side):
        for iy in range(side):
            accum = 0.0
            for pt, weight in zip(anchor_xy, anchor_gain):
                accum += (np.linalg.norm(pt - (ix, iy)) + side ** 0.5) ** -1 * weight
            field[ix, iy] = accum
    # Аддитивный белый шум, масштабированный через jitter_amp
    field += prng.random(size=(side, side)) * jitter_amp
    plt.imshow(field)
    plt.show()

# Примеры с нарастающей мелкомасштабной текстурой
synth_region(100, 0.0)
synth_region(100, 0.01)
synth_region(100, 0.05)

Это даёт гладкое поле, формируемое пространственно распределёнными источниками влияния. Параметр jitter_amp управляет количеством высокочастотной текстуры поверх базовой структуры. Нулевое значение подчёркивает крупномасштабную вариацию и плавные переходы; повышение параметра добавляет локальную шероховатость.

Можно менять число аттракторов, их позиции и веса, чтобы сформировать нужный ландшафт. Также можно изменить функцию влияния, регулируя скорость затухания с расстоянием. Эти настройки делают метод очень гибким и помогают избежать блочных переходов и жёстких границ.

Почему это важно

Когда нужна 2D-поверхность, которая выглядит более «физичной», а не случайной — то есть медленно меняется на коротких дистанциях, но заметно варьируется на больших масштабах, — ключ в управлении пространственной корреляцией. Определив базовое поле и добавив небольшое количество аддитивного белого шума, вы получите предсказуемое, воспроизводимое поведение с минимумом артефактов и сможете точно задать степень гладкости или разнообразия карты.

Если предпочитаете начинать с грубой разметки и уточнять её, можно интерполировать разреженную сетку до плотной, например с помощью scipy.interpolate.griddata. А если цель — повысить энергию на низких пространственных частотах, направленная коррекция спектра тоже даст сильную глобальную вариацию при ограничённых локальных изменениях.

Итоги

Независимое пер-пиксельное сэмплирование даёт белый шум и разрушает локальную согласованность. Если вам нужен гладкий фон с глобальной структурой, сформируйте коррелированное базовое поле и, только при необходимости, добавьте контролируемое количество аддитивного белого шума. Показанный выше паттерн накопления влияния аттракторов — компактный путь к результату; его легко настраивать, варьируя расположение аттракторов, их веса, форму функции влияния и итоговую амплитуду шума.