python 2.7中的karger min cut算法

时间:2014-05-23 09:10:18

标签: python algorithm min

这是我的karger min cut算法的代码。据我所知,我实现的算法是正确的。但我没有得到正确答案。如果有人可以查看出了什么问题,我将不胜感激。

import random
from random import randint

#loading data from the text file#
with open('data.txt') as req_file:
    mincut_data = []
    for line in req_file:
        line = line.split()
        if line:
            line = [int(i) for i in line]
            mincut_data.append(line)

#extracting edges from the data #            
edgelist = []
nodelist = []
for every_list in mincut_data:
    nodelist.append(every_list[0])
    temp_list = []
    for temp in range(1,len(every_list)):
        temp_list = [every_list[0], every_list[temp]]
        flag = 0
        for ad in edgelist:
            if set(ad) == set(temp_list):
                flag = 1
        if flag == 0 :
            edgelist.append([every_list[0],every_list[temp]])


#karger min cut algorithm#
while(len(nodelist) > 2):
    val = randint(0,(len(edgelist)-1))
    print val
    target_edge = edgelist[val]
    replace_with = target_edge[0]
    should_replace = target_edge[1]
    for edge in edgelist:
        if(edge[0] == should_replace):
            edge[0] = replace_with
        if(edge[1] == should_replace):
            edge[1] = replace_with
    edgelist.remove(target_edge)
    nodelist.remove(should_replace)
    for edge in edgelist:
        if edge[0] == edge[1]:
            edgelist.remove(edge)

print ('edgelist remaining: ',edgelist)
print ('nodelist remaining: ',nodelist)

测试用例数据是:

1 2 3 4 7
2 1 3 4
3 1 2 4
4 1 2 3 5
5 4 6 7 8
6 5 7 8
7 1 5 6 8
8 5 6 7

请将其复制到文本文件中并保存为" data.txt"并运行程序

答案应该是: 最小割数是2和 切口位于边缘[(1,7),(4,5)]

6 个答案:

答案 0 :(得分:10)

此代码也有效。

import random, copy
data = open("***.txt","r")
G = {}
for line in data:
    lst = [int(s) for s in line.split()]
    G[lst[0]] = lst[1:]   

def choose_random_key(G):
    v1 = random.choice(list(G.keys()))
    v2 = random.choice(list(G[v1]))
    return v1, v2

def karger(G):
    length = []
    while len(G) > 2:
        v1, v2 = choose_random_key(G)
        G[v1].extend(G[v2])
        for x in G[v2]:
            G[x].remove(v2)
            G[x].append(v1) 
        while v1 in G[v1]:
            G[v1].remove(v1)
        del G[v2]
    for key in G.keys():
        length.append(len(G[key]))
    return length[0]

def operation(n):
    i = 0
    count = 10000   
    while i < n:
        data = copy.deepcopy(G)
        min_cut = karger(data)
        if min_cut < count:
            count = min_cut
        i = i + 1
    return count


print(operation(100))

答案 1 :(得分:9)

因此,Karger的算法是一种“随机算法”。也就是说,每次运行它都会产生一个绝不保证最佳的解决方案。一般的方法是运行很多次并保持最佳解决方案。对于许多配置,将会有许多最佳或近似最佳的解决方案,因此您可以尝试快速找到一个好的解决方案。

据我所知,您只运行一次算法。因此,解决方案不太可能是最佳解决方案。尝试在for循环中运行100次并保持最佳解决方案。

答案 2 :(得分:2)

如菲尔所说,我必须运行我的程序100次。代码中还有一个更正是:

for edge in edgelist:
        if edge[0] == edge[1]:
            edgelist.remove(edge)

这部分代码没有正确消除自循环。所以我不得不改变代码:

for i in range((len(edgelist)-1),-1,-1):
        if edgelist[i][0] == edgelist[i][1]:
            edgelist.remove(edgelist[i])

这条线不需要。因为目标节点会自动更改为自循环,并且它将被删除。

edgelist.remove(target_edge)

然后如前所述,程序循环了100次,我通过随机化获得了最小的削减。 :)

答案 3 :(得分:2)

在看这篇文章的答案时,我偶然发现了乔尔的评论。根据Karger的算法,必须随机均匀地选择边缘。你可以找到我的实施,这是基于奥斯卡的答案和乔尔的评论如下:

class KargerMinCutter:
def __init__(self, graph_file):
    self._graph = {}
    self._total_edges = 0
    with open(graph_file) as file:
        for index, line in enumerate(file):
            numbers = [int(number) for number in line.split()]
            self._graph[numbers[0]] = numbers[1:]
            self._total_edges += len(numbers[1:])

def find_min_cut(self):
    min_cut = 0
    while len(self._graph) > 2:
        v1, v2 = self._pick_random_edge()
        self._total_edges -= len(self._graph[v1])
        self._total_edges -= len(self._graph[v2])
        self._graph[v1].extend(self._graph[v2])
        for vertex in self._graph[v2]:
            self._graph[vertex].remove(v2)
            self._graph[vertex].append(v1)
        self._graph[v1] = list(filter(lambda v: v != v1, self._graph[v1]))
        self._total_edges += len(self._graph[v1])
        self._graph.pop(v2)
    for edges in self._graph.values():
        min_cut = len(edges)
    return min_cut

def _pick_random_edge(self):
    rand_edge = randint(0, self._total_edges - 1)
    for vertex, vertex_edges in self._graph.items():
        if len(vertex_edges) <= rand_edge:
            rand_edge -= len(vertex_edges)
        else:
            from_vertex = vertex
            to_vertex = vertex_edges[rand_edge]
            return from_vertex, to_vertex

答案 4 :(得分:1)

请注意,我的回复是在Python3中,因为这篇文章上次收到回复已有几年了。

进一步重复@ sestus&#39;上面有用的答案,我想解决三个特点:

  1. 在类KarmgerMinCut()的_pick_random_edge方法中,rand_edge最终将匹配vertex_edges的长度。我调整了代码以从rand_edge中减去1,因此rand_edge不会尝试在大于数组长度的位置1处获取元素。
  2. 了解哪些顶点包含代表最小切割的两个子组。在&#34; supervertices&#34;上实现的功能dict实现了这个目标。
  3. 多次运行此算法(在我的情况下,100次),并跟踪最小的min_cut及其相关的supervertices。这就是我的外部函数full_karger()实现的目标。我不够聪明,不能将其作为内部实现

    from random import randint
    from math import log
    
    class KargerMinCut():
    
        # 0: Initialize graph
        def __init__(self, graph_file):
    
            # 0.1: Load graph file
            self.graph = {}
            self.total_edges = 0
            self.vertex_count = 0
            with open(graph_file, "r") as file:
                for line in file:
                    numbers = [int(x) for x in line.split('\t') if x!='\n']
                    vertex = numbers[0]
                    vertex_edges = numbers[1:]
                    self.graph[vertex] = vertex_edges
                    self.total_edges+=len(vertex_edges)
                    self.vertex_count+=1            
            self.supervertices = {}
            for key in self.graph:
                self.supervertices[key] = [key]
    
        # 1: Find the minimum cut
        def find_min_cut(self):
            min_cut = 0
            while len(self.graph)>2:
                # 1.1: Pick a random edge
                v1, v2 = self.pick_random_edge()
                self.total_edges -= len(self.graph[v1])
                self.total_edges -= len(self.graph[v2])
                # 1.2: Merge the edges
                self.graph[v1].extend(self.graph[v2])
                # 1.3: Update all references to v2 to point to v1
                for vertex in self.graph[v2]:
                    self.graph[vertex].remove(v2)
                    self.graph[vertex].append(v1)
                # 1.4: Remove self loops
                self.graph[v1] = [x for x in self.graph[v1] if x != v1]
                # 1.5: Update total edges
                self.total_edges += len(self.graph[v1])
                self.graph.pop(v2)
                # 1.6: Update cut groupings
                self.supervertices[v1].extend(self.supervertices.pop(v2))
            # 1.7: Calc min cut
            for edges in self.graph.values():
                min_cut = len(edges)
            # 1.8: Return min cut and the two supervertices
            return min_cut, self.supervertices      
    
        # 2: Pick a truly random edge:
        def pick_random_edge(self):
            rand_edge = randint(0, self.total_edges-1)
            for vertex, vertex_edges in self.graph.items():
                if len(vertex_edges) < rand_edge:
                    rand_edge -= len(vertex_edges)
                else:
                    from_vertex = vertex
                    to_vertex = vertex_edges[rand_edge-1]
                    return from_vertex, to_vertex
    
        # H.1: Helpful young man who prints our graph
        def print_graph(self):
            for key in self.graph:
                print("{}: {}".format(key, self.graph[key]))
    
    graph = KargerMinCut('kargerMinCut.txt')
    
    def full_karger(iterations):
        graph = KargerMinCut('kargerMinCut.txt')
        out = graph.find_min_cut()
        min_cut = out[0]
        supervertices = out[1]
    
        for i in range(iterations):
            graph = KargerMinCut('kargerMinCut.txt')
            out = graph.find_min_cut()
            if out[0] < min_cut:
                min_cut = out[0]
                supervertices = out[1]
        return min_cut, supervertices
    
    out = full_karger(100)
    print("min_cut: {}\nsupervertices: {}".format(out[0],out[1]))
    

答案 5 :(得分:0)

我完全同意以上答案。但是,当我使用Python 3.x运行您的代码时,结果是生成了代码1。此代码有时可以工作,但有时会失败。实际上,就像上面提到的@ user_3317704一样,您的代码中有一个错误:

for edge in edgelist:
    if edge[0] == edge[1]:
        edgelist.remove(edge)

进行任务时,请勿更改列表中的项目。它会引发错误。供您参考,可能是

newEdgeList = edgeList.copy();
for edge in edgeList:
    if edge[0] == edge[1]:
        newEdgeList.remove(edge);
edgeList = newEdgeList;