Python大作业耦合网络信息传播

Posted Dreamchaser追梦

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Python大作业耦合网络信息传播相关的知识,希望对你有一定的参考价值。

文章目录


前言

说实话,已经好久没写博文了,上篇博文的时间都是两个月前。但在这种情况下,粉丝蹭蹭往上涨,hh,这属实是我没想到的,让我着实有种愧疚感。
当然最近没写博文,除了找不到合适的题材外,最重要的是最近有亿点点忙(上课、实验报告、健身、做项目、开会…)剩下的时间都是零零散散,没办法静下心来写东西。

这次主要是趁着要写实验报告,所以顺手将实验报告的思路改写成博文,而作业题目也挺有意思的,是耦合网络信息传播,可以模拟病毒扩散或者信息的扩散,听着是不是有种高大上的感觉?所以我把它写作博文供大家学习参考。(又水了一篇~)

注:本博文代码仅供学习交流,谢绝抄袭!


一、题目介绍

作业题目是一个PPT文件,上来就是一个大标题

第二页就是解题思路(说实话有些突兀,我连题目都不知道是啥就开始叫我一步步做下去,属实有点懵了,这里最好加一个具体的问题,比如用Python编写程序模拟耦合网络的信息传播)







以上便是题目的PPT内容。

二、解题思路

题目内容大致是要我们编写程序模拟耦合网络的信息传播。

解题思路PPT文件其实已经告诉我们了,我们只需按照PPT的步骤一步步进行代码复现即可。

1.构造一个耦合网络

按照PPT的意思,应该是要我们构造一个ER-BA双层网络。

①构造ER网络

生成ER随机网络(算法):
初始: 网络总结点数 N, 没有边;
网络构建过程:每对节点以概率 p 被选择,进行连边,不允许重复连边。

我这里用邻接矩阵(对于Python就是一个二维列表)来存储网络,ER[i][j]==1就说明i和j节点之间有连线,反之无连线。这里用一个全局二维数组变量ER来存储。

根据生成的规则我们编写如下代码来生成ER随机网络(注意这里方法的封装,把这些可变的概率、节点数当做入参,以便后续改变参数),这里我们利用random.random()方法来模拟概率,当随机生成的数字小于概率就生成该边。

# ER随机网络
ER = [[]]
# 生成ER随机网络,n表示点个数,p表示生成边的概率
def generateER(n, p):
    global ER
    # 这种方式声明可以避免浅拷贝问题
    ER = [([0] * n) for i in range(n)]
    for i in range(0, n):
        for j in range(i + 1, n):
            # 随机生成的数
            x = random.random()
            # 概率生成边
            if x < p:
                ER[i][j] = 1
                ER[j][i] = 1

②构造BA网络

BA无标度网络

(1) 初始时网络有 m_0 个节点,可以没有边,也可以像ER网络一样生成一些边;也可以前m_0 个节点是全连通结构。 (2)
每个时间步,一个新节点加入这个网络,并从已存在的网络中选择 m个节点(m≤m_0) 与之相连,选择概率 p_i 与已存在的节点 i的度成正比 p_i=k_i/∑_j▒k_j

(3) 当网络中存在 N 个节点后,停止。

同样BA无标度网络也是以邻接矩阵来存储,这里按照给定的规则进行编码。
考虑到规则比较复杂,可以分为以下几个步骤:

  • 初始化BA网络,此时BA网络只有m0个初始节点,边关系随意
  • 迭代增加m个节点,每增加一个节点便去根据规则概率寻找m个节点

上述逻辑实现有些复杂,这里我们简化条件来实现题目要求的效果:

  • 假设每次增加节点时至少有m个度不为0的节点(因为如果没有m个度不为0的节点,这就意味着我们需要考虑度为0的情况)
  • 初始m0个节点组成的网络为全连通网络

由于每次增加节点时选取m个节点操作过于复杂,所以我将其封装为一个方法来增加代码可读性。

以下是代码实现:

# BA无标度网络
BA = [[]]
# 在下标0到end的klist列表中根据k的权重来寻找一个下标返回,不包括exclude_list中的节点
def findOne(klist, end, exclude_list):
    flag = True
    k_sum = 0
    for i in range(0, end):
        k_sum += klist[i]
    result = end - 1
    while flag:
        x = random.random() * k_sum
        pre = 0
        for i in range(0, end):
            pre += klist[i]
            if pre > x:
                result = i
                break
        flag = False
        # 检查结果是否在exclude_list中,如果是则重来,否则返回结果
        for i in exclude_list:
            if i == result:
                flag = True
                break
    return result


# 根据BA无标度网络的规则在klist(下标为0到end 之间)中寻找m个数
# 为了简化逻辑,这里参与竞争(k!=0)的节点数必定大于m
def find(k_list, m, end):
    # 结果数组
    result = []
    # 初始化
    for i in range(0, m):
        j = findOne(k_list, end, result)
        # 增加结果
        result.append(j)
    return result


# 生成BA无标度网络,n表示点个数,m0表示初始节点数
def generateBA(n, m0, m):
    global BA
    # 初始BA无标度网络(利用生成器创建避免浅拷贝)
    BA = [([0] * n) for i in range(n)]
    # 初始化前m0个节点都为连通网络
    for i in range(0, m0):
        for j in range(i + 1, m0):
            BA[i][j] = 1
            BA[j][i] = 1
    # 初始度数组
    k_list = [m0 - 1] * m0 + [0] * (n - m)
    # 遍历后面的节点,模拟加入
    for i in range(m0, n):
        # 选出m个节点
        nodes = find(k_list, m, i)
        for j in nodes:
            BA[i][j] = 1
            BA[j][i] = 1
            # 更新度数组
            k_list[i] += 1
            k_list[j] += 1

③双层ER-BA网络模型

构建出双层网络结构(如ER-ER网卡,ER-BA网络,BA-BA网络),假设层间节点一对一连接,如下图所示。可以理解不同社交平台相互耦合。


这里不需要代码操作,但是我们需要理解,ER节点和BA节点是一一对应的,就像上图展示的那样。
在代码中可以理解为ER网络中下标为i的节点和BA网络中下标为i的节点实际上是连通的。

2.利用SIR模型来模拟信息传播

SIR (Susceptible – Infected - Recoved) model:

SIR传播模型思路:

如果一个 S (健康或者没有接收到信息)状态节点 与一个I (感染或信息传播者)状态节点接触,那么这个S状态的节点被感染的概率为 β .
因此,在t时刻,如果S状态节点有 m 个I状态邻居, 那么下个时间步 t + 1,该节点被感染的概率为 1−(1−β)^m。

t 时刻每个I状态节点在下一时间步,即 t + 1 康复的概率为 γ。

我们可以将上述思路转化为以下步骤:
1.初始一个感染点
2.迭代循环t模拟时间步,每次迭代,每个节点都会根据规则刷新状态

  • 如果节点状态为S(正常,未被感染),则它有1−(1−β)^m的概率被感染成I状态
  • 如果节点状态为I(被感染),则它有γ的概率康复转变为R状态
  • 如果节点状态为R,则状态不会改变(模拟病毒免疫)

3.记录每个时间点的S节点数、I节点数、R节点数

在这里需要注意的是,在考虑周围感染节点时,别忘了考虑ER-BA网络“上下层”的感染情况,因为这里ER网络和BA网络对应的节点时连通的。

这里写代码时也要考虑到入参,同时对于复杂的逻辑进行另外的封装以实现良好的代码可读性,以下是代码实现:

# ER网络感染的情况,S表示为健康,I表示已感染,R表示已康复
ER_SIR = []

BA_SIR = []

# 用于记录每日数据的数组
slist = []
ilist = []
rlist = []
# 对ER网络的i节点进行处理,i表示节点下标,t表示天数,b表示感染系数,y表示康复系数
def er_sir_one(i, t, b, y):
    global ER, BA, ER_SIR, BA_SIR, slist, ilist, rlist
    if ER_SIR[i] == 'S':
        # 开始统计周围节点感染的数量
        inum = 0
        for x in range(len(ER_SIR)):
            if (not x == i) and ER[i][x] == 1 and ER_SIR[x] == 'I':
                inum += 1
        # 因为是双层网络,所以BA也要考虑
        if BA_SIR[i] == 'I':
            inum += 1
            ilist[t] += 1
        p = random.random()
        # 有1-(1-b)^inum概率感染
        if p < 1 - (1 - b) ** inum:
            ER_SIR[i] = 'I'
            ilist[t] += 1
            return
        slist[t] += 1
    elif ER_SIR[i] == 'I':
        p = random.random()
        # 有y的几率康复
        if p < y:
            ER_SIR[i] = 'R'
            rlist[t] += 1
            return
        ilist[t] += 1
    else:
        rlist[t] += 1


# 对BA网络的i节点进行处理,i表示节点下标,t表示天数,b表示感染系数,y表示康复系数
def ba_sir_one(i, t, b, y):
    global ER, BA, ER_SIR, BA_SIR, slist, ilist, rlist
    if BA_SIR[i] == 'S':
        # 开始统计周围节点感染的数量
        inum = 0
        for x in range(len(BA_SIR)):
            if (not x == i) and BA[i][x] == 1 and BA_SIR[x] == 'I':
                inum += 1

        # 因为是双层网络,所以ER也要考虑
        if ER_SIR[i] == 'I':
            inum += 1
        p = random.random()
        # 有1-(1-b)^inum概率感染
        if p < 1 - (1 - b) ** inum:
            BA_SIR[i] = 'I'
            ilist[t] += 1
            return
        slist[t] += 1
    # 有y的几率康复
    elif BA_SIR[i] == 'I':
        p = random.random()
        if p < y:
            BA_SIR[i] = 'R'
            rlist[t] += 1
            return
        ilist[t] += 1
    else:
        rlist[t]+=1

def sir(b, y, t):
    global ER_SIR, BA_SIR, slist, ilist, rlist
    n = len(ER[0])
    # 初始化ER_SIR,BA_SIR
    ER_SIR = ['S'] * n
    BA_SIR = ['S'] * n

    # 初始化每日统计数据的数组
    slist = [0] * t
    ilist = [0] * t
    rlist = [0] * t

    # 随机感染ER网络中的一个节点
    x = random.randint(0, n - 1)
    ER_SIR[x] = 'I'

    # 遍历t天,模拟过了t天
    for day in range(t):
        # 遍历所有节点
        for node in range(n):
            # 对双层网络状态进行一遍刷新
            er_sir_one(node, day, b, y)
            ba_sir_one(node, day, b, y)

3.画图

最后便是根据要求将结果以图表方式可视化展现,这里用了matplotlib这个包,以下是代码实现:

# 画图
plt.plot(list(range(t)), slist, linewidth=4,label=u'S')
plt.plot(list(range(t)), ilist, linewidth=4,label=u'I')
plt.plot(list(range(t)), rlist, linewidth=4,label=u'R')
plt.legend()  # 让图例生效
plt.xlabel(u"t")  # X轴标签
plt.ylabel("N(t)")  # Y轴标签
plt.title("SIR model result")  # 标题
plt.show()

三、完整代码

以下是完整代码实现:

import random
import matplotlib.pyplot as plt

# ER随机网络
ER = [[]],
# BA无标度网络
BA = [[]]

# ER网络感染的情况,S表示为健康,I表示已感染,R表示已康复
ER_SIR = []

BA_SIR = []

# 用于记录每日数据的数组
slist = []
ilist = []
rlist = []


# 生成ER随机网络,n表示点个数,p表示生成边的概率
def generateER(n, p):
    global ER
    # 避免浅拷贝
    ER = [([0] * n) for i in range(n)]
    for i in range(0, n):
        for j in range(i + 1, n):
            # 随机生成的数
            x = random.random()
            # 概率生成边
            if x < p:
                ER[i][j] = 1
                ER[j][i] = 1


# 在下标0到end的klist列表中根据k的权重来寻找一个下标返回,不包括exclude_list中的节点
def findOne(klist, end, exclude_list):
    flag = True
    k_sum = 0
    for i in range(0, end):
        k_sum += klist[i]
    result = end - 1
    while flag:
        x = random.random() * k_sum
        pre = 0
        for i in range(0, end):
            pre += klist[i]
            if pre > x:
                result = i
                break
        flag = False
        # 检查结果是否在exclude_list中,如果是则重来,否则返回结果
        for i in exclude_list:
            if i == result:
                flag = True
                break
    return result


# 根据BA无标度网络的规则在klist(下标为0到end 之间)中寻找m个数
# 为了简化逻辑,这里参与竞争(k!=0)的节点数必定大于m
def find(k_list, m, end):
    # 结果数组
    result = []
    # 初始化
    for i in range(0, m):
        j = findOne(k_list, end, result)
        # 增加结果
        result.append(j)
    return result


# 生成BA无标度网络,n表示点个数,m0表示初始节点数
def generateBA(n, m0, m):
    global BA
    # 初始BA无标度网络(利用生成器创建避免浅拷贝)
    BA = [([0] * n) for i in range(n)]
    # 初始化前m0个节点都为连通网络
    for i in range(0, m0):
        for j in range(i + 1, m0):
            BA[i][j] = 1
            BA[j][i] = 1
    # 初始度数组
    k_list = [m0 - 1] * m0 + [0] * (n - m)
    # 遍历后面的节点,模拟加入
    for i in range(m0, n):
        # 选出m个节点
        nodes = find(k_list, m, i)
        for j in nodes:
            BA[i][j] = 1
            BA[j][i] = 1
            # 更新度数组
            k_list[i] += 1
            k_list[j] += 1


# 对ER网络的i节点进行处理,i表示节点下标,t表示天数,b表示感染系数,y表示康复系数
def er_sir_one(i, t, b, y):
    global ER, BA, ER_SIR, BA_SIR, slist, ilist, rlist
    if ER_SIR[i] == 'S':
        # 开始统计周围节点感染的数量
        inum = 0
        for x in range(len(ER_SIR)):
            if (not x == i) and ER[i][x] == 1 and ER_SIR[x] == 'I':
                inum += 1
        # 因为是双层网络,所以BA也要考虑
        if BA_SIR[i] == 'I':
            inum += 1
            ilist[t] += 1
        p = random.random()
        # 有1-(1-b)^inum概率感染
        if p < 1 - (1 - b) ** inum:
            ER_SIR[i] = 'I'
            ilist[t] += 1
            return
        slist[t] += 1
    elif ER_SIR[i] == 'I':
        p = random.random()
        # 有y的几率康复
        if p < y:
            ER_SIR[i] = 'R'
            rlist[t] += 1
            return
        ilist[t] += 1
    else:
        rlist[t] += 1


# 对BA网络的i节点进行处理,i表示节点下标,t表示天数,b表示感染系数,y表示康复系数
def ba_sir_one(i, t, b, y):
    global ER, BA, ER_SIR, BA_SIR, slist, ilist, rlist
    if BA_SIR[i] == 'S':
        # 开始统计周围节点感染的数量
        inum = 0
        for x in range(len(BA_SIR)):
            if (not x == i) and BA[i][x] == 1 and BA_SIRPython大作业耦合网络信息传播

Python大作业耦合网络信息传播

影响信息传播的可计算因素分析

聚类系数可变无标度网络模型Holme-Kim HK模型

吴恩达机器学习作业——BP神经网络--Python实现

爬虫大作业