Пересобрал анимации, где-то подрезал, где-то цвета убрал. Вроде на качестве не сильно сказалось, зато теперь бо́льшая часть на habrastorage поместилась. Теперь должно быть лучше.
Из конфигураций на ум приходят только циклические КА и их подвиды, где определён строгий порядок перехода, вроде a → b → … → n → a. Рассмотрим на следующих неделях.
Много подобных можно найти в частных вариантах. Например, при моделировании лесных пожаров огонь выполняет роль хищника, а деревья – жертвы. Со своими дополнениями и вариациями.
На КА с двумя состояниями тоже можно добавить псевдопоколения для дополнительных цветов, но это уже надстройка, не был уверен, насколько такие демонстрации будут "чистыми". На следующих неделях можно будет и для них добавить.
Добавили новые состояния клеток – семена и ростки. У всех свои шансы на сгорание (с потолка), в зависимости от количества огня вокруг. Деревья разбрасывают семена, аналогично распространению огня, но в зависимости от количества взрослых деревьев вокруг, с шансом 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: эх, при редактировании номера строк видны, а при прочтении нет :с
Оригинал изначально тяжеловато написан. Надеюсь, не усложнил его дополнительно)
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}, где всего по одному коду.
Справедливо, если это система, в которой будут декады в значении отдельных, системообразующих промежутков. Добавлять их к неделям – избыточно, и привносит дополнительную путаницу. Заменять ими недели – лично я не считаю хорошим решением.
Уже второй раз в этой ветке неявно вспоминается французский революционный календарь)
Ох, это значительно проще. Спасибо, обновил.
Крайне досадно вышло.
Пересобрал анимации, где-то подрезал, где-то цвета убрал. Вроде на качестве не сильно сказалось, зато теперь бо́льшая часть на habrastorage поместилась. Теперь должно быть лучше.
Да, если задать нечётный радиус, этот эффект пропадёт. Спасибо за дополнение.
R3/T3/G6/NNR
Из конфигураций на ум приходят только циклические КА и их подвиды, где определён строгий порядок перехода, вроде a → b → … → n → a. Рассмотрим на следующих неделях.
Много подобных можно найти в частных вариантах. Например, при моделировании лесных пожаров огонь выполняет роль хищника, а деревья – жертвы. Со своими дополнениями и вариациями.
Как раз на прошлой неделе смотрели на цветные орнаменты)
На КА с двумя состояниями тоже можно добавить псевдопоколения для дополнительных цветов, но это уже надстройка, не был уверен, насколько такие демонстрации будут "чистыми". На следующих неделях можно будет и для них добавить.
До особых правил пока далековато, да и на Хабре уже есть соответствующая статья. Присмотрюсь.
У меня пока нет. Может на следующих неделях, но загадывать не буду.
Python 3.11, matplotlib 3.6.3
Лаадно
На выгоревшей земле (серый) 5 ходов ничего не растёт
Значения из прошлого комментария, с припиской "выглядит хаотично. С ними ещё нужно играться"
Окей
Добавили новые состояния клеток – семена и ростки. У всех свои шансы на сгорание (с потолка), в зависимости от количества огня вокруг. Деревья разбрасывают семена, аналогично распространению огня, но в зависимости от количества взрослых деревьев вокруг, с шансом 0.0001 появления семечка из ничего. Семечки (коричневые) за 1 шаг превращаются в ростки, ростки (светло-зелёные) – в деревья. Модель без ветра, с водой.
Выглядит хаотично, надо со значениями играться.
Опустим шутку про рост деревьев в реальном времени.
Модель без ветра и воды; вероятность появления нового дерева в два раза ниже вероятности первичного возгорания; оставим низкую влажность (0.8+0.03*x), при высокой совсем наглядность пропадёт; начало с 60% леса:
Можно считать, что мы добавили характеристику повышенной влажности. Пожалуйста)
Жаль, тут в блоках кода нельзя вручную выделять цветом/жиром.
Изменения:
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: эх, при редактировании номера строк видны, а при прочтении нет :с
Всё так) Это одна из вариаций клеточного автомата, с ней не будет надобности в этом дополнении
Будет публичный рейтинг или это только для внутреннего использования?
Оригинал изначально тяжеловато написан. Надеюсь, не усложнил его дополнительно)
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}
, где всего по одному коду.Справедливо, если это система, в которой будут декады в значении отдельных, системообразующих промежутков. Добавлять их к неделям – избыточно, и привносит дополнительную путаницу. Заменять ими недели – лично я не считаю хорошим решением.
Уже второй раз в этой ветке неявно вспоминается французский революционный календарь)