This is my Dijkstra's implementation. It's passing all cases in the pytest input.n.txt files but when I submit to the grading software (that doesn't provide the test or any output) I get an invalid result.
Here's my solution (passes all provided test cases, but not hidden ones).
# Uses python3
import queue
import sys
from math import inf
def dijkstra(adj, cost, s, t):
seen = set([s])
dist = [inf] * len(adj)
dist[s] = 0
prev = [None] * len(adj)
prev[s] = s
q = queue.Queue()
q.put(s)
while not q.empty():
n = q.get()
# print(n)
edges = []
for i, adjacent in enumerate(adj[n]):
edges.append([adjacent, cost[n][i]])
for i, edge in enumerate(edges):
d = dist[n] + edge[1]
if d < dist[edge[0]]:
dist[edge[0]] = d
edge[1] = d
prev[edge[0]] = n
edges = sorted(edges, key=lambda x: x[1])
for (e, w) in edges:
if not e in seen:
seen.add(e)
q.put(e)
# print(dist)
# print(prev)
return dist[t] if dist[t] is not inf else -1
def parse(input):
data = list(map(int, input.split()))
n, m = data[0:2]
data = data[2:]
edges = list(zip(zip(data[0 : (3 * m) : 3], data[1 : (3 * m) : 3]), data[2 : (3 * m) : 3]))
data = data[3 * m :]
adj = [[] for _ in range(n)]
cost = [[] for _ in range(n)]
for ((a, b), w) in edges:
adj[a - 1].append(b - 1)
cost[a - 1].append(w)
s, t = data[0] - 1, data[1] - 1
return dijkstra(adj, cost, s, t)
if __name__ == "__main__":
input = sys.stdin.read()
print(parse(input))
def test_parse():
assert 3 == parse(open("input.txt").read())
assert 6 == parse(open("input.1.txt").read())
assert -1 == parse(open("input.2.txt").read())
assert 3 == parse(open("input.3.txt").read())
assert 0 == parse(open("input.4.txt").read())
assert 0 == parse(open("input.5.txt").read())
The format of the input is as follows...
number_of_vertices number_of_edges
from to weight
from to weight
start end
input.txt
4 4
1 2 1
4 1 2
2 3 2
1 3 5
1 3
input.1.txt
5 9
1 2 4
1 3 2
2 3 2
3 2 1
2 4 2
3 5 4
5 4 1
2 5 3
3 4 4
1 5
input.2.txt
3 3
1 2 7
1 3 5
2 3 2
3 2
input.3.txt
5 5
1 2 1
1 3 2
2 3 1
2 4 6
3 4 1
1 4
input.4.txt
5 6
1 2 1
1 3 2
2 3 1
2 4 6
3 4 1
1 1 2
1 1
input.5.txt
4 4
1 2 1
2 3 1
3 4 1
4 1 1
1 1
My program passes ALL of these. And I've tried messing around with all the edge cases I can think of testing but still it fails with a "Wrong answer" error in the testing software.
One of the comments of the thread of somebody who DID solve it:
Wow! I really struggled to put this one together, not because I didn't
understand the Dijkstra algorithm but because of the difficulty in
adjusting the priority of an item already added to a Python
PriorityQueue class (whose use was implied by importing queue in the
start code) or by keeping track of its position in the priority queue,
which made translating the algorithm, as presented in the lectures,
verbatim difficult.
In case it is helpful to others, the way I got around this was to move
from thinking in terms of inserting vertices to the priority queue to
inserting references to the vertices, with most updated distance at
the time of insertion as the priority, instead. That way we don't need
to adjust the priority of an item already added to the queue at a
later time.
We may end up inserting several references to the same vertex to the
queue, but we will, of course, encounter the reference with the least
distance first, and we can ignore any future references to the same
vertex that we might encounter afterwards. Further, we can abort the
algorithm as soon as we've popped a reference to the destination
vertex.
This still runs pretty efficiently (for me, a maximum time of about a
twentieth of that allowed), and is, in retrospect, a small adjustment
in viewing the problem.
Your algorithm uses a queue; Dijkstra's algorithm does not use a queue.
At each iteration you must select the unconfirmed vertex with the shortest path distance. This can be done using a min-priority queue, where the path distance is the priority, but note also that each vertex may have to be added to the priority queue more than once if it is discovered via different paths of different distances. (Your classmate initially tried to do this the hard way - by updating the priority of a vertex already in the priority queue, instead of just allowing each vertex to be present in the priority queue multiple times.)
So your algorithm is not a proper implementation of Dijkstra's algorithm, because it confirms the vertices in the order they are discovered, rather than in order of path distance from the source vertex.
Related
I have a triple for loop that creates a 1 row and 2 column collection of numbers starting at 0 0 and going up to 2 2. The third for loop counts from 0 to 8. The code looks as follows:
for N in range(0,3):
for K in range(0,3):
print(N,K)
for P in range(0,9):
print(P)
If you run this code you get the obvious output:
0 0
0
1
2
3
4
5
6
7
8
0 1
0
1
2
3
4
5
6
7
8
0 2
0
1
2
3
4
5
6
7
8
...
And so on. I want instead of the output of 0 to 8 after the N K printout, instead something that looks like:
0 0
0
0 1
1
0 2
2
1 0
3
1 1
4
1 2
5
2 0
6
2 1
7
2 2
8
My first guess was an if statement that said:
if P == Q:
break
where Q was several sets of sums and even the N,K array. However, I couldn't figure out the best way to get my
wanted output. I do think an if statement is the best way to achieve my wanted result, but I'm not quite sure of how to approach it. P is necessary for the rest of my code as it will be used in some subplots.
As this is just an increment by one at each print, you can just do compute the index with N * 3 + K
for N in range(0, 3):
for K in range(0, 3):
print(N, K)
print(N * 3 + K)
CODE DEMO
You can use zip to traverse two iterables in parallel. In this case, one of the iterables is the result of a nested list. That can be handled by using itertools.product, as follows:
import itertools
for (N, K), P in zip(itertools.product(range(3), range(3)), range(9)):
print(N, K)
print(P)
I have started to implement backwards Dijkstra's algorithm in python:
def backwardsDijkstra(g: DoubleDictGraph, s, t):
q = PriorityQueue()
dist = {}
next = {}
q.add(t, 0)
dist[t] = 0
found = False
while not q.isEmpty() and not found:
x = q.pop()
for y in g.parseNin(x):
if y not in dist.keys() or dist[x] + g.Cost(x, y) < dist[y]:
dist[y] = dist[x] + g.Cost(x, y)
q.add(y, dist[y])
next[y] = x
if x == t:
found = True
return dist[s], next
And I can't figure out why it stops at the second iteration for the next graph for example:
5 7
0 1 5
0 2 20
1 2 10
1 3 30
2 3 5
2 4 20
3 4 10
(number of vertices, number of edges, (start,end,cost)
The bug in you code is at this line:
if x == t:
found = True
Since you are starting from t (target), you should be looking for s (source). Currently you are breaking out of the loop for the first candidate (which is t).
I have a matrix as shown below (taken from a txt file with an argument), and every cell has neighbors. Once you pick a cell, that cell and all neighboring cells that containing the same number will disappear.
1 0 4 7 6 8
0 5 4 4 5 5
2 1 4 4 4 6
4 1 3 7 4 4
I've tried to do this with using recursion. I separated function four parts which are up(), down() , left() and right(). But I got an error message: RecursionError: maximum recursion depth exceeded in comparison
cmd=input("Row,column:")
cmdlist=command.split(",")
row,column=int(cmdlist[0]),int(cmdlist[1])
num=lines[row-1][column-1]
def up(x,y):
if lines[x-2][y-1]==num and x>1:
left(x,y)
right(x,y)
lines[x-2][y-1]=None
def left(x,y):
if lines[x-1][y-2]==num and y>1:
up(x,y)
down(x,y)
lines[x-1][y-2]=None
def right(x,y):
if lines[x-1][y]==num and y<len(lines[row-1]):
up(x,y)
down(x,y)
lines[x-1][y]=None
def down(x,y):
if lines[x][y-1]==num and x<len(lines):
left(x,y)
right(x,y)
lines[x][y-1]=None
up(row,column)
down(row,column)
for i in lines:
print(str(i).strip("[]").replace(",","").replace("None"," "))
When I give the input (3,3) which represents the number of "4", the output must be like this:
1 0 7 6 8
0 5 5 5
2 1 6
4 1 3 7
I don't need fixed code, just the main idea will be enough. Thanks a lot.
Recursion error happens when your recursion does not terminate.
You can solve this without recursing using set's of indexes:
search all indexes that contain the looked for number into all_num_idx
add the index you are currently at (your input) to a set tbd (to be deleted)
loop over the tbd and add all indexed from all_num_idx that differ only in -1/+1 in row or col to any index thats already in the set
do until tbd does no longer grow
delete all indexes from tbd:
t = """4 0 4 7 6 8
0 5 4 4 5 5
2 1 4 4 4 6
4 1 3 7 4 4"""
data = [k.strip().split() for k in t.splitlines()]
row,column=map(int,input("Row,column:").strip().split(";"))
num = data[row][column]
len_r =len(data)
len_c = len(data[0])
all_num_idx = set((r,c) for r in range(len_r) for c in range(len_c) if data[r][c]==num)
tbd = set( [ (row,column)] ) # inital field
tbd_size = 0 # different size to enter while
done = set() # we processed those already
while len(tbd) != tbd_size: # loop while growing
tbd_size=len(tbd)
for t in tbd:
if t in done:
continue
# only 4-piece neighbourhood +1 or -1 in one direction
poss_neighbours = set( [(t[0]+1,t[1]), (t[0],t[1]+1),
(t[0]-1,t[1]), (t[0],t[1]-1)] )
# 8-way neighbourhood with diagonals
# poss_neighbours = set((t[0]+a,t[1]+b) for a in range(-1,2) for b in range(-1,2))
tbd = tbd.union( poss_neighbours & all_num_idx)
# reduce all_num_idx by all those that we already addded
all_num_idx -= tbd
done.add(t)
# delete the indexes we collected
for r,c in tbd:
data[r][c]=None
# output
for line in data:
print(*(c or " " for c in line) , sep=" ")
Output:
Row,column: 3,4
4 0 7 6 8
0 5 5 5
2 1 6
4 1 3 7
This is a variant of a "flood-fill-algorythm" flooding only cells of a certain value. See https://en.wikipedia.org/wiki/Flood_fill
Maybe you should replace
def right(x,y):
if lines[x-1][y]==num and y<len(lines[row-1]):
up(x,y)
down(x,y)
lines[x-1][y]=None
by
def right(x,y):
if lines[x-1][y]==num and y<len(lines[row-1]):
lines[x-1][y]=None
up(x - 1,y)
down(x - 1,y)
right(x - 1, y)
and do the same for all the other functions.
Putting lines[x-1][y]=None ensure that your algorithm stops and changing the indices ensure that the next step of your algorithm will start from the neighbouring cell.
my question relates to this problem https://leetcode.com/problems/combination-sum-iii/discuss/ and all backtracking problems.
My question is: why is my code (really similar to other people's answers) always have a larger run time than theirs?
def combinationSum3(self, k, n):
"""
:type k: int how many number
:type n: int how much add to
:rtype: List[List[int]]
"""
res=[]
self.backtrack(k, n, [], res)
newres=[]
for each in res:
newres.append(list(each))
return newres
def backtrack(self, k, n, path, res):
if len(path)> k or sum(path)>n:
return
if len(set(path))==k and sum(path)==n:
if set(path) not in res:
res.append(set(path))
return
for i in range(1, 10):
self.backtrack(k, n, path+[i], res)
other people's code that passed the time limit:
# #param {integer} k
# #param {integer} n
# #return {integer[][]}
def combinationSum3(self, k, n):
if n > sum([i for i in range(1, 11)]):
return []
res = []
self.sum_help(k, n, 1, [], res)
return res
def sum_help(self, k, n, curr, arr, res):
if len(arr) == k:
if sum(arr) == n:
res.append(list(arr))
return
if len(arr) > k or curr > 9:
return
for i in range(curr, 10):
arr.append(i)
self.sum_help(k, n, i + 1, arr, res)
arr.pop()
The main difference and slowdown is due to your code testing many more combinations than the other solution. You generate all combinations of numbers, which leads you to test the "same" combination multiple times, whereas the other solution only generates each possible candidate once, by only allowing next number in the sequence to be equal or larger than the previous one.
Look at the following, limited list of candidates, where the range of numbers is limited to be from 1 to 3:
1 1 1
1 1 2
1 1 3
1 2 1 <-
1 2 2
1 2 3
1 3 1 <-
1 3 2 <-
1 3 3
2 1 1 <-
2 1 2 <-
2 1 3 <-
2 2 1 <-
2 2 2
2 2 3
2 3 1 <-
2 3 2 <-
2 3 3
3 1 1 <-
3 1 2 <-
3 1 3 <-
3 2 1 <-
3 2 2 <-
3 2 3 <-
3 3 1 <-
3 3 2 <-
3 3 3
The entries with <- represent combinations that you test, which are unnecessary, and not tested by the other program.
In addition, and as a consequence of your extra candidates generated, you also need to spend extra time on each possible solution, to make sure it is not already in the solution set (to avoid duplicates). That is not needed by the other solution, as there each candidate is unique. This extra test also adds to your run time, to make it even worse. However, the main thing to fix is the number of candidates you test!
I have this Numpy code:
def uniq(seq):
"""
Like Unix tool uniq. Removes repeated entries.
:param seq: numpy.array. (time,) -> label
:return: seq
"""
diffs = np.ones_like(seq)
diffs[1:] = seq[1:] - seq[:-1]
idx = diffs.nonzero()
return seq[idx]
Now, I want to extend this to support 2D arrays and make it use Theano. It should be fast on the GPU.
I will get an array with multiple sequences as multiple batches in the format (time,batch), and a time_mask which specifies indirectly the length of each sequence.
My current try:
def uniq_with_lengths(seq, time_mask):
# seq is (time,batch) -> label
# time_mask is (time,batch) -> 0 or 1
num_batches = seq.shape[1]
diffs = T.ones_like(seq)
diffs = T.set_subtensor(diffs[1:], seq[1:] - seq[:-1])
time_range = T.arange(seq.shape[0]).dimshuffle([0] + ['x'] * (seq.ndim - 1))
idx = T.switch(T.neq(diffs, 0) * time_mask, time_range, -1)
seq_lens = T.sum(T.ge(idx, 0), axis=0) # (batch,) -> len
max_seq_len = T.max(seq_lens)
# I don't know any better way without scan.
def step(batch_idx, out_seq_b1):
out_seq = seq[T.ge(idx[:, batch_idx], 0).nonzero(), batch_idx][0]
return T.concatenate((out_seq, T.zeros((max_seq_len - out_seq.shape[0],), dtype=seq.dtype)))
out_seqs, _ = theano.scan(
step,
sequences=[T.arange(num_batches)],
outputs_info=[T.zeros((max_seq_len,), dtype=seq.dtype)]
)
# out_seqs is (batch,max_seq_len)
return out_seqs.T, seq_lens
How to construct out_seqs directly?
I would do something like out_seqs = seq[idx] but I'm not exactly sure how to express that.
Here's a quick answer that only addresses part of your task:
def compile_theano_uniq(x):
diffs = x[1:] - x[:-1]
diffs = tt.concatenate([tt.ones_like([x[0]], dtype=diffs.dtype), diffs])
y = diffs.nonzero_values()
return theano.function(inputs=[x], outputs=y)
theano_uniq = compile_theano_uniq(tt.vector(dtype='int32'))
The key is nonzero_values().
Update: I can't imagine any way to do this without using theano.scan. To be clear, and using 0 as padding, I'm assuming that given the input
1 1 2 3 3 4 0
1 2 2 2 3 3 4
1 2 3 4 5 0 0
you would want the output to be
1 2 3 4 0 0 0
1 2 3 4 0 0 0
1 2 3 4 5 0 0
or even
1 2 3 4 0
1 2 3 4 0
1 2 3 4 5
You could identify the indexes of the items you want to keep without using scan. Then either a new tensor needs to be constructed from scratch or the values you want to keep some how moved to make the sequences contiguous. Neither approaches seem feasible without theano.scan.