Pull to refresh
115
0

Python developer

Send message

Ох, это значительно проще. Спасибо, обновил.

Крайне досадно вышло.

Пересобрал анимации, где-то подрезал, где-то цвета убрал. Вроде на качестве не сильно сказалось, зато теперь бо́льшая часть на habrastorage поместилась. Теперь должно быть лучше.

Да, если задать нечётный радиус, этот эффект пропадёт. Спасибо за дополнение.

R3/T3/G6/NNR

Из конфигураций на ум приходят только циклические КА и их подвиды, где определён строгий порядок перехода, вроде a → b → … → n → a. Рассмотрим на следующих неделях.

Много подобных можно найти в частных вариантах. Например, при моделировании лесных пожаров огонь выполняет роль хищника, а деревья – жертвы. Со своими дополнениями и вариациями.

Как раз на прошлой неделе смотрели на цветные орнаменты)

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

До особых правил пока далековато, да и на Хабре уже есть соответствующая статья. Присмотрюсь.

У меня пока нет. Может на следующих неделях, но загадывать не буду.

Лаадно

На выгоревшей земле (серый) 5 ходов ничего не растёт

import matplotlib.pyplot as plt
import numpy as np
from matplotlib import animation, colors

# https://scipython.com/blog/the-forest-fire-model/
# Christian Hill, январь 2016.
# Обновлено в январе 2020.
# Дополнения: водные преграды by Jen Ciarochi:
# https://triplebyte.com/blog/how-fire-spreads-mathematical-models-and-simulators

NY = (-1, -1, -1, 0, 0, 1, 1, 1)
NX = (-1, 0, 1, -1, 1, -1, 0, 1)
NZ = (0.1, 0.1, 0.1, 0.1, 1, 0.1, 1, 1)
EMPTY, TREE, FIRE, WATER, SEED, SPROUT, BURNT = 0, 1, 2, 3, 4, 5, 6
colors_list = [(0.2, 0, 0), (0, 0.5, 0), (1, 0, 0), 'blue', 'brown', 'lightgreen', 'darkgray']
cmap = colors.ListedColormap(colors_list)
bounds = tuple(range(8))
norm = colors.BoundaryNorm(bounds, cmap.N)
burnt_cells = {}


def iterate(X):
    """Итерации леса в соответствии с четырьмя правилами лесного пожара"""

    X1 = np.zeros((ny, nx))
    for ix in range(1, nx - 1):
        for iy in range(1, ny - 1):
            cell = X[iy, ix]
            if cell == WATER:
                X1[iy, ix] = WATER
                continue
            elif cell == FIRE:
                burnt_cells[(iy, ix)] = 5
                X1[iy, ix] = BURNT
                continue
            elif cell == BURNT:
                if burnt_cells[(iy, ix)]:
                    burnt_cells[(iy, ix)] -= 1
                    X1[iy, ix] = BURNT
                else:
                    del burnt_cells[(iy, ix)]
                    X1[iy, ix] = EMPTY
                continue
            burning_neighbours = 0
            fertile_neighbours = 0
            for dy, dx in zip(NY, NX):
                if abs(dy) == abs(dx) and np.random.random() < 0.573:
                    continue
                if X[iy+dy, ix+dx] == FIRE:
                    burning_neighbours += 1
                elif X[iy+dy, ix+dx] == TREE:
                    fertile_neighbours += 1
            if np.random.random() <= fire_spread_chances[X[iy, ix]][burning_neighbours]:
                X1[iy, ix] = FIRE
            elif cell == SEED:
                X1[iy, ix] = SPROUT
            elif cell == SPROUT:
                X1[iy, ix] = TREE
            elif cell == TREE:
                X1[iy, ix] = TREE
            elif np.random.random() <= forest_spread_chances[fertile_neighbours]:
                X1[iy, ix] = SEED
    return X1


forest_fraction = 0.3
p = 0.0001
fire_spread_chances = {
        0: [0] * 9,  # EMPTY
        1: [0.0002] + [0.8 + 0.03*x for x in range(8)],  # TREE
        4: [0.0000] + [0.2 + 0.05*x for x in range(8)],  # SEED
        5: [0.0001] + [0.3 + 0.07*x for x in range(8)],  # SEED
    }
forest_spread_chances = [0.0001] + [0.1 + 0.1*x for x in range(8)]
nx, ny = 100, 100
X = np.zeros((ny, nx))
X[1:ny-1, 1:nx-1] = np.random.randint(0, 2, size=(ny - 2, nx - 2))
X[1:ny-1, 1:nx-1] = np.random.random(size=(ny - 2, nx - 2)) < forest_fraction
X[10:50, 10:12] = WATER
X[30:31, 10:30] = WATER
X[10:50, 28:30] = WATER
X[11:40, 30:32] = WATER
X[10:40, 43:45] = WATER
X[25:26, 30:44] = WATER
X[10:11, 30:44] = WATER

fig = plt.figure(figsize=(25 / 3, 6.25))
ax = fig.add_subplot(111)
ax.set_axis_off()
im = ax.imshow(X, cmap=cmap, norm=norm)  # , interpolation='nearest')

def animate(i):
    im.set_data(animate.X)
    animate.X = iterate(animate.X)


animate.X = X

interval = 100
anim = animation.FuncAnimation(fig, animate, interval=interval, frames=200)
plt.show()

Значения из прошлого комментария, с припиской "выглядит хаотично. С ними ещё нужно играться"

Окей

Добавили новые состояния клеток – семена и ростки. У всех свои шансы на сгорание (с потолка), в зависимости от количества огня вокруг. Деревья разбрасывают семена, аналогично распространению огня, но в зависимости от количества взрослых деревьев вокруг, с шансом 0.0001 появления семечка из ничего. Семечки (коричневые) за 1 шаг превращаются в ростки, ростки (светло-зелёные) – в деревья. Модель без ветра, с водой.

Выглядит хаотично, надо со значениями играться.

import matplotlib.pyplot as plt
import numpy as np
from matplotlib import animation, colors

# https://scipython.com/blog/the-forest-fire-model/
# Christian Hill, январь 2016.
# Обновлено в январе 2020.
# Дополнения: ветер, водные преграды by Jen Ciarochi:
# https://triplebyte.com/blog/how-fire-spreads-mathematical-models-and-simulators

NY = (-1, -1, -1, 0, 0, 1, 1, 1)
NX = (-1, 0, 1, -1, 1, -1, 0, 1)
NZ = (0.1, 0.1, 0.1, 0.1, 1, 0.1, 1, 1)
EMPTY, TREE, FIRE, WATER, SEED, SPROUT = 0, 1, 2, 3, 4, 5
colors_list = [(0.2, 0, 0), (0, 0.5, 0), (1, 0, 0), 'blue', 'brown', 'lightgreen']
cmap = colors.ListedColormap(colors_list)
bounds = [0, 1, 2, 3, 4, 5, 6]
norm = colors.BoundaryNorm(bounds, cmap.N)


def iterate(X):
    """Итерации леса в соответствии с четырьмя правилами лесного пожара"""

    X1 = np.zeros((ny, nx))
    for ix in range(1, nx - 1):
        for iy in range(1, ny - 1):
            cell = X[iy, ix]
            if cell == WATER:
                X1[iy, ix] = WATER
                continue
            elif cell == FIRE:
                X1[iy, ix] = EMPTY
                continue
            burning_neighbours = 0
            fertile_neighbours = 0
            for dy, dx in zip(NY, NX):
                if abs(dy) == abs(dx) and np.random.random() < 0.573:
                    continue
                if X[iy+dy, ix+dx] == FIRE:
                    burning_neighbours += 1
                elif X[iy+dy, ix+dx] == TREE:
                    fertile_neighbours += 1
            if np.random.random() <= fire_spread_chances[X[iy, ix]][burning_neighbours]:
                X1[iy, ix] = FIRE
            elif cell == SEED:
                X1[iy, ix] = SPROUT
            elif cell == SPROUT:
                X1[iy, ix] = TREE
            elif cell == TREE:
                X1[iy, ix] = TREE
            elif np.random.random() <= forest_spread_chances[fertile_neighbours]:
                X1[iy, ix] = SEED
    return X1


forest_fraction = 0.3
p = 0.0001
fire_spread_chances = {
        0: [0] * 9,  # EMPTY
        1: [0.0002] + [0.8 + 0.03*x for x in range(8)],  # TREE
        4: [0.0000] + [0.2 + 0.05*x for x in range(8)],  # SEED
        5: [0.0001] + [0.3 + 0.07*x for x in range(8)],  # SPROUT
    }
forest_spread_chances = [0.0001] + [0.1 + 0.1*x for x in range(8)]
nx, ny = 100, 100
X = np.zeros((ny, nx))
X[1:ny-1, 1:nx-1] = np.random.randint(0, 2, size=(ny - 2, nx - 2))
X[1:ny-1, 1:nx-1] = np.random.random(size=(ny - 2, nx - 2)) < forest_fraction
X[10:50, 10:12] = WATER
X[30:31, 10:30] = WATER
X[10:50, 28:30] = WATER

fig = plt.figure(figsize=(25 / 3, 6.25))
ax = fig.add_subplot(111)
ax.set_axis_off()
im = ax.imshow(X, cmap=cmap, norm=norm)  # , interpolation='nearest')

def animate(i):
    im.set_data(animate.X)
    animate.X = iterate(animate.X)


animate.X = X

interval = 100
anim = animation.FuncAnimation(fig, animate, interval=interval, frames=200)
plt.show()

Опустим шутку про рост деревьев в реальном времени.

Модель без ветра и воды; вероятность появления нового дерева в два раза ниже вероятности первичного возгорания; оставим низкую влажность (0.8+0.03*x), при высокой совсем наглядность пропадёт; начало с 60% леса:

import matplotlib.pyplot as plt
import numpy as np
from matplotlib import animation, colors

# https://scipython.com/blog/the-forest-fire-model/
# Christian Hill, январь 2016.
# Обновлено в январе 2020.

NY = (-1, -1, -1, 0, 0, 1, 1, 1)
NX = (-1, 0, 1, -1, 1, -1, 0, 1)
EMPTY, TREE, FIRE = 0, 1, 2
colors_list = [(0.2, 0, 0), (0, 0.5, 0), (1, 0, 0), 'orange']
cmap = colors.ListedColormap(colors_list)
bounds = [0, 1, 2, 3]
norm = colors.BoundaryNorm(bounds, cmap.N)


def iterate(X):
    """Итерации леса в соответствии с четырьмя правилами лесного пожара"""

    X1 = np.zeros((ny, nx))
    for ix in range(1, nx - 1):
        for iy in range(1, ny - 1):
            if X[iy, ix] == EMPTY and np.random.random() <= p:
                X1[iy, ix] = TREE
            elif X[iy, ix] == TREE:
                X1[iy, ix] = TREE
                burning_neighbours = 0
                for dx, dy in zip(NY, NX):
                    if abs(dx) == abs(dy) and np.random.random() < 0.573:
                        continue
                    if X[iy+dy, ix+dx] == FIRE:
                        burning_neighbours += 1
                if np.random.random() <= spread_chances[burning_neighbours]:
                    X1[iy, ix] = FIRE
    return X1


forest_fraction = 0.6
p = 0.0001
spread_chances = [0.0002] + [0.8 + 0.03*x for x in range(8)]
nx, ny = 100, 100
X = np.zeros((ny, nx))
X[1:ny-1, 1:nx-1] = np.random.randint(0, 2, size=(ny - 2, nx - 2))
X[1:ny-1, 1:nx-1] = np.random.random(size=(ny - 2, nx - 2)) < forest_fraction

fig = plt.figure(figsize=(25 / 3, 6.25))
ax = fig.add_subplot(111)
ax.set_axis_off()
im = ax.imshow(X, cmap=cmap, norm=norm)  # , interpolation='nearest')

def animate(i):
    im.set_data(animate.X)
    animate.X = iterate(animate.X)


animate.X = X

interval = 5
anim = animation.FuncAnimation(fig, animate, interval=interval, frames=200)
plt.show()

Можно считать, что мы добавили характеристику повышенной влажности. Пожалуйста)

import matplotlib.pyplot as plt
import numpy as np
from matplotlib import animation, colors

# https://scipython.com/blog/the-forest-fire-model/
# Christian Hill, январь 2016.
# Обновлено в январе 2020.
# Дополнения: ветер, водные преграды by Jen Ciarochi:
# https://triplebyte.com/blog/how-fire-spreads-mathematical-models-and-simulators

NY = (-1, -1, -1, 0, 0, 1, 1, 1)
NX = (-1, 0, 1, -1, 1, -1, 0, 1)
NZ = (0.1, 0.1, 0.1, 0.1, 1, 0.1, 1, 1)
EMPTY, TREE, FIRE, WATER = 0, 1, 2, 3
colors_list = [(0.2, 0, 0), (0, 0.5, 0), (1, 0, 0), 'orange', 'blue']
cmap = colors.ListedColormap(colors_list)
bounds = [0, 1, 2, 3, 4]
norm = colors.BoundaryNorm(bounds, cmap.N)


def iterate(X):
    """Итерации леса в соответствии с четырьмя правилами лесного пожара"""

    X1 = np.zeros((ny, nx))
    for ix in range(1, nx - 1):
        for iy in range(1, ny - 1):
            if X[iy, ix] == WATER:
                X1[iy, ix] = WATER
            elif X[iy, ix] == EMPTY and np.random.random() <= p:
                X1[iy, ix] = TREE
            elif X[iy, ix] == TREE:
                X1[iy, ix] = TREE
                burning_neighbours = 0
                for dx, dy, dz in zip(NY, NX, NZ):
                    if abs(dx) == abs(dy) and np.random.random() < 0.573:
                        continue
                    if X[iy+dy, ix+dx] == FIRE and np.random.random() <= dz:
                        # X1[iy, ix] = FIRE
                        burning_neighbours += 1
                if np.random.random() <= spread_chances[burning_neighbours]:
                    X1[iy, ix] = FIRE
    return X1


forest_fraction = 0.2
p = 0.03
spread_chances = [0.0001] + [0.5 + 0.06*x for x in range(8)]
nx, ny = 100, 100
X = np.zeros((ny, nx))
X[1:ny-1, 1:nx-1] = np.random.randint(0, 2, size=(ny - 2, nx - 2))
X[1:ny-1, 1:nx-1] = np.random.random(size=(ny - 2, nx - 2)) < forest_fraction
X[10:90, 10:15] = WATER
X[10:90, 40:45] = WATER
X[10:90, 60:65] = WATER
X[10:90, 80:85] = WATER

fig = plt.figure(figsize=(25 / 3, 6.25))
ax = fig.add_subplot(111)
ax.set_axis_off()
im = ax.imshow(X, cmap=cmap, norm=norm)  # , interpolation='nearest')

def animate(i):
    im.set_data(animate.X)
    animate.X = iterate(animate.X)


animate.X = X

interval = 50
anim = animation.FuncAnimation(fig, animate, interval=interval, frames=200)
plt.show()

Жаль, тут в блоках кода нельзя вручную выделять цветом/жиром.

Изменения:
33 строка – добавили счётчик горящих соседей (учитывает 0.573 шанс на диагональных);
37-39 – теперь не объявляем дерево горящим при первом же огненном соседе с break, а просто увеличиваем счётчик;
40-41 – убираем else, он уже не нужен. По spread_chances от количества горящих соседей бросаем кубик на продолжение пожара;
45 – чуть снизили вероятность появления нового дерева;
46 – f убрали, это теперь spread_chances[0];
47 – добавили вероятность продолжения пожара, от количества горящих соседей. [0.0001, 0.5, 0.56, 0.62, 0.68, 0.74, 0.8, 0.86, 0.92]. Немного поигрался со значениями, эти выглядят неплохо.

upd: эх, при редактировании номера строк видны, а при прочтении нет :с

Всё так) Это одна из вариаций клеточного автомата, с ней не будет надобности в этом дополнении

Статьи будут оцениваться отдельно по техническим и журналистским критериям, с использованием 10-балльной шкалы

Будет публичный рейтинг или это только для внутреннего использования?

Оригинал изначально тяжеловато написан. Надеюсь, не усложнил его дополнительно)

tl;dr для классического метода: https://youtu.be/awBsjdjxJnE
Он сам по себе очень интересен. Остальное – для тех, кого сильно заинтересовали детали и вариации с условиями.

Один и тот же процесс, смотря, с какой стороны) Со стороны общества – сегрегация, [но] на агрегированные группы.

Пост в целом отвечает на этот вопрос – категорию \p{L} и её подкатегории. В частных случаях можно посмотреть на категорию письменности или их комбинацию, но там будут и небуквенные символы.

Если реализация не поддерживает категории, то уже нужно смотреть конкретные доки. В Python, к примеру, стандартный re не поддерживает, и нужно или использовать сторонние модули, или как-то выкручиваться через str.isalpha(), который как раз и отсылает к категориям Юникода. \w в re работает аналогично str.isalnum(), потому найдёт весь \p{L}, но вместе с \p{N} и _. С флагом ASCII будет работать аналогично реализациям с поддержкой категорий, где \w ограничивается только стандартной [a-z] латиницей + [0-9_].

А что происходит со статьями без указанного уровня? Чуть понажимал – часть сложилась в "простые", а часть пропала (upd: это был временный баг, увидел. Видимо, они просто пропадают). Не рассматриваете отдельную опцию "не указано"?

Не хватает возможности мультивыбора в фильтрах и фильтров в целом в "моей ленте".

Ну и предложение от себя:
Возможность голосовать за уровень сложности статьи, с автоматическим изменением уровня. Может решить проблему статей без указанного уровня (и почти всех статей до сего момента). Для статей с не совсем правильно указанным уровнем может скорректировать (изначальное мнение автора учитывается, например, с весом 10, если есть).

Спасибо, добавлю пояснение. Их в оригинальном материале не было, добавил от себя. Это ссылки (сливаются с обычным текстом до наведения) на сторонний ресурс, где можно для примера взглянуть на конкретные коды, которые относятся к данной категории. Группы с неявно привязанными кодами ({Private_Use}, {Surrogate} и {Unassigned}) или являющиеся объединением других подкатегорий ссылок не имеют. Также не указывал ссылки для {Paragraph_Separator} и {Line_Separator}, где всего по одному коду.

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

Уже второй раз в этой ветке неявно вспоминается французский революционный календарь)

Information

Rating
Does not participate
Registered
Activity

Specialization

Backend Developer
Python