作业分析,Karger最小割:(python)Engineering: Algorithms1 - SELF PACED Algorithms: Design and Analysis
Programming Assignment 3
Download the following text file (right click and select "Save As..."): kargerMinCut.txt
The file contains the adjacency list representation of a simple undirected graph. There are 200 vertices labeled 1 to 200. The first column in the file represents the vertex label, and the particular row (other entries except the first column) tells all the vertices that the vertex is adjacent to. So for example, the 6th row looks like : "6 155 56 52 120 ......". This just means that the vertex with label 6 is adjacent to (i.e., shares an edge with) the vertices with labels 155,56,52,120,......,etc
Your task is to code up and run the randomized contraction algorithm for the min cut problem and use it on the above graph to compute the min cut. (HINT: Note that you'll have to figure out an implementation of edge contractions. Initially, you might want to do this naively, creating a new graph from the old every time there's an edge contraction. But you should also think about more efficient implementations.) (WARNING: As per the video lectures, please make sure to run the algorithm many times with different random seeds, and remember the smallest cut that you ever find.) Write your numeric answer in the space provided. So e.g., if your answer is 5, just type 5 in the space provided.
目前已经得到正确答案,把解决问题的思路记录一下:
1,读取文件
file = open("你的路径/kargerMinCut.txt",'r') dic = {} i = 1 str = '' for line in file: dic[i] = [] for char in line: if (char == "\t" or char == "\n") and str != "": dic[i].append(int(str)) str = "" if char != "\t" and char != "\n": str += char i += 1 for j in range(1,i): dic[j] = dic[j][1:]
这里犯了个小错误:‘\n’ ‘\t’ 只是一个字符
最后结果是把文件内容读到一个dict里面,key是node的名字1-200,value是一个list,存储图的连接信息。
2,建立数据库
为了避免每次运行代码都做一个新的图,这里我一共用了3个list同时更新:存储节点或者合并过的节点的archive,存储node是否在当前图里的activation,存储每个node的邻居的neighbor
activation = [] for j in range(len(dic)): activation.append(True) act_num = len(dic)
ii = len(dic) + 1 archive = [] for j in range(1,ii): archive.append({j})
neighbor = [] for j in range(len(dic)): neighbor.append(dic[j+1])
3,开始运行Karger算法
3,1初始化几个变量
act_num = len(activation) min_lib_element = 0 min_lib = -1 length = len(neighbor)
act_num表示现在还剩下的有效node的个数,一开始是200
min_lib_element表示这次循环的链接数结果
min_lib表示目前运行过的循环,最小链接数结果
length表示初始的长度,用于每次循环开始前的activation和act_num的初始化
3,2随机选择两个nodes
这里需要随机选node1和node2两个变量,形式是{1}或者{50,57,75,199}这样,
首先看node1,从1-act_num随机选一个int出来叫index,然后到activation里面找第index个True的元素,对应在archive里面的元素就是node1
然后看node1的neighbor,随机选一个出来,但这里选的neighbor不一定在activation里面对应True,所以需要循环activation表,找到坐标k,使得activation[k]为True,这个neighbor又在archive[k]里面,那么这个archive[k]就是node2
其中找node1时候用到的函数如下:
def find_true(activation,archive,index): i = 0 for k in range(len(activation)): if activation[k] == True: i += 1 else: continue if i == index: return archive[k]
3,3如何处理nodes合并
举个例子:
node NO.1和node NO.2合并以后的
archive:[{1},{2},{3},...,{200},{1,2}]
activation:[False,False,True,...,True,True]
neighbor前200个元素来自第一步的dic,后面需要写一个函数来计算任意两个nodes组合以后新的neighbor。当然,如果组合之后新的node已经在archive里面了,那就直接调用之前计算的结果。
合并函数如下:
def contract(node1,node2,archive,activation,act_num,neighbor): activation[archive.index(node1)] = False activation[archive.index(node2)] = False node_temp = node1|node2 if node_temp not in archive: archive.append(node_temp) activation.append(True) combine_node(node1,node2,neighbor) else: activation[archive.index(node_temp)] = True for x in node_temp: activation[archive.index({x})] = False act_num -= 1 return act_num
如果这个组合是新的,那么需要在上面那个函数里嵌套使用这个函数来更新neighbor表:
def combine_node(node1,node2,neighbor): same = list(node1)+list(node2) temp = [] for n1 in node1: for nbr in neighbor[n1-1]: if nbr not in temp and nbr not in same: temp.append(nbr) for n2 in node2: for nbr in neighbor[n2-1]: if nbr not in temp and nbr not in same: temp.append(nbr) neighbor.append(temp)
3,4 循环
for _ in range(你想循环的次数):
#initialization for j in range(length): activation[j] = True for j in range(length,len(activation)): activation[j] = False act_num = copy.deepcopy(length)
# 只要保持激活的节点还有2个以上,那就继续缩并节点 while(act_num > 2): index = random.randint(1,act_num) node1 = find_true(activation,archive,index) pseudo_node2_index = random.choice(neighbor[archive.index(node1)])- 1 if activation[pseudo_node2_index] == True: node2 = archive[pseudo_node2_index] else: for k in range(len(activation)): if activation[k] == True and list(archive[pseudo_node2_index])[0] in archive[k]: node2 = archive[k] act_num = contract(node1,node2,archive,activation,act_num,neighbor) node = [] for k in range(len(activation)): if activation[k] == True: node.append(archive[k]) for n1 in node[0]: for n2 in node[1]: if {n1,n2} in libedge: min_lib_element += 1 if min_lib < 0: min_lib = min_lib_element elif min_lib_element < min_lib: min_lib = min_lib_element min_lib_element = 0
#最后执行以下这个,看结果 print(min_lib)
结束!结果是17
4,总结:
做到后面发现用pandas来做,代码可能可以更精简