Skip to content

Commit

Permalink
Added doctest and more explanation about Dijkstra execution. (TheAlgo…
Browse files Browse the repository at this point in the history
…rithms#1014)

* Added doctest and more explanation about Dijkstra execution.

* tests were not passing with python2 due to missing __init__.py file at number_theory folder

* Removed the dot at the beginning of the imported modules names because 'python3 -m doctest -v data_structures/hashing/*.py' and 'python3 -m doctest -v data_structures/stacks/*.py' were failing not finding hash_table.py and stack.py modules.

* Moved global code to main scope and added doctest for project euler problems 1 to 14.

* Added test case for negative input.

* Changed N variable to do not use end of line scape because in case there is a space after it the script will break making it much more error prone.

* Added problems description and doctests to the ones that were missing. Limited line length to 79 and executed python black over all scripts.

* Changed the way files are loaded to support pytest call.

* Added __init__.py to problems to make them modules and allow pytest execution.

* Added project_euler folder to test units execution

* Changed 'os.path.split(os.path.realpath(__file__))' to 'os.path.dirname()'
  • Loading branch information
brunohadlich authored and cclauss committed Jul 16, 2019
1 parent 2fb3bee commit 267b5ef
Show file tree
Hide file tree
Showing 100 changed files with 2,651 additions and 1,468 deletions.
1 change: 1 addition & 0 deletions .travis.yml
Expand Up @@ -26,6 +26,7 @@ script:
matrix
networking_flow
other
project_euler
searches
sorts
strings
Expand Down
2 changes: 1 addition & 1 deletion data_structures/hashing/double_hash.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3

from .hash_table import HashTable
from hash_table import HashTable
from number_theory.prime_numbers import next_prime, check_prime


Expand Down
2 changes: 1 addition & 1 deletion data_structures/hashing/hash_table_with_linked_list.py
@@ -1,4 +1,4 @@
from .hash_table import HashTable
from hash_table import HashTable
from collections import deque


Expand Down
Empty file.
2 changes: 1 addition & 1 deletion data_structures/hashing/quadratic_probing.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3

from .hash_table import HashTable
from hash_table import HashTable


class QuadraticProbing(HashTable):
Expand Down
2 changes: 1 addition & 1 deletion data_structures/stacks/infix_to_postfix_conversion.py
Expand Up @@ -2,7 +2,7 @@
from __future__ import absolute_import
import string

from .Stack import Stack
from stack import Stack

__author__ = 'Omkar Pathak'

Expand Down
117 changes: 94 additions & 23 deletions graphs/dijkstra.py
@@ -1,24 +1,50 @@
"""pseudo-code"""

"""
DIJKSTRA(graph G, start vertex s,destination vertex d):
// all nodes initially unexplored
let H = min heap data structure, initialized with 0 and s [here 0 indicates the distance from start vertex]
while H is non-empty:
remove the first node and cost of H, call it U and cost
if U is not explored
mark U as explored
if U is d:
return cost // total cost from start to destination vertex
for each edge(U, V): c=cost of edge(u,V) // for V in graph[U]
if V unexplored:
next=cost+c
add next,V to H (at the end)
DIJKSTRA(graph G, start vertex s, destination vertex d):
//all nodes initially unexplored
1 - let H = min heap data structure, initialized with 0 and s [here 0 indicates
the distance from start vertex s]
2 - while H is non-empty:
3 - remove the first node and cost of H, call it U and cost
4 - if U has been previously explored:
5 - go to the while loop, line 2 //Once a node is explored there is no need
to make it again
6 - mark U as explored
7 - if U is d:
8 - return cost // total cost from start to destination vertex
9 - for each edge(U, V): c=cost of edge(U,V) // for V in graph[U]
10 - if V explored:
11 - go to next V in line 9
12 - total_cost = cost + c
13 - add (total_cost,V) to H
You can think at cost as a distance where Dijkstra finds the shortest distance
between vertexes s and v in a graph G. The use of a min heap as H guarantees
that if a vertex has already been explored there will be no other path with
shortest distance, that happens because heapq.heappop will always return the
next vertex with the shortest distance, considering that the heap stores not
only the distance between previous vertex and current vertex but the entire
distance between each vertex that makes up the path from start vertex to target
vertex.
"""

import heapq


def dijkstra(graph, start, end):
"""Return the cost of the shortest path between vertexes start and end.
>>> dijkstra(G, "E", "C")
6
>>> dijkstra(G2, "E", "F")
3
>>> dijkstra(G3, "E", "F")
3
"""

heap = [(0, start)] # cost from start node,end node
visited = set()
while heap:
Expand All @@ -28,20 +54,65 @@ def dijkstra(graph, start, end):
visited.add(u)
if u == end:
return cost
for v, c in G[u]:
for v, c in graph[u]:
if v in visited:
continue
next = cost + c
heapq.heappush(heap, (next, v))
return (-1, -1)
return -1


G = {
"A": [["B", 2], ["C", 5]],
"B": [["A", 2], ["D", 3], ["E", 1], ["F", 1]],
"C": [["A", 5], ["F", 3]],
"D": [["B", 3]],
"E": [["B", 4], ["F", 3]],
"F": [["C", 3], ["E", 3]],
}

"""
Layout of G2:
E -- 1 --> B -- 1 --> C -- 1 --> D -- 1 --> F
\ /\
\ ||
----------------- 3 --------------------
"""
G2 = {
"B": [["C", 1]],
"C": [["D", 1]],
"D": [["F", 1]],
"E": [["B", 1], ["F", 3]],
"F": [],
}

"""
Layout of G3:
E -- 1 --> B -- 1 --> C -- 1 --> D -- 1 --> F
\ /\
\ ||
-------- 2 ---------> G ------- 1 ------
"""
G3 = {
"B": [["C", 1]],
"C": [["D", 1]],
"D": [["F", 1]],
"E": [["B", 1], ["G", 2]],
"F": [],
"G": [["F", 1]],
}

shortDistance = dijkstra(G, "E", "C")
print(shortDistance) # E -- 3 --> F -- 3 --> C == 6

shortDistance = dijkstra(G2, "E", "F")
print(shortDistance) # E -- 3 --> F == 3

G = {'A': [['B', 2], ['C', 5]],
'B': [['A', 2], ['D', 3], ['E', 1]],
'C': [['A', 5], ['F', 3]],
'D': [['B', 3]],
'E': [['B', 1], ['F', 3]],
'F': [['C', 3], ['E', 3]]}
shortDistance = dijkstra(G3, "E", "F")
print(shortDistance) # E -- 2 --> G -- 1 --> F == 3

shortDistance = dijkstra(G, 'E', 'C')
print(shortDistance)
if __name__ == "__main__":
import doctest
doctest.testmod()
Empty file.
31 changes: 26 additions & 5 deletions project_euler/problem_01/sol1.py
@@ -1,13 +1,34 @@
'''
"""
Problem Statement:
If we list all the natural numbers below 10 that are multiples of 3 or 5,
we get 3,5,6 and 9. The sum of these multiples is 23.
Find the sum of all the multiples of 3 or 5 below N.
'''
"""
from __future__ import print_function

try:
raw_input # Python 2
raw_input # Python 2
except NameError:
raw_input = input # Python 3
n = int(raw_input().strip())
print(sum([e for e in range(3, n) if e % 3 == 0 or e % 5 == 0]))


def solution(n):
"""Returns the sum of all the multiples of 3 or 5 below n.
>>> solution(3)
0
>>> solution(4)
3
>>> solution(10)
23
>>> solution(600)
83700
>>> solution(-7)
0
"""

return sum([e for e in range(3, n) if e % 3 == 0 or e % 5 == 0])


if __name__ == "__main__":
print(solution(int(raw_input().strip())))
43 changes: 31 additions & 12 deletions project_euler/problem_01/sol2.py
@@ -1,20 +1,39 @@
'''
"""
Problem Statement:
If we list all the natural numbers below 10 that are multiples of 3 or 5,
we get 3,5,6 and 9. The sum of these multiples is 23.
Find the sum of all the multiples of 3 or 5 below N.
'''
"""
from __future__ import print_function

try:
raw_input # Python 2
raw_input # Python 2
except NameError:
raw_input = input # Python 3
n = int(raw_input().strip())
sum = 0
terms = (n-1)//3
sum+= ((terms)*(6+(terms-1)*3))//2 #sum of an A.P.
terms = (n-1)//5
sum+= ((terms)*(10+(terms-1)*5))//2
terms = (n-1)//15
sum-= ((terms)*(30+(terms-1)*15))//2
print(sum)


def solution(n):
"""Returns the sum of all the multiples of 3 or 5 below n.
>>> solution(3)
0
>>> solution(4)
3
>>> solution(10)
23
>>> solution(600)
83700
"""

sum = 0
terms = (n - 1) // 3
sum += ((terms) * (6 + (terms - 1) * 3)) // 2 # sum of an A.P.
terms = (n - 1) // 5
sum += ((terms) * (10 + (terms - 1) * 5)) // 2
terms = (n - 1) // 15
sum -= ((terms) * (30 + (terms - 1) * 15)) // 2
return sum


if __name__ == "__main__":
print(solution(int(raw_input().strip())))
98 changes: 57 additions & 41 deletions project_euler/problem_01/sol3.py
@@ -1,50 +1,66 @@
from __future__ import print_function

'''
"""
Problem Statement:
If we list all the natural numbers below 10 that are multiples of 3 or 5,
we get 3,5,6 and 9. The sum of these multiples is 23.
Find the sum of all the multiples of 3 or 5 below N.
'''
'''
This solution is based on the pattern that the successive numbers in the series follow: 0+3,+2,+1,+3,+1,+2,+3.
'''
"""
from __future__ import print_function

try:
raw_input # Python 2
raw_input # Python 2
except NameError:
raw_input = input # Python 3
n = int(raw_input().strip())
sum=0
num=0
while(1):
num+=3
if(num>=n):
break
sum+=num
num+=2
if(num>=n):
break
sum+=num
num+=1
if(num>=n):
break
sum+=num
num+=3
if(num>=n):
break
sum+=num
num+=1
if(num>=n):
break
sum+=num
num+=2
if(num>=n):
break
sum+=num
num+=3
if(num>=n):
break
sum+=num

print(sum);

def solution(n):
"""
This solution is based on the pattern that the successive numbers in the
series follow: 0+3,+2,+1,+3,+1,+2,+3.
Returns the sum of all the multiples of 3 or 5 below n.
>>> solution(3)
0
>>> solution(4)
3
>>> solution(10)
23
>>> solution(600)
83700
"""

sum = 0
num = 0
while 1:
num += 3
if num >= n:
break
sum += num
num += 2
if num >= n:
break
sum += num
num += 1
if num >= n:
break
sum += num
num += 3
if num >= n:
break
sum += num
num += 1
if num >= n:
break
sum += num
num += 2
if num >= n:
break
sum += num
num += 3
if num >= n:
break
sum += num
return sum


if __name__ == "__main__":
print(solution(int(raw_input().strip())))

0 comments on commit 267b5ef

Please sign in to comment.