Деградация связной структуры

Кролики и удавы
Вирусы и питоны

Всё, что один человек написал, другой может переврать!

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

Перейдем к делу, у нас есть узлы(может это и люди) связанные между собой. Узлы деградируют/болеют и их можно лечить, изолировать в карантине, ну и они отключаются, увы иногда навсегда.

# количество тиков времени до попадания в карантин.
# Или до выпадения из структуры компрометированного узла
# т.е. узел скомпрометирован/болен, но это еще не обнаружено и он несколько тиков
# времени продолжает заражать других
quarantine = 2

# если после стольких дней узел не исправлен - удаляем окончательно
dead = 14

#  вероятность деградации узла, заражения здорового, появления вируса и т.д. 
#  за один тик времени при общении с зараженным узлом вне карантина
infection_probably = 0.5

#  вероятность починки узла, выздоровления больного,
#  исчезновения вируса и т.д. за один тик времени
pr_recover = 0.5

#  число узлов/жителей
w_size = 1024 * 32

#  связность, пытаемся создать у каждого узла не более max_conn связей
max_conn = 6

import numpy as np
from tqdm import tqdm

map_conn = []

# храним тут состояние узла. =0 исправен, >0  но <quarantine то заразен
# и не изолирован, >quarantine то изолирован, >dead - отключен
# <0 - вылечен и обладает иммунитетом/патч применен
map_stat = np.zeros((w_size), dtype="int16")

# храним связную структуру в виде списка списков связности.
# Т.е. map_conn[i] - список с номерами узлов связанных с узлом i
for i in range(w_size):
    map_conn.append([])
    
for i in tqdm(range(w_size)):
    t = np.random.randint(0, max_conn//2)
    tt = np.random.randint(0, w_size, (t))
    for j in tt:
        map_conn[i].append(j)
        map_conn[j].append(i)

for i in range(w_size):
    map_conn[i] = list(set(map_conn[i]))    

# очевидно, что возможно и больше max_conn связей.
# Мы создаем случайно для каждого узла max_conn//2 связей,
# но связность симметрична.

# состояние узла. Если 0 то здоров, если <0 переболел, восстановлен и 
# не будет больше болеть
# если >0 то инфицирован/деградирует, если >quarantine, то в изоляции
# если >dead отключен окончательно, увы.
map_stat[:] = 0
# в начале все здоровы

map_stat[np.random.randint(1, w_size)] = 1
# случайный узел деградировал, первый день деградации.

day = 1

print("map num ", w_size)

while True:

    tmp_map_conn = map_conn.copy()
    tmp_map_stat = map_stat.copy()
# тут сохраняем структуру во временное хранилище и 
# начнем вычислять новое состояние

    for i in range(w_size):
# если узел был инфицирован и восстановлен.
# Болел - выздоровел/патч накатили - иммунитет
# состояние сохраняется
        if tmp_map_stat[i] < 0:
            map_stat[i] = tmp_map_stat[i]
            continue

# если узел был инфицирован и более чем quarantine тиков времени/дней
# увеличиваем счетчик тиков на 1
        if tmp_map_stat[i] >= quarantine:
            map_stat[i] = tmp_map_stat[i] + 1
            continue

# если узел был инфицирован и менее чем quarantine тиков времни/дней
# то он выздоравливает с вероятностью pr_recover
# или счетчик тиков деградации увеличивается на один
        if tmp_map_stat[i] > 0:
            if np.random.rand() < pr_recover:
                map_stat[i] = -tmp_map_stat[i]
            else:
                map_stat[i] = tmp_map_stat[i] + 1
# если узел в деградации больше quarantine тиков,
# то наверно это обнаружится и узел изолируют и начнут восстанавливать
# и все его связи исчезнут и связи с ним тоже
                if map_stat[i] >= quarantine:
                    for j in tmp_map_conn[i]:
                        if i in map_conn[j]:
                            map_conn[j].remove(i)
                    map_conn[i] = []
            continue

# если узел здоров, то в общении со своим контактом может
# заразиться с вероятностью infection_probably
        if tmp_map_stat[i] == 0:
            map_stat[i] = 0
            for j in tmp_map_conn[i]:
                t = np.random.rand()
                if (
                    t < infection_probably
                    and tmp_map_stat[j] > 0
                    and tmp_map_stat[j] < quarantine
                ):
                    map_stat[i] = 1
                    break

# считаем число элементов до 0, от 0 до quarantine
# от quarantine до dead 
    immun = np.count_nonzero(map_stat < 0)
    quar = np.count_nonzero((map_stat >= quarantine) & (map_stat < dead))
    dead_s = np.count_nonzero(map_stat >= dead)
    infl = np.sum((map_stat > 0) & (map_stat < dead))
    print(
        "day {0:3d} infl {1:6d} immun {2:6d} quarantine {3:6d} dead {4:6d}".format(
            day, infl, immun, quar, dead_s
        )
    )
# если больных нет, считать прекращаем
    if infl == 0:
        break
# и так далее
    day += 1

Тут самое время сказать — ну и что, что такого могут открыть десяток строк питона!
Мне же результат был интересен и полезен.

Получилось вот что (и был бы рад, если кто проверит и ткнет в ошибку): при infection_probably = 0.9 и max_conn = 4, т.е. при страшной заразности и при минимуме общения — 4 контакта в день — массовой деградации нет. Десяток запусков конечно не статистика, но мне достаточно.

А вот при infection_probably = 0.1 и max_conn = 40 погибает половина. Т.е. слабозаразная напасть при большой связности сообщества губит половину её членов. Причем от величины quarantine это не зависит.

Специально для сайта ITWORLD.UZ. Новость взята с сайта Хабр