最小割Karger算法

问题描述

我正在尝试实施Krager's Min。 python中的cut算法可解决以下问题。这个问题来自斯坦福大学的edx课程“算法:设计和分析,第1部分”

文件包含一个简单的无向图的邻接列表表示。有200个标记为1到200的顶点。文件的第一列表示顶点标签,特定的行(第一列以外的其他条目)告诉该顶点与之相邻的所有顶点。因此,例如,第六行看起来像:“ 6 155 56 52 120 ......”。这只是意味着带有标签6的顶点与带有标签155,56,52,120,......等的顶点相邻(即与之共享一条边)

针对最小切割问题运行随机收缩算法,并将其用于上图以计算最小切割。

它涉及以下邻接表:https://pastebin.pl/view/39b087f8

这是我在python中编写的代码-我知道实现是幼稚的,但是我只是想在优化之前弄清楚它。

import random
import copy
with open('Contraction hwk.txt') as f:  #Please input the correct file path
    proto = f.readlines()
proto2 = [(x.replace('\t',' ').strip('\n')).split() for x in proto]
adjl = [[int(x) for x in i] for i in proto2]

solnset = []
for x in range(1000):#Repeated a 100 times cause the algo is not always correct
    wadjl = copy.deepcopy(adjl)
    nodes = list(range(1,len(adjl)+1))
    while len(nodes) > 2:
        randnodeval = random.choice(nodes)  #Select a random node
        randnode = wadjl[randnodeval-1] #Assign the random node to a var --> randnode
        randedgeval = random.choice(randnode[1:]) # Picks another random node(edge node) that is adjacent to the initial this will contract with the original random node
        if randedgeval == randnodeval:
            continue
        for node in wadjl[randedgeval-1][1:]:#This loop will go to all nodes adjacent to the edge node and replace the value of the edge node with the random node
            if node == randnodeval:#If the node is the same as the random node it removes the node as an adjacent node (Deletion of edge node from random node)
                modnode = wadjl[node-1][1:]
                edgevalnode = modnode.index(randedgeval)
                wadjl[node-1].pop(edgevalnode+1)
                continue
            modnode = wadjl[node-1][1:]
            edgevalnode = modnode.index(randedgeval)
            wadjl[node-1][edgevalnode+1] = randnodeval 
        randnodeidx = wadjl[randedgeval-1][1:].index(randnodeval)#This yeilds the index of the random node in the adjaceny list of the edge node 
        wadjl[randedgeval-1].pop(randnodeidx+1)#The random node is removed from that list(Deletion of random node from edgenode)
        randnode.extend(wadjl[randedgeval-1][1:])#The adjacency list of the edge node is merged with that of the random node
        del wadjl[randedgeval-1][1:]#The edge node is deleted
        try:#repeates/edges to itself are removed from the random node
            repeats = [i for i,x in enumerate(randnode) if x == randnodeval]
            for repeate in repeats:
                randnode.pop(repeat)
        except:
            pass
        #Edge node is removed from the nodes list
        noderemove = nodes.index(randedgeval) 
        nodes.pop(noderemove)
    for entry in wadjl:
        if len(entry) > 1:
            minc = len(entry) - 1 #edges in min cut case
            solnset.append(minc) #append solution to solution set
            break
# print(solnset)
print(min(solnset))

基于一些互联网搜索,答案似乎是17。我得到的答案是20,而且由于解决方案集太多,我认为算法的实现不正确。我相信,如果实施正确,该解决方案应该会经常出现在这个集合中。

解决方法

您发布的代码不会随机选择一致的边,除非收缩的图碰巧具有一致的度数(几乎总是这样)。通过选择一个随机节点,然后选择该节点的随机邻居,它将使邻居少的节点超重,这将导致最佳切割边缘比其应有的收缩更多,从而产生更大的最终切割。

理论上,Karger算法还需要进行Θ(n选择2)次迭代才能以恒定的概率成功,并且n select 2在这里是200(200-1)/ 2 = 19,900,但是该图并不接近最坏的情况,因为大多数情况下100次迭代似乎绰绰有余。

这是我的实现方式

import fileinput
import random


def find(parents,i):
    r = i
    while r in parents:
        r = parents[r]
    while i in parents:
        p = parents[i]
        parents[i] = r
        i = p
    return i


def unite(parents,i,j):
    parents[i] = j


def karger(n,edges):
    edges = list(edges)
    random.shuffle(edges)
    parents = {}
    for i,j in edges:
        if n <= 2:
            break
        i = find(parents,i)
        j = find(parents,j)
        if i == j:
            continue
        unite(parents,j)
        n -= 1
    return sum(find(parents,i) != find(parents,j) for (i,j) in edges)


def main():
    lines = list(fileinput.input())
    n = len(lines)
    edges = set()
    for line in lines:
        fields = iter(map(int,line.split()))
        u = next(fields)
        edges.update((min(u,v),max(u,v)) for v in fields)
    print(min(karger(n,edges) for k in range(1000)))


if __name__ == "__main__":
    main()

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...