进行复杂网络相关研究的基础,就是要构建适当的网络模型。这次选择用Matlab构建一个WS小世界网络练练手。
首先,为了方便数据处理,网络模型我们用邻接矩阵的表示,这样虽然看上去不太直观,但是对于矩阵运算是很友好的。
小世界网络分为两种:NW小世界网络和WS小世界网络。它们的构造都基于一个初试的最近邻耦合网络A:也就是网络中每个节点都与左右相邻的各K/2个节点相连,其中参数K满足N>>K>>lnN>>1(N为网络节点数)。在此基础上,NW小世界网络通过对A进行随机化加边实现网络构造,而WS小世界网络则通过随机的边重连进行网络构造。
WS小世界网络的随机边重连方法也非常容易理解:以概率p对网络中每一条边进行重连,其中新诞生的边必须满足是原网络中没有的边,且节点不能与自己相连。
1、为了方便计算,在Matlab中我们用邻接矩阵A来表示网络,A的每一个元素A[m, n]表示节点m和节点n是否相连。
2、构造最近邻耦合网络。明显A是一个对称矩阵,因此我们只需要计算矩阵A的上三角部分(也就是只考虑每个节点与之相连的序号更大的节点),明显对于序号为m的节点来说,从m+1到min(m + K/2, N)的节点都是与它相连的。但是只考虑这部分节点是不全面的,因为对于N=10,K=6的网络来说,2号节点不仅要连接顺时针方向的3、4、5号,也要连接逆时针方向的9、10、1号节点,明显9和10号也属于需要计算的邻接矩阵上三角部分,因此对于节点m而言,满足(m-K/2+N)%N > m的节点也要被考虑在内。可能到这一步大家会很难想到如何去构造这样一个矩阵,但是如果你看到了一个已知最近邻耦合矩阵的样子,一定会豁然开朗。
上图就是N=16,K=8情况下的最近邻耦合矩阵A,很明显的看到:上三角部分刚好为1元素刚好可以构成主对角线往上的前K/2条对角线以及倒数K/2条对角线,因此我们可以直接在零矩阵的基础上直接加上这K条全1的对角线即可。最后,再将矩阵作对称处理。
A = sparse(N, N);
for i = 1 : K/2
A = A + diag(ones(1,N-i),i);
A = A + diag(ones(1, i), N-i);
end
A = A + A';
3、进行随机边重连。
为了后续对生成网络的进一步分析,这里采用不同的p进行随机重连以试验效果。对于一个确定的概率p,我们可以遍历所有的节点,再以概率p确定是否要将它作为待重连边的起点,当然我们也可以给N个节点分配(0, 1)的随机数,再以p作为分界一次性确定所有起点。
StartVs = find(rand(1, N) < p); %通过概率p在N个顶点中随机选择需要重连的边起点
确定了起点后,我们要在该起点所有存在的边中随机选择一条作为旧边,当然,对于邻接矩阵而言,我们要做的是选择该边对应的终点序号;选择好旧终点后,我们也需要确定一个新边对应的新终点,这个终点既不能是起点自身,也不能是与起点已经建立连接的点(因为不允许重复边的出现)。确定后,我们只需要对相应的邻接矩阵值进行更新即可。
值得注意的是,实际的操作中有几点需要注意:确定了起点不一定代表一定可以进行边重连,因为不能够保证起点对应的存在的边,也不一定有起点尚未连接的剩余终点,所以为了避免死循环,我们一定要给随机寻找的过程设置考虑临界条件。对于随机选点的过程,我这里采用在[1, N]取随机数的方法完成,因此为了避免死循环,我设置了两个count分别对新旧终点的找寻进行计数,一旦超过计数值,则默认为不存在满足条件的顶点,直接放弃这次边重连。(之所以可以放弃是因为小世界网络对边重连的次数微动不敏感,并不影响后续的研究)
% WS小世界网络
P = [0, 0.0001, 0.0005, 0.001, 0.005, 0.01, 0.05, 0.1, 0.25, 0.5, 0.75, 0.9, 1];
for p = 1 : length(P) %对每一个P进行随机边重连,以构造不同耦合程度的网络
This_A = A;
StartVs = find(rand(1, N) < p); %通过概率p在N个顶点中随机选择需要重连的边起点
for i = 1 : length(StartVs)
v1 = StartVs(i);
%随机找重连边的旧终点
v2_old = fix(rand * N + 1);
count_v2_old = N; %计数器,防止死循环
while v2_old == v1 || This_A(v1, v2_old) ~= 1
v2_old = fix(rand * N + 1);
count_v2_old = count_v2_old - 1;
if count_v2_old <= 0
break;
end
end
%随机找重连边的新终点
v2_new = fix(rand * N + 1);
count_v2_new = N; %计数器,防止死循环
while v2_new == v1 || This_A(v1, v2_new) ~= 0
v2_new = fix(rand * N + 1);
count_v2_new = count_v2_new - 1;
if count_v2_new <= 0
break;
end
end
%检查新旧终点是否符合条件,不符合则直接放弃本次重连
if v2_old == v1 || This_A(v1, v2_old) ~= 1 || v2_new == v1 || This_A(v1, v2_new) ~= 0
break;
end
%检查完毕,进行边重连
This_A(v1, v2_old) = 0;
This_A(v2_old, v1) = 0;
This_A(v1, v2_new) = 1;
This_A(v2_new, v1) = 1;
end
end
下面展示一下WS小世界网络边重连的效果。