the story of a tree hackerrank solution error - python

Found this problem in hackerrank and have failed to pass some testcases.
One day Bob drew a tree, with n nodes and n-1 edges on a piece of paper. He soon discovered that parent of a node depends on the root of the tree. The following images shows an example of that:
Learning the fact, Bob invented an exciting new game and decided to play it with Alice. The rules of the game is described below:
Bob picks a random node to be the tree's root and keeps the identity of the chosen node a secret from Alice. Each node has an equal probability of being picked as the root.
Alice then makes a list of g guesses, where each guess is in the form u v and means Alice guesses that parent(v) = u is true. It's guaranteed that an undirected edge connecting u and v exists in the tree.
For each correct guess, Alice earns one point. Alice wins the game if she earns at least k points (i.e., at least k of her guesses were true).
Alice and Bob play q games. Given the tree, Alice's guesses, and the value of k for each game, find the probability that Alice will win the game and print it on a new line as a reduced fraction in the format p/q.
Solution:
There is a tree with some edges marked with arrows. For every vertex in a tree you have to count how many arrows point towards it.For one fixed vertex this may be done via one DFS. Every arrow that was traversed during DFS in direction opposite to its own adds 1.If you know the answer for vertex v, you can compute the answer for vertex u adjacent to v in O(1).
It's almost the same as for v, but if there are arrows u->v or v->u, their contributions are reversed.Now you can make the vertex u crawl over the whole graph by moving to adjacent vertices in the second DFS.
Problem: It is not able to pass all the test cases. I did sanity testing of the code and found no problem but I am not getting any clue why this is not working in hackerrank platform.
import sys
def gcd(a, b):
if not b:
return a
return gcd(b, a%b)
def dfs1(m, guess, root, seen):
'''keep 1 node as root and calculate how many arrows are pointing towards it'''
count = 0
for i in m[root]:
if seen[i][root] != 1 and seen[root][i] != 1:
seen[i][root] = 1
seen[root][i] = 1
count += (1 if guess[root][i] == 1 else 0) + dfs1(m, guess, i, seen)
return count
def dfs2(m, guess, root, seen, cost, k):
'''now make every node as root and calculate how many nodes
are pointed towards it; If u is the root node for which
dfs1 calculated n (number of arrows pointed towards the root)
then for v (adjacent node of u), it would be n-1 as v is the
made the parent now in this step (only if there is a guess, if
there is no guess then it would be not changed)'''
win = cost >= k
for i in m[root]:
if seen[i][root] != 1 and seen[root][i] != 1:
seen[i][root] = 1
seen[root][i] = 1
win += dfs2(m, guess, i, seen, cost - (1 if guess[root][i] == 1 else -guess[i][root]), k)
return win
q = int(raw_input().strip())
for a0 in xrange(q):
n = int(raw_input().strip())
m = {}
guess = [[0 for i in range(n+1)] for i in range(n+1)]
seen = [[0 for i in range(n+1)] for i in range(n+1)]
for a1 in xrange(n-1):
u,v = raw_input().strip().split(' ')
u,v = [int(u),int(v)]
if u not in m:
m[u] = []
m[u].append(v)
if v not in m:
m[v] = []
m[v].append(u)
g,k = raw_input().strip().split(' ')
g,k = [int(g),int(k)]
for a1 in xrange(g):
u,v = raw_input().strip().split(' ')
u,v = [int(u),int(v)]
guess[u][v] = 1
cost = dfs1(m, guess, 1, seen)
seen = [[0 for i in range(n+1)] for i in range(n+1)]
win = dfs2(m, guess, 1, seen, cost, k)
g = gcd(win, n)
print("{0}/{1}".format(win/g, n/g))

One possibility is that the code is correct but you are getting a stack overflow.
There can be 100,000 nodes and if these are all connected in a line your depth first search recursion will fail.
If this is true, then converting the DFS code from a recursive to an iterative formulation (by keeping a stack of things to try in an array) should help.
Another possibility is that there could be a guess such as 1,2 and a guess such as 2,1. In this case I am not sure that the score updating code would work:
win += dfs2(m, guess, i, seen, cost - (1 if guess[root][i] == 1 else -guess[i][root]), k)
perhaps this would work better:
win += dfs2(m, guess, i, seen, cost - guess[root][i] + guess[i][root], k)

Related

Determine How Long For Graph To Be Completely Visited

Suppose there is a population of 500 people, and each person knows
exactly 4 other people in the population. Suppose one person gets
infected with a disease, and that disease is guaranteed to infect the
other 4 people that person knows in exactly 1 day's time.
Write an algorithm to determine how many days it takes for everybody
to be infected, given an input adjacency matrix.
I can't figure out how to properly keep track of the days. I tried the following, but it just gives me a number close to the total number of nodes (it should be much lower):
import random
random.seed(20)
nodes = 500
num_connections = 4
# generate random graph
graph = []
for node_num in range(nodes):
myrow = [0 for node in range(nodes - num_connections)] + [1 for connection in range(num_connections)]
random.shuffle(myrow)
graph.append(myrow)
days = 0
visited = [0]
queue = [0]
while queue:
ss = queue.pop(0)
for neighbor, is_connected in enumerate(graph[ss]):
if is_connected == 1 and neighbor not in visited:
visited.append(neighbor)
queue.append(neighbor)
days += 1
print(days)
What am I doing wrong?
find longest shortest path from first infected person to any other person,
using the Dijkstra algorithm.
The length of this path is the number
of days.
your problem is equivalent to finding the longest shortest route in the graph. but if you want to fix your current code, I fixed it for you :)
import random
random.seed(20)
nodes = 50
num_connections = 4
# generate random graph
graph = []
for node_num in range(nodes):
myrow = [0 for node in range(nodes - num_connections)] + [1 for connection in range(num_connections)]
random.shuffle(myrow)
graph.append(myrow)
days = 0
visited = [0]
queue = [0]
while len(visited) < nodes:
patients = queue.copy()
queue.clear()
days += 1
while len(patients) > 0:
ss = patients.pop(0)
for neighbor, is_connected in enumerate(graph[ss]):
if is_connected == 1 and neighbor not in visited:
visited.append(neighbor)
queue.append(neighbor)
if len(queue) == 0:
print(f'The given directional graph is not connected, after {days} days, {len(visited)} persons has been infected')
exit()
print(days)

Python Ray Collisions Logic Incorrect (USACO 2020 December Bronze Q3 "Stuck in a Rut")

I am trying to solve this question from the USACO website. Problem Link: http://www.usaco.org/index.php?page=viewproblem2&cpid=1061
Farmer John has recently expanded the size of his farm, so from the perspective of his cows it is effectively now infinite in size! The cows think of the grazing area of the farm as an infinite 2D grid of square "cells", each filled with delicious grass (think of each cell as a square in an infinite chessboard). Each of Farmer John's N cows (1≤N≤50) starts out in a different cell; some start facing north, and some start facing east.
Every hour, every cow either
Stops if the grass in her current cell was already eaten by another
cow.
Eats all the grass in her current cell and moves one cell forward
according to the direction she faces.
Over time, each cow therefore leaves a barren "rut" of empty cells behind her.
If two cows move onto the same grassy cell in the same move, they share the cell and continue moving in their respective directions in the next hour.
Please determine the amount of grass eaten by each cow. Some cows never stop, and therefore eat an infinite amount of grass.
INPUT FORMAT (input arrives from the terminal / stdin):
The first line of input contains N. Each of the next N lines describes the starting location of a cow, in terms of a character that is either N (for north-facing) or E (for east-facing) and two nonnegative integers x and y (0≤x≤1000000000, 0≤y≤1000000000) giving the coordinates of a cell. All x-coordinates are distinct from each-other, and similarly for the y-coordinates.
To be as clear as possible regarding directions and coordinates, if a cow is in cell (x,y) and moves north, she ends up in cell (x,y+1). If she instead had moved east, she would end up in cell (x+1,y).
OUTPUT FORMAT (print output to the terminal / stdout):
Print N lines of output. Line i in the output should describe the number of cells worth of grass that the ith cow in the input eats. If a cow eats an infinite amount of grass, output "Infinity" for that cow.
SAMPLE INPUT:
6
E 3 5
N 5 3
E 4 6
E 10 4
N 11 2
N 8 1
SAMPLE OUTPUT:
5
3
Infinity
Infinity
2
5
SCORING:
In test cases 2-5, all coordinates are at most 100.
In test cases 6-10, there are no additional constraints.
My logic is that since simulating the collisions would be too slow because the field is huge, we can sort the cows by their x values, iterate over all the collisions/intersections of cows and stop the ones that should be stopped, and after iterating, print out the distances of the stopped cows. And if a cow hasn't stopped, print "Infinity".
My code:
# Defining the cow class with the original order position, x, y, distance,
# and whether or not it stopped.
class Cow:
def __init__(self, i, x, y):
self.i = i
self.x = x
self.y = y
self.dist = 0
self.stopped = False
# Get input from console and split cows into east facing and north facing cows.
n = int(input().strip())
hor = []
ver = []
ans = [0] * n
for i in range(n):
line = input().strip().split()
if line[0] == 'E':
hor.append(Cow(i, int(line[1]), int(line[2])))
else:
ver.append(Cow(i, int(line[1]), int(line[2])))
hor.sort(key = lambda c: c.x)
ver.sort(key = lambda c: c.x)
# Iterate over every possible collision. Logic problem here:
for h in hor:
for v in ver:
vdist = abs(h.y - v.y)
hdist = abs(h.x - v.x)
if h.stopped and v.stopped:
continue
elif h.stopped:
if v.x >= h.x and v.x <= h.x + h.dist and v.y <= h.y:
if vdist > hdist:
v.dist = vdist
v.stopped = True
elif v.stopped:
if v.x >= h.x and h.y <= v.y + v.dist and v.y <= h.y:
if hdist > vdist:
h.dist = hdist
h.stopped = True
else:
if v.x >= h.x and v.y <= h.y:
if vdist > hdist:
v.dist = vdist
v.stopped = True
if hdist > vdist:
h.dist = hdist
h.stopped = True
# Combine the lists and put them back into the original order.
cows = hor + ver
cows.sort(key = lambda c: c.i)
# Print out all the cows' distances, and it a cow hasn't stopped, replace distance with Infinity.
for i in cows:
if not i.stopped:
i.dist = "Infinity"
print(i.dist)
I'm not sure if it's just my code that isn't correct, or if it's my basic logic. If anyone can provide a fix, it would be appreciated.
Try this revised approach, using set to add the movements and check intersection.
from collections import deque
import sys
class Cow:
def __init__(self, d, x, y, amt):
self.d = d
self.x = x
self.y = y
self.amt = amt
lines = sys.stdin.read().strip().split('\n')
n = int(lines[0])
EMPTY = set()
COW = []
for line in lines[1:]:
d, x, y = line.split()
x, y = int(x), int(y)
COW.append(Cow(d, x, y, 0))
S = set()
for i in range(n):
for j in range(n):
S.add(abs(COW[i].x - COW[j].x))
S.add(abs(COW[i].y - COW[j].y))
S2 = set()
for k in S:
S2.add(k -1)
S2.add(k)
S2.add(k + 1)
S2.add(max(S) + 1)
dq = deque(sorted(S2)) #
SCORE = [None for _ in range(n)]
t = 0
while dq:
#nt += 1
dt = dq.popleft() - t
dt = max(dt, 1)
t += dt
VOID = []
for i in range(n):
if SCORE[i] is None:
if (COW[i].x, COW[i].y) in EMPTY:
SCORE[i] = COW[i].amt
continue
VOID.append((COW[i].x, COW[i].y))
if COW[i].d == 'N': COW[i].y += dt
elif COW[i].d == 'E': COW[i].x += dt
COW[i].amt += dt
for spot in VOID: EMPTY.add(spot)
for i in range(n):
print(SCORE[i] if SCORE[i] else 'Infinity')
To keep track of your algorithm you could split 'intersection-finding' and 'cow-stopping' into separate parts.
import sys
from collections import namedtuple
Cow = namedtuple('Cow', ['distance','facing','x','y','number'])
lines = sys.stdin.read().strip().split('\n')
cows = [Cow(0,*[int(x) if x.isnumeric() else x for x in i.split()], e)
for e,i in enumerate(lines[1:])]
# finding intersections
# save if distances differ, sorted descending by distance
intersections = []
for cowA, cowB in [(cowA, cowB)
for cowB in cows if cowB.facing == 'N'
for cowA in cows if cowA.facing == 'E'
]:
if cowA.x < cowB.x and cowA.y > cowB.y:
d1, d2 = cowB.x - cowA.x, cowA.y - cowB.y
if d1 != d2:
intersections.append(
sorted([Cow(d1, *cowA[1:]),Cow(d2, *cowB[1:])], reverse=True))
# sorting intersections by larger distance
# checking if a cow reached the intersection or stopped earlier
distances = [int(10E9)] * len(cows)
for i in sorted(intersections):
if i[1].distance < distances[i[1].number] and i[0].distance < distances[i[0].number]:
distances[i[0].number] = i[0].distance
for i in distances:
print('Infinity' if i==int(10E9) else i)
Output
5
3
Infinity
Infinity
2
5
My mistake was hor.sort(key = lambda c: c.x) where I sorted the list by the 1st element instead of the 2nd.
It should be hor.sort(key = lambda c: c.y) since that's what matters at the intersections.

Project Euler Project 67 - Python

I am doing the Project Euler #67 in Python. My program, which worked for Project 18, does not work for Project 67.
Code (excludes the opening of the file and the processing of information):
for i in range(len(temp)):
list1 = temp[i]
try:
list2 = temp[i+1]
trynum1 = list1[lastinput] + max(list2[lastinput],list2[lastinput+1])
try:
trynum2 = list1[lastinput+1] + max(list2[lastinput+1],list2[lastinput+2])
if trynum1 > trynum2:
outputlist.append(list1[lastinput])
else:
outputlist.append(list1[lastinput+1])
lastinput += 1
except IndexError:
outputlist.append(list1[0])
except IndexError:
if list1[lastinput] > list1[lastinput+1]:
outputlist.append(list1[lastinput])
else:
outputlist.append(list1[lastinput+1])
Variables:
temp is the triangle of integers
outputlist is a list which stores the numbers chosen by the program
I know the answer is 7273, but my program finds 6542. I cannot find an error which causes the situation. Please may you help me on it.
Logic
My approach to this program is to find one number (list1[lastinput]) and add it up with the larger number of the two below it (trynum1), compare with the number to the right of the first number (list1[lastinput+1]), adding the larger number of two below it (trynum2). I append the larger one to the output list.
This approach is logically flawed. When you're in row 1, you don't have enough information to know whether moving right or left will lead you to the largest sum, not with only a 2-row lookahead. You would need to look all the way to the bottom to ensure getting the best path.
As others have suggested, start at the bottom and work up. Remember, you don't need the entire path, just the sum. At each node, add the amount of the better of the two available paths (that's the score you get in taking that node to the bottom). When you get back to the top, temp[0][0], that number should be your final answer.
I thought day and night about problem 18 and I solved it, the same way I solved this one.
P.S. 100_triangle.txt is without 1st string '59'.
# Maximum path sum II
import time
def e67():
start = time.time()
f=open("100_triangle.txt")
summ=[59]
for s in f:
slst=s.split()
lst=[int(item) for item in slst]
for i in range(len(lst)):
if i==0:
lst[i]+=summ[i]
elif i==len(lst)-1:
lst[i]+=summ[i-1]
elif (lst[i]+summ[i-1])>(lst[i]+summ[i]):
lst[i]+=summ[i-1]
else:
lst[i]+=summ[i]
summ=lst
end = time.time() - start
print("Runtime =", end)
f.close()
return max(summ)
print(e67()) #7273
Though starting from the bottom is more efficient, I wanted to see if I could implement Dijkstra's algorithm on this one; it works well and only takes a few seconds (didn't time it precisely):
from math import inf
f = open("p067_triangle.txt", "r")
tpyramid = f.read().splitlines()
f.close()
n = len(tpyramid)
pyramid = [[100 - int(tpyramid[i].split()[j]) for j in range(i+1)] for i in range(n)]
paths = [[inf for j in range(i+1)] for i in range(n)]
paths[0][0] = pyramid[0][0]
def mini_index(pyr):
m = inf
for i in range(n):
mr = min([i for i in pyr[i] if i >= 0]+[inf])
if mr < m:
m, a, b = mr, i, pyr[i].index(mr)
return m, a, b
counter = 0
omega = inf
while counter < n*(n+1)/2:
min_weight, i, j = mini_index(paths)
if i != n-1:
paths[i+1][j] = min( paths[i+1][j], min_weight + pyramid[i+1][j])
paths[i+1][j+1] = min( paths[i+1][j+1], min_weight + pyramid[i+1][j+1])
else:
omega = min(omega, min_weight)
paths[i][j] = -1
counter += 1
print(100*n - omega)
Here is my solution. Indeed you have to take the bottom - up approach.
Result confirmed with PE. Thanks!
def get_triangle(listLink):
triangle = [[int(number) for number in row.split()] for row in open(listLink)]
return triangle
listOfLists = get_triangle('D:\\Development\\triangle.txt')
for i in range(len(listOfLists) - 2, -1, -1):
for j in range(len(listOfLists[i])):
listOfLists[i][j] += max(listOfLists[i+1][j], listOfLists[i+1][j+1])
print(listOfLists[0][0])

What's wrong with my recursion implementation?

I have recently started learning programming, just completed a course on edX. I was trying to solve this problem on HackerRank and it is running out of time in each case. What am I doing wrong?
n,k = input().strip().split(' ')
n,k = [int(n),int(k)]
x = [int(x_temp) for x_temp in input().strip().split(' ')]
x.sort()
def transmitter(aList=[], target=0):
'''
accepts a list of house location, and a target location for the transmitter
returns the optimal number of transmitters required to cover all the houses
'''
List = aList[:]
start = target - k
end = target + k + 1
for i in range(start, end):
if i in List:
List.remove(i)
if not List:
return 1
m = max(List)
for e in List:
if transmitter(List, e) < m:
m = transmitter(List, e)
return 1 + m
m = max(x)
for e in x:
if transmitter(x, e) < m:
m = transmitter(x, e)
print(m)
I am pretty new to this. Sorry for making any obvious mistakes, or for posting this here in case this is not the suitable site. In that case, it will be really helpful if you can recommend a site where I can ask such question.
the screenshot of the question
I'm pretty sure a greedy algorithm solves this problem optimally in just O(N) time. There's not need for any recursion. Just place each transmitter in turn as far to the right as you can without leaving any houses to its left uncovered. Stop when the last house is covered.
Here's how I'd code that:
def hackerland(houses, k): # houses should be sorted list of locations
first = None # location of first uncovered house
last = 0 # last location covered by a previous transmitter
prev = None
count = 0 # transmitters = []
for x in houses:
if first is not None and x > first + k:
first = None
count += 1 # transmitters.append(prev)
last = prev + k
if last is not None and x > last:
last = None
first = x
prev = x
if first is not None:
count += 1 # transmitters.append(prev)
return count # return transmitters
I've included comments that show how this code could be easily modified to return a list of the transmitter locations, rather than just a count of how many are needed.
It is not necessary to take a recursive approach. In fact, you can just work forward, iterate over the houses, placing transmitters when the previously placed one does not reach far enough to cover the current house, etc.
It is a bit more complicated than that, but not much. See this code:
# input
n,k = input().strip().split(' ')
n,k = [int(n),int(k)]
x = [int(x_temp) for x_temp in input().strip().split(' ')]
# eliminate duplicate house x-xoordinates, they don't influence the result
houses = list(set(x))
houses.sort()
# add extreme far dummy house (will make the loop easier)
houses.append(100000)
reachedX = 0 # coordinate until where the previously placed transmitter reaches
unreachedX = -1 # coordinate that the next one needs to cover (to the left)
lastHouseId = -1 # index where previous transmitter was placed
transmitters = [] # coordinates of the placed transmitters
for houseId, houseX in enumerate(houses):
if reachedX > unreachedX: # we might still be in range of last transmitter
if houseX > reachedX: # we just went out of reach
unreachedX = houseX # this house must be covered by next one
elif houseX - k > unreachedX: # transmitter here wouldn't reach far enough back
lastHouseId = houseId - 1 # place it on previous house
reachedX = houses[lastHouseId] + k
transmitters.append(houses[lastHouseId])
print(transmitters)
print(len(transmitters))

karger min cut algorithm in python 2.7

Here is my code for the karger min cut algorithm.. To the best of my knowledge the algorithm i have implemented is right. But I don get the answer right. If someone can check what's going wrong I would be grateful.
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)
The test case data is :
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
Please copy it in a text file and save it as "data.txt" and run the program
The answer should be :
the number of min cuts is 2 and
the cuts are at edges [(1,7), (4,5)]
This code also works.
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))
So Karger's algorithm is a `random alogorithm'. That is, each time you run it it produces a solution which is in no way guaranteed to be best. The general approach is to run it lots of times and keep the best solution. For lots of configurations there will be many solutions which are best or approximately best, so you heuristically find a good solution quickly.
As far as I can see, you are only running the algorithms once. Thus the solution is unlikely to be the optimal one. Try running it 100 times in for loop and holding onto the best solution.
As stated by Phil, I had to run my program 100 times. And one more correction in the code was :
for edge in edgelist:
if edge[0] == edge[1]:
edgelist.remove(edge)
This part of the code did not correctly eliminate the self loops. So I had to change the code like :
for i in range((len(edgelist)-1),-1,-1):
if edgelist[i][0] == edgelist[i][1]:
edgelist.remove(edgelist[i])
And this line was not needed. since the target node would be automatically changed to self loop and it would be removed.
edgelist.remove(target_edge)
Then as said earlier, the program was looped for 100 times, and I got the minimum cut by randomization. :)
While looking at this post's answers, I came across Joel's comment. According to Karger's algorithm, the edge must be chosen uniformly at random. You can find my implementation which is based on Oscar's answer and Joel's comment below:
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
Note, my response is in Python3 as it has been a few years since this post last received a response.
Further iterating upon #sestus' helpful answer above, I wanted to address three features:
Within the _pick_random_edge method of the class KarmgerMinCut(), rand_edge will ultimately match the length of vertex_edges. I adjusted the code to subtract 1 from rand_edge so rand_edge does not attempt to grab an element at a location 1 greater than the array length.
Understand which vertices comprise the two subgroupings representing the minimum cut. The functions implemented upon the "supervertices" dict achieve this.
Run this algorithm a large number of times (in my case, 100 times) and keep track of the smallest min_cut and its related supervertices. That's what my outside function full_karger() achieves. I am not clever enough to implement this as an internal
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]))
I totally agree with the above answers. But when I run your code with Python 3.x, it turns out to produce Code 1. This code sometimes works, but sometimes fails. In fact, as #user_3317704 mentioned above, there is a mistake in your code:
for edge in edgelist:
if edge[0] == edge[1]:
edgelist.remove(edge)
You should not change the items of the list when you do a for-do. It raises mistakes. For your reference, it could be
newEdgeList = edgeList.copy();
for edge in edgeList:
if edge[0] == edge[1]:
newEdgeList.remove(edge);
edgeList = newEdgeList;

Categories

Resources