优先连接算法的多种实现(Python)

来源:互联网 发布:unity3d 物体淡入淡出 编辑:程序博客网 时间:2024/05/18 21:43

优先连接算法介绍

优先连接算法是复杂网络中的一种经典算法。所谓优先连接,就是新的顶点在加入网络的过程中,以正比于网络中顶点的度的概率去连接顶点。在复杂网络演化中运用优先连接,得到的网络的度分布具有幂律分布的特性。

算法1:以正比于顶点度的概率连接顶点

以正比于顶点度的概率连接顶点的算法是最普通的算法。由于顶点的度肯定是整数,所以有一种简单的方法可以在O(1)的时间内完成一次优先连接选择。
设有一组顶点,序号为1,2,3,….,n,顶点的度存储在数组degrees里,顶点k的度就是degrees[k].

算法描述:

  1. 生成一个数组choose_array,初始化不含任何元素。
  2. 遍历数组degrees,对每个遍历到的顶点索引值k,它度数是degrees[k],那么就在choose_array中加入degrees[k]个k。
  3. 生成一个在0-len(choose_array)-1之间的随机整数random_number,choose_array[random_number]就是所选择的顶点。

算法的原理就是顶点索引在choose_array中出现的次数等于顶点的度。这样,顶点度多大,在choose_array中出现次数越多,被选中概率越大。

from random import randintdef construct_choose_array(degrees):    choose_array = []    length = len(degrees)    for index in range(1, length):        degree = degrees[index]        if degree != 0:            repeat_index_array = [index] * degree            choose_array += repeat_index_array    return choose_arraydef prefer_attach_proportional_to_degree(choose_array):    length = len(choose_array)    random_number = randint(0, length - 1)    return choose_array[random_number]

当某个顶点的度增加时,只需要在choose_array数组最后增加这个顶点的索引值即可。顶点在choose_array数组中出现的位置与被选择的概率无关。

算法2:以正比于顶点吸引力的概率连接顶点

算法1虽然能在O(1)时间内完成一次优先连接选择,但是是以空间换时间的方式实现的,空间消耗很大。并且,通常情况下我们并不是以完全正比于顶点度的概率去选择的,而是每个顶点计算一个吸引力(power),以正比于顶点power的概率去选择。顶点的power不一定是整数,如果要采用算法1,就必须把所有顶点的power转化成整数。但是这会导致数据的失真,或者所占空间巨大。
我们需要一种新的算法来解决这个问题。其实以正比于顶点吸引力的概率连接顶点就相当于遗传算法中的以正比于个体适应度去选择个体。而遗传算法采用转轮盘赌法去解决这个问题。下面介绍转轮盘赌法。

设有一组顶点,序号为1,2,3,….,n,顶点的power存储在数组powers里,顶点k的power就是powers[k],且powers[k]大于0
算法描述:

  1. 生成一个数组choose_array,初始化为[0]。
  2. 遍历数组powers,对每个遍历到的顶点索引值k,choose_array[k]=powers[k]+
    choose_array[k-1]
  3. 生成一个在0-choose_array[-1]之间的随机数random_number,用二分搜索找到random_number落在区间(choose_array[x-1],choose_array[x]]中,那么就是选中了顶点x。

算法的原理就是转轮盘赌法。

from random import uniformdef construct_choose_array(powers):    choose_array = [0]    length = len(powers)    for index in range(1, length):        choose_array.append(choose_array[index - 1] + powers[index])    return choose_arraydef binary_search(choose_array, value):    if value < choose_array[0] or value > choose_array[-1]:        print('not in this array')        return    start = 1    end = len(choose_array) - 1    while True:        middle = int((start + end) / 2)        if value <= choose_array[middle]:            if value > choose_array[middle - 1]:                return middle            else:                end = middle - 1        else:            start = middle + 1def prefer_attach_proportional_to_power(choose_array):    random_number = uniform(0, choose_array[-1])    return binary_search(choose_array, random_number)

记顶点个数为n,算法占用空间复杂度是O(n),算法时间复杂度是O(logn)。

算法3:有附加情况下的轮盘赌法

在使用优先连接时,通常情况下,我们是构造一次choose_array,choose_array保持不变,然后进行多次优先连接选择。也就是顶点的吸引力与做优先连接选择的顶点无关。但是在有些情况下,顶点的吸引力与做优先连接的顶点有关,比例在合作网络演化中,一个作者在选择他的合作者的时候,会优先考虑之前与他有过合作关系的作者。对于不同的作者,同一个作者对他们的吸引力不同。但只有一小部分顶点的吸引力会改变,其他的不会改变,如果还采用算法2,对于每一次优先连接选择,就必须构造一次choose_array,效率很低。下面将采用一种比较简单的等价技巧来解决这个问题。

设有一组顶点,序号为1,2,3,….,n,顶点的power存储在数组powers里。顶点k的power就是powers[k],且powers[k]大于0。做优先连接的顶点为i。对于顶点i,附加的吸引力在字典additional_powers里,键是顶点索引,值是附加的吸引力。例如顶点k出现在additional_powers里,那么顶点k对顶点i的吸引力就是powers[k]+additional_powers[k]。additional_powers的大小远小于powers的大小。

算法描述:

  1. 生成一个数组choose_array,初始化为[0]。
  2. 遍历数组powers,对每个遍历到的顶点索引值k,在choose_array末尾加上元素 powers[k]+choose_array[k-1] 。
  3. 对于做优先连接的顶点i,生成additional_powers。
  4. 记录现有choose_array的长度为length。遍历additional_powers,对每个遍历到的顶点索引值k,choose_array末尾加上choose_array[-1]+additional_powers[k],并用字典temp_dict记录附加顶点在choose_array中的位置信息。
  5. 生成一个在0-choose_array[-1]之间的随机数random_number,用二分搜索找到random_number落在区间(choose_array[x-1],choose_array[x]]中,如果x<=length-1,那么就是选中了顶点x。否则,再根据temp_dict去找对应的顶点。
  6. 删除choose中索引大于length的元素,恢复choose_array。

    算法就是利用了顶点被选中的概率只与顶点的总吸引力有关,而与吸引力被分成多少部分,分布在数组choose_array的哪些地方无关的性质,达到等价选择的效果。

from random import uniformdef construct_choose_array(powers): #与算法二一样    choose_array = [0]    length = len(powers)    for index in range(1, length):        choose_array.append(choose_array[index - 1] + powers[index])    return choose_arraydef binary_search(choose_array, value): #与算法2一样    if value < choose_array[0] or value > choose_array[-1]:        print('not in this array')        return    start = 1    end = len(choose_array) - 1    while True:        middle = int((start + end) / 2)        if value <= choose_array[middle]:            if value > choose_array[middle - 1]:                return middle            else:                end = middle - 1        else:            start = middle + 1def prefer_attach_proportional_to_power_additional(choose_array, additional_powers):    random_number = uniform(0, choose_array[-1])    return additional_binary_search(choose_array, additional_powers, random_number)def additional_binary_search(choose_array, additional_powers, random_number):    temp_dict = {}    index = len(choose_array)    short_end = index    temp_total = choose_array[-1]    for key in additional_powers:        temp_total += additional_powers[key]        choose_array.append(temp_total)        temp_dict[index] = key        index += 1    print(choose_array)    position = binary_search(choose_array, random_number)    if position >= short_end:        position = temp_dict[position]    end = len(choose_array)    for i in range(short_end, end):        del choose_array[-1]    return position

这样,也只需要生成一次choose_array,对于不同的做优先连接选择的顶点,只需要变动一小部分,大大降低了时间复杂度。

第一次写博客,写得不好还请见谅~有任何问题或者讨论,请留言回复~

1 0
原创粉丝点击