Connecting Nearest Neighborモデルを用いたグラフの生成

Connecting Nearest Neighborモデルとは,スケールフリー性,クラスタ性,スモールワールド性を満たすソーシャルネットワーク的なグラフを生成するアルゴリズムの一つです.何かしらのシミュレーションなどに使えるのではないかと思います.


アルゴリズムは以下を繰り返すだけとなります.

  1. 1-pの確率で新たなvertexを作成.ランダムに既存のvertexを一つ選ぶ.新たなvertexと選ばれた既存のvertexを結びつける.
  2. pの確率で,ランダムに選んだpotential edge一つを本物のedgeへと変換する.


ちなみに,potential edgeとは次のような,潜在的なedgeです.

例えば,以下のようなグラフがあった場合のpotential edgeは,1と3を結ぶedgeとなります.

  2
 / \
1   3

また,以下のようなグラフであった場合は,1-5,1-3,5-3,2-4を結ぶedgeがpotential edgeとなります.

    4
    |
1-2-3
  |
  5

2hopを1hopに変えるようなedgeがpotential edgeであるとも言えます.


pythonでのプログラムは以下のようになります.

#!/usr/bin/env python
# this program generates social netwrok graphs by connecting nearest-neighbor
#
# see Alexei Vazquez, "Growing network with local rules: Preferential
# attachment, clustering hierarchy, and degree correlations", Physics Review
# E 67, 2003.

import random


class node:
    def __init__(self, id):
        self.link = []
        self.id = id

class cnn:
    def _initialize(self):
        random.seed()

        self.nodes = {}
        self.nodes[0] = node(0)
        self.nodes[1] = node(1)
        self.nodes[2] = node(2)

        self.nodes[0].link = [self.nodes[2]]
        self.nodes[1].link = [self.nodes[2]]

        self.nodes[2].link = [self.nodes[0]]
        self.nodes[2].link = [self.nodes[1]]

        self.neighbor = [(0, 1)]

        self.n = 2


    def generate(self, step, p):
        self._initialize()

        for var in range(step):
            rnd = random.random()
            if rnd < p:
                self._addLink()
            else:
                self._addNode()


    def _addNode(self):
        self.n += 1

        self.nodes[self.n] = node(self.n)

        rnd = int(random.uniform(0, self.n - 1))

        self.neighbor += [(link.id, self.n) for link in self.nodes[rnd].link]

        self.nodes[self.n].link += [self.nodes[rnd]]
        self.nodes[rnd].link    += [self.nodes[self.n]]


    def _addLink(self):
        if len(self.neighbor) == 0:
            return

        rnd = int(random.uniform(0, len(self.neighbor)))
        id1, id2 = self.neighbor[rnd]
        del self.neighbor[rnd]
        
        self.nodes[id1].link += [self.nodes[id2]]
        self.nodes[id2].link += [self.nodes[id1]]

    def printLink(self):
        for n in range(self.n):
            if len(self.nodes[n].link) == 0:
                continue

            for link in self.nodes[n].link:
                if n < link.id:
                    print '(%d, %d)' % (n, link.id)

    # draw with NodeBox
    # see http://nodebox.net/code/index.php/Graph
    def draw(self, width, height):
        try:
            graph = ximport("graph")
        except ImportError:
            graph = ximport("__init__")
            reload(graph)

        size(width, height)
        g = graph.create()

        for n in range(self.n):
            if len(self.nodes[n].link) == 0:
                continue

            for link in self.nodes[n].link:
                if n < link.id:
                    g.add_edge(n, link.id)

        g.solve()
        g.styles.apply()
        g.draw(directed = False, traffic = 1)


if __name__ == "__main__":
    graph = cnn()
    graph.generate(1000, 0.666)

    graph.printLink()

"""
graph = cnn()
graph.generate(1000, 0.666)

graph.printLink()

graph.draw(1440, 900)
"""


NodeBoxhttp://nodebox.net/code/index.php/Graph)を用いてvisualizeしてみたところ,以下のようになりました.



ちなみに,100回ループでp = 0.666とした場合の出力は以下のようになります.

(0, 2)
(0, 1)
(0, 3)
(0, 5)
(0, 9)
(0, 10)
(0, 11)
(0, 7)
(0, 12)
(0, 18)
(1, 2)
(1, 3)
(1, 5)
(1, 6)
(1, 7)
(1, 12)
(1, 11)
(1, 8)
(1, 10)
(1, 4)
(1, 16)
(1, 20)
(1, 17)
(1, 9)
(1, 34)
(2, 4)
(2, 3)
(2, 10)
(2, 12)
(2, 7)
(2, 16)
(2, 5)
(2, 20)
(2, 11)
(2, 25)
(2, 26)
(3, 6)
(3, 11)
(3, 12)
(3, 7)
(3, 5)
(3, 10)
(3, 14)
(3, 19)
(3, 22)
(3, 26)
(3, 34)
(3, 20)
(3, 37)
(4, 17)
(4, 20)
(5, 8)
(5, 9)
(5, 13)
(5, 7)
(5, 10)
(5, 26)
(6, 12)
(6, 11)
(6, 14)
(6, 7)
(7, 22)
(7, 16)
(8, 9)
(8, 33)
(9, 13)
(9, 18)
(9, 15)
(9, 10)
(9, 26)
(10, 16)
(10, 20)
(10, 26)
(10, 31)
(11, 12)
(11, 14)
(11, 19)
(12, 14)
(12, 16)
(13, 15)
(13, 21)
(13, 28)
(14, 19)
(14, 27)
(15, 23)
(15, 24)
(15, 28)
(16, 25)
(16, 20)
(19, 27)
(20, 25)
(20, 31)
(20, 30)
(23, 24)
(23, 35)
(24, 29)
(24, 28)
(25, 30)
(27, 32)
(31, 38)
(34, 36)