Skip to content

Commit eafc4f0

Browse files
authored
Merge pull request #43 from IanDoarn/master
Syntax and README updates!
2 parents 771a394 + 1c86a70 commit eafc4f0

File tree

9 files changed

+106
-80
lines changed

9 files changed

+106
-80
lines changed

README.rst

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -47,28 +47,29 @@ Quick Start Guide
4747

4848
.. code:: python
4949
50-
from pygorithm.sorting import bubble_sort
51-
myList = [12, 4, 3, 5, 13, 1, 17, 19, 15]
52-
sortedList = bubble_sort.sort(myList)
53-
print(sortedList)
50+
>>> from pygorithm.sorting import bubble_sort
51+
>>> my_list = [12, 4, 3, 5, 13, 1, 17, 19, 15]
52+
>>> sorted_list = bubble_sort.sort(my_list)
53+
>>> print(sorted_list)
54+
>>> [1, 3, 4, 5, 12, 13, 15, 17, 19]
5455
5556
5657
* To get the code for function used
5758

5859
.. code:: python
5960
60-
from pygorithm.sorting import bubble_sort
61-
code = bubble_sort.get_code()
62-
print(code)
61+
>>> from pygorithm.sorting import bubble_sort
62+
>>> code = bubble_sort.get_code()
63+
>>> print(code)
6364
6465
6566
* To get the time complexity of an algorithm
6667

6768
.. code:: python
6869
69-
from pygorithm.sorting import bubble_sort
70-
time_complexity = bubble_sort.time_complexities()
71-
print(time_complexity)
70+
>>> from pygorithm.sorting import bubble_sort
71+
>>> time_complexity = bubble_sort.time_complexities()
72+
>>> print(time_complexity)
7273
7374
* To see all the available functions in a module, you can just type ``help()`` with the module name as argument. For example,
7475

@@ -92,8 +93,6 @@ Quick Start Guide
9293
quick_sort
9394
selection_sort
9495
shell_sort
95-
>>> from pygorithm.sorting import bubble_sort
96-
>>> help(bubble_sort)
9796
9897
9998
Tests

docs/Data_Structure.rst

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,6 @@ Features
7070
queue
7171
stack
7272
tree
73-
>>> from pygorithm.data_structures import graph
74-
>>> help(graph)
7573
7674
7775
Stack

docs/Fibonacci.rst

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,6 @@ Features
5050
modules
5151
recursion
5252
53-
>>> from pygorithm.fibonacci import memoization
54-
>>> help(memoization)
5553
5654
Implementations API
5755
-------------------

docs/Searching.rst

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,13 @@ Quick Start Guide
1212
# import the required searching algorithm
1313
from pygorithm.searching import binary_search
1414
15-
myList = [12, 4, 2, 14, 3, 7, 5]
15+
my_list = [12, 4, 2, 14, 3, 7, 5]
1616
1717
# pre-requisite for binary search is that the list should be sorted
18-
myList.sort()
18+
my_list.sort()
1919
2020
# to search an element in the above list
21-
index = binary_search.search(myList, 7)
21+
index = binary_search.search(my_list, 7)
2222
print(index)
2323
2424
Features
@@ -48,8 +48,6 @@ Features
4848
linear_search
4949
modules
5050
quick_select
51-
>>> from pygorithm.searching import binary_search)
52-
>>> help(binary_search)
5351
5452
5553
* For Searching:
@@ -60,13 +58,13 @@ Features
6058
# import the required searching algorithm
6159
from pygorithm.searching import binary_search
6260
63-
myList = [12, 4, 2, 14, 3, 7, 5]
61+
my_list = [12, 4, 2, 14, 3, 7, 5]
6462
6563
# pre-requisite for binary search is that the list should be sorted
66-
myList.sort()
64+
my_list.sort()
6765
6866
# to search an element in the above list
69-
index = binary_search.search(myList, 7)
67+
index = binary_search.search(my_list, 7)
7068
print(index)
7169
7270
* Get time complexities of all the searching algorithms

docs/Sorting.rst

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ Quick Start Guide
1515
my_list = [12, 4, 2, 14, 3, 7, 5]
1616
1717
# to sort the _list
18-
sorted__list = bubble_sort.sort(my_list)
18+
sorted_list = bubble_sort.sort(my_list)
1919
2020
Features
2121
--------
@@ -42,8 +42,6 @@ Features
4242
quick_sort
4343
selection_sort
4444
shell_sort
45-
>>> from pygorithm.sorting import bubble_sort
46-
>>> help(bubble_sort)
4745
4846
* For sorting:
4947
Remember ``sort()`` function takes its parameter as a _list only.
@@ -56,7 +54,7 @@ Features
5654
my_list = [12, 4, 2, 14, 3, 7, 5]
5755
5856
# to sort the _list
59-
sorted__list = bubble_sort.sort(my_list)
57+
sorted_list = bubble_sort.sort(my_list)
6058
6159
* Get time complexities of all the sorting algorithms
6260

docs/index.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,10 @@ Quick Start Guide
4242
# This will print the code for bubble sort
4343
print(bubble_sort.get_code())
4444
45-
myList = [12, 4, 2, 14, 3, 7, 5]
45+
my_list = [12, 4, 2, 14, 3, 7, 5]
4646
4747
# to sort the list
48-
sorted_list = bubble_sort.sort(myList)
48+
sorted_list = bubble_sort.sort(my_list)
4949
5050
Getting Started
5151
---------------

pygorithm/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,5 +63,5 @@
6363
'searching',
6464
'sorting',
6565
'string',
66-
'pathing',
66+
'pathfinding',
6767
]

pygorithm/pathfinding/astar.py

Lines changed: 58 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,19 @@
1+
"""
2+
A*
3+
4+
Author: Timothy Moore
5+
6+
A* is an informed search algorithm, or a best-first search,
7+
meaning that it solves problems by searching among all possible
8+
paths to the solution (goal) for the one that incurs the smallest
9+
cost (least distance travelled, shortest time, etc.), and among
10+
these paths it first considers the ones that appear to lead most
11+
quickly to the solution.
12+
https://en.wikipedia.org/wiki/A*_search_algorithm
13+
"""
114
import heapq
15+
import inspect
16+
217

318
class OneDirectionalAStar(object):
419
"""AStar object
@@ -28,7 +43,7 @@ class OneDirectionalAStar(object):
2843
#
2944
# You would expand your side of center and find the top and bottom at
3045
# (5 + 12) - WORSE than just going to them. This is the case where we
31-
# would NOT add the path base->center->top to the open list because
46+
# would NOT add the path base->center->top to the _open list because
3247
# (for these weights) it will never be better than base->top.
3348
#
3449
# You would also add the new node (55 + 0) or the destination.
@@ -37,14 +52,15 @@ class OneDirectionalAStar(object):
3752
# river with a cost of (18 + 12).
3853
#
3954
# You expand the top node on the other side of the river next and find
40-
# one of the neighbors is already on the open list (the destination)
55+
# one of the neighbors is already on the _open list (the destination)
4156
# at a score of (55 + 0), but your cost to get there is (30 + 0). This
4257
# is where you would REPLACE the old path with yourself.
4358

4459
def __init__(self):
4560
pass
46-
47-
def reverse_path(self, node):
61+
62+
@staticmethod
63+
def reverse_path(node):
4864
"""
4965
Walks backward from an end node to the start
5066
node and reconstructs a path. Meant for internal
@@ -75,7 +91,7 @@ def find_path(self, graph, start, end, heuristic_fn):
7591
- The distance between nodes are small
7692
- There are too many nodes for an exhaustive search
7793
to ever be feasible.
78-
- The world is mostly open (ie there are many paths
94+
- The world is mostly _open (ie there are many paths
7995
from the start to the end that are acceptable)
8096
- Execution speed is more important than accuracy.
8197
The best way to do this is to make the heuristic slightly
@@ -94,21 +110,21 @@ def find_path(self, graph, start, end, heuristic_fn):
94110
"""
95111

96112
# It starts off very similiar to Dijkstra. However, we will need to lookup
97-
# nodes in the open list before. There can be thousands of nodes in the open
113+
# nodes in the _open list before. There can be thousands of nodes in the _open
98114
# list and any unordered search is too expensive, so we trade some memory usage for
99115
# more consistent performance by maintaining a dictionary (O(1) lookup) between
100116
# vertices and their nodes.
101-
open_lookup = {}
102-
open = []
117+
_open_lookup = {}
118+
_open = []
103119
closed = set()
104120

105121
# We require a bit more information on each node than Dijkstra
106122
# and we do slightly more calculation, so the heuristic must
107123
# prune enough nodes to offset those costs. In practice this
108-
# is almost always the case if their are any large open areas
124+
# is almost always the case if their are any large _open areas
109125
# (nodes with many connected nodes).
110126

111-
# Rather than simply expanding nodes that are on the open list
127+
# Rather than simply expanding nodes that are on the _open list
112128
# based on how close they are to the start, we will expand based
113129
# on how much distance we predict is between the start and end
114130
# node IF we go through that parent. That is a combination of
@@ -120,15 +136,19 @@ def find_path(self, graph, start, end, heuristic_fn):
120136

121137
counter = 0
122138
heur = heuristic_fn(graph, start, end)
123-
open_lookup[start] = { 'vertex': start, 'dist_start_to_here': 0, 'pred_dist_here_to_end': heur, 'pred_total_dist': heur, 'parent': None }
124-
heapq.heappush(open, (heur, counter, start))
139+
_open_lookup[start] = {'vertex': start,
140+
'dist_start_to_here': 0,
141+
'pred_dist_here_to_end': heur,
142+
'pred_total_dist': heur,
143+
'parent': None}
144+
heapq.heappush(_open, (heur, counter, start))
125145
counter += 1
126146

127-
while len(open) > 0:
128-
current = heapq.heappop(open)
147+
while len(_open) > 0:
148+
current = heapq.heappop(_open)
129149
current_vertex = current[2]
130-
current_dict = open_lookup[current_vertex]
131-
del open_lookup[current_vertex]
150+
current_dict = _open_lookup[current_vertex]
151+
del _open_lookup[current_vertex]
132152
closed.update(current_vertex)
133153

134154
if current_vertex == end:
@@ -142,8 +162,10 @@ def find_path(self, graph, start, end, heuristic_fn):
142162
# node first.
143163
continue
144164

145-
cost_start_to_neighbor = current_dict['dist_start_to_here'] + graph.get_edge_weight(current_vertex, neighbor)
146-
neighbor_from_lookup = open_lookup.get(neighbor, None) # avoid searching twice
165+
cost_start_to_neighbor = current_dict['dist_start_to_here'] \
166+
+ graph.get_edge_weight(current_vertex, neighbor)
167+
# avoid searching twice
168+
neighbor_from_lookup = _open_lookup.get(neighbor, None)
147169
if neighbor_from_lookup is not None:
148170
# If our heuristic is NOT consistent or the grid is NOT uniform,
149171
# it is possible that there is a better path to a neighbor of a
@@ -158,50 +180,49 @@ def find_path(self, graph, start, end, heuristic_fn):
158180
if cost_start_to_neighbor < old_dist_start_to_neighbor:
159181
pred_dist_neighbor_to_end = neighbor_from_lookup['pred_dist_here_to_end']
160182
pred_total_dist_through_neighbor_to_end = cost_start_to_neighbor + pred_dist_neighbor_to_end
161-
# Note, we've already shown that neighbor (the vector) is already in the open list,
183+
# Note, we've already shown that neighbor (the vector) is already in the _open list,
162184
# but unfortunately we don't know where and we have to do a slow lookup to fix the
163185
# key its sorting by to the new predicted total distance.
164186

165187
# In case we're using a fancy debugger we want to search in user-code so when
166188
# this lookup freezes we can see how much longer its going to take.
167189
found = None
168-
for i in range(0, len(open)):
169-
if open[i][2] == neighbor:
190+
for i in range(0, len(_open)):
191+
if _open[i][2] == neighbor:
170192
found = i
171193
break
172194
if found is None:
173-
raise Exception('A vertex is in the open lookup but not in open. This is impossible, please submit an issue + include the graph!')
174-
# todo I'm not certain about the performance characteristics of doing this with heapq, nor if
175-
# it would be better to delete heapify and push or rather than replace
176-
open[i] = (pred_total_dist_through_neighbor_to_end, counter, neighbor)
195+
raise Exception('A vertex is in the _open lookup but not in _open. '
196+
'This is impossible, please submit an issue + include the graph!')
197+
# TODO: I'm not certain about the performance characteristics of doing this with heapq, nor if
198+
# TODO: it would be better to delete heapify and push or rather than replace
199+
200+
# TODO: Local variable 'i' could be referenced before assignment
201+
_open[i] = (pred_total_dist_through_neighbor_to_end, counter, neighbor)
177202
counter += 1
178-
heapq.heapify(open)
179-
open_lookup[neighbor] = { 'vertex': neighbor,
203+
heapq.heapify(_open)
204+
_open_lookup[neighbor] = {'vertex': neighbor,
180205
'dist_start_to_here': cost_start_to_neighbor,
181206
'pred_dist_here_to_end': pred_dist_neighbor_to_end,
182207
'pred_total_dist': pred_total_dist_through_neighbor_to_end,
183-
'parent': current_dict }
208+
'parent': current_dict}
184209
continue
185-
186-
210+
187211
# We've found the first possible way to the path!
188212
pred_dist_neighbor_to_end = heuristic_fn(graph, neighbor, end)
189213
pred_total_dist_through_neighbor_to_end = cost_start_to_neighbor + pred_dist_neighbor_to_end
190-
heapq.heappush(open, (pred_total_dist_through_neighbor_to_end, counter, neighbor))
191-
open_lookup[neighbor] = { 'vertex': neighbor,
214+
heapq.heappush(_open, (pred_total_dist_through_neighbor_to_end, counter, neighbor))
215+
_open_lookup[neighbor] = {'vertex': neighbor,
192216
'dist_start_to_here': cost_start_to_neighbor,
193217
'pred_dist_here_to_end': pred_dist_neighbor_to_end,
194218
'pred_total_dist': pred_total_dist_through_neighbor_to_end,
195-
'parent': current_dict }
219+
'parent': current_dict}
196220

197221
return None
198222

199223
@staticmethod
200-
def get_code(self):
224+
def get_code():
201225
"""
202226
returns the code for the current class
203227
"""
204228
return inspect.getsource(OneDirectionalAStar)
205-
206-
207-

0 commit comments

Comments
 (0)