diff --git a/.travis.yml b/.travis.yml index afa3c2f..74caa90 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,21 +5,56 @@ python: 3.7 # Install ruby to get gem command before_install: - - sudo apt-add-repository -y ppa:brightbox/ruby-ng - - sudo apt-get -y update - - sudo apt-get -y install ruby-full + - python --version + - pip install -U pip + - pip install -U pytest + - pip install codecov + - sudo apt-add-repository -y ppa:brightbox/ruby-ng + - sudo apt-get -y update + - sudo apt-get -y install ruby-full before_script: - - gem install awesome_bot + - gem install awesome_bot -script: - - awesome_bot README.md --allow-dupe --allow-redirect - - python Algorithm_tests/dynamic_programming_tests/knapsack_tests/knapsack_bottomup_test.py - - python Algorithm_tests/dynamic_programming_tests/sequence_alignment/sequence_alignment_test.py - - python Algorithm_tests/graphtheory_tests/bellman_ford_test.py - - python Algorithm_tests/other_tests/test_binarysearch.py - - python Algorithm_tests/math_tests/intersection_test.py - - python Algorithm_tests/math_tests/union_test.py - - python Algorithm_tests/cryptology_tests/ceasar_test.py - - python Algorithm_tests/other_tests/test_intervalscheduling.py - - python Algorithm_tests/sorting_tests/test_sorting.py +install: + - pip3 install -r requirements.txt + - pip3 install pytest + - pip3 install pytest-cov + - pip3 install codecov + +script: + - awesome_bot README.md --allow-dupe --allow-redirect + - pytest --cov=Algorithms/ + + # # Dynamic Programming Tests + # - python Algorithm_tests/dynamic_programming_tests/knapsack_tests/knapsack_bottomup_test.py + # - python Algorithm_tests/dynamic_programming_tests/sequence_alignment/sequence_alignment_test.py + # - python Algorithm_tests/dynamic_programming_tests/weighted_interval_scheduling/weighted_interval_scheduling_test.py + # + # # Graph Theory Tests + # - python Algorithm_tests/graphtheory_tests/bellman_ford_test.py + # - python Algorithm_tests/graphtheory_tests/kahn_topological_ordering_test.py + # - python Algorithm_tests/graphtheory_tests/Djikstra/djikstra_heap_test.py + # - python Algorithm_tests/graphtheory_tests/Djikstra/djikstra_naive_test.py + # - python Algorithm_tests/graphtheory_tests/kruskal_unionfind_test.py + # - python Algorithm_tests/graphtheory_tests/prims_algorithm_test.py + # - python Algorithm_tests/graphtheory_tests/BFS_test.py + # - python Algorithm_tests/graphtheory_tests/DFS_test.py + # + # # Math tests + # - python Algorithm_tests/other_tests/test_binarysearch.py + # - python Algorithm_tests/math_tests/intersection_test.py + # - python Algorithm_tests/math_tests/union_test.py + # + # # Cryptography tests + # - python Algorithm_tests/cryptology_tests/ceasar_test.py + # + # # "Other" tests + # - python Algorithm_tests/other_tests/test_medianmaintenance.py + # - python Algorithm_tests/other_tests/test_intervalscheduling.py + # + # # Sorting tests + # - python Algorithm_tests/sorting_tests/test_sorting.py + +after_success: + - codecov diff --git a/Algorithm_tests/cryptology_tests/ceasar_test.py b/Algorithm_tests/cryptology_tests/ceasar_test.py index 0956a50..bffff78 100644 --- a/Algorithm_tests/cryptology_tests/ceasar_test.py +++ b/Algorithm_tests/cryptology_tests/ceasar_test.py @@ -4,27 +4,25 @@ # For importing from different folders # OBS: This is supposed to be done with automated testing, hence relative to folder we want to import from -sys.path.append('Algorithms/cryptology/ceasar_shifting_cipher') +sys.path.append("Algorithms/cryptology/ceasar_shifting_cipher") # If run from local: -#sys.path.append('../../Algorithms/cryptology/ceasar_shifting_cipher') +# sys.path.append('../../Algorithms/cryptology/ceasar_shifting_cipher') from ceasar_shift_cipher import encrypt, decrypt # Note this is not robust.. but im trying to make it a habit to make some tests. # Some are better than nothing. But these are not complete at all. class test_ceasar_cipher(unittest.TestCase): - def setUp(self): # test cases we wish to run - self.message1 = 'abc' + self.message1 = "abc" self.shift1 = 3 - self.correct_encrypt1 = 'def' + self.correct_encrypt1 = "def" - self.message2 = 'xyz ' + self.message2 = "xyz " self.shift2 = 1 - self.correct_encrypt2 = 'yz a' - + self.correct_encrypt2 = "yz a" def test_encryption_message1(self): encrypted_message1 = encrypt(self.message1, self.shift1) @@ -42,6 +40,7 @@ def test_decryption_message2(self): decrypted_message2 = decrypt(self.correct_encrypt2, self.shift2) self.assertEqual(decrypted_message2, self.message2) -if __name__ == '__main__': + +if __name__ == "__main__": print("Running ceasar cipher tests:") - unittest.main() \ No newline at end of file + unittest.main() diff --git a/Algorithm_tests/dynamic_programming_tests/knapsack_tests/knapsack_bottomup_test.py b/Algorithm_tests/dynamic_programming_tests/knapsack_tests/knapsack_bottomup_test.py index f286196..79a415e 100644 --- a/Algorithm_tests/dynamic_programming_tests/knapsack_tests/knapsack_bottomup_test.py +++ b/Algorithm_tests/dynamic_programming_tests/knapsack_tests/knapsack_bottomup_test.py @@ -3,12 +3,13 @@ # For importing from different folders # OBS: This is supposed to be done with automated testing, hence relative to folder we want to import from -sys.path.append('Algorithms/dynamic_programming/knapsack') +sys.path.append("Algorithms/dynamic_programming/knapsack") # If run from local: -#sys.path.append('../../../Algorithms/dynamic_programming/knapsack/') +# sys.path.append('../../../Algorithms/dynamic_programming/knapsack/') from knapsack_bottomup import knapsack + class test_KnapSack(unittest.TestCase): def setUp(self): self.weights1, self.values1, self.capacity1 = [], [], 100 @@ -23,7 +24,11 @@ def setUp(self): self.n3 = len(self.weights2) self.correctvalue3, self.correctitems3 = 0, [] - self.weights4, self.values4, self.capacity4 = [1, 2, 4, 2, 5], [5, 3, 5, 3, 2], 5 + self.weights4, self.values4, self.capacity4 = ( + [1, 2, 4, 2, 5], + [5, 3, 5, 3, 2], + 5, + ) self.n4 = len(self.weights4) self.correctvalue4, self.correctitems4 = 11, [0, 1, 3] @@ -32,30 +37,41 @@ def setUp(self): self.correctvalue5, self.correctitems5 = 0, [] def test_noitems(self): - total_value, items = knapsack(self.n1, self.capacity1, self.weights1, self.values1) + total_value, items = knapsack( + self.n1, self.capacity1, self.weights1, self.values1 + ) self.assertEqual(self.correctvalue1, total_value) self.assertEqual(self.correctitems1, items) def test_singleitem_value(self): - total_value, items = knapsack(self.n2, self.capacity2, self.weights2, self.values2) + total_value, items = knapsack( + self.n2, self.capacity2, self.weights2, self.values2 + ) self.assertEqual(self.correctvalue2, total_value) self.assertEqual(self.correctitems2, items) def test_negativevalues(self): - total_value, items = knapsack(self.n3, self.capacity3, self.weights3, self.values3) + total_value, items = knapsack( + self.n3, self.capacity3, self.weights3, self.values3 + ) self.assertEqual(self.correctvalue3, total_value) self.assertEqual(self.correctitems3, items) def test_simpleexample(self): - total_value, items = knapsack(self.n4, self.capacity4, self.weights4, self.values4) + total_value, items = knapsack( + self.n4, self.capacity4, self.weights4, self.values4 + ) self.assertEqual(self.correctvalue4, total_value) self.assertEqual(self.correctitems4, items) def test_weight_too_heavy(self): - total_value, items = knapsack(self.n5, self.capacity5, self.weights5, self.values5) + total_value, items = knapsack( + self.n5, self.capacity5, self.weights5, self.values5 + ) self.assertEqual(self.correctvalue5, total_value) self.assertEqual(self.correctitems5, items) -if __name__ == '__main__': + +if __name__ == "__main__": print("Running Knapsack tests:") - unittest.main() \ No newline at end of file + unittest.main() diff --git a/Algorithm_tests/dynamic_programming_tests/sequence_alignment/sequence_alignment_test.py b/Algorithm_tests/dynamic_programming_tests/sequence_alignment/sequence_alignment_test.py index 6e0165b..7e58b5b 100644 --- a/Algorithm_tests/dynamic_programming_tests/sequence_alignment/sequence_alignment_test.py +++ b/Algorithm_tests/dynamic_programming_tests/sequence_alignment/sequence_alignment_test.py @@ -3,57 +3,58 @@ # For importing from different folders # OBS: This is supposed to be done with automated testing, hence relative to folder we want to import from -sys.path.append('Algorithms/dynamic_programming/') +sys.path.append("Algorithms/dynamic_programming/") # If run from local: -#sys.path.append('../../../Algorithms/dynamic_programming/') +# sys.path.append('../../../Algorithms/dynamic_programming/') from sequence_alignment import SequenceAlignment + class test_sequence_alignment(unittest.TestCase): def setUp(self): - self.x1 = 'ABC' - self.y1 = 'ADC' + self.x1 = "ABC" + self.y1 = "ADC" self.correct_editstep1 = 1 - self.x2 = 'AB' - self.y2 = 'A' + self.x2 = "AB" + self.y2 = "A" self.correct_editstep2 = 1 - self.x3 = 'A' - self.y3 = '' + self.x3 = "A" + self.y3 = "" self.correct_editstep3 = 1 - self.x4 = 'ABC' - self.y4 = 'ABCDE' + self.x4 = "ABC" + self.y4 = "ABCDE" self.correct_editstep4 = 2 - self.x5 = 'ABCKL' - self.y5 = 'ADCE' + self.x5 = "ABCKL" + self.y5 = "ADCE" self.correct_editstep5 = 3 - self.x6 = 'A'*10 - self.y6 = '' + self.x6 = "A" * 10 + self.y6 = "" self.correct_editstep6 = 10 - self.x7 = '' - self.y7 = 'A' * 10 + self.x7 = "" + self.y7 = "A" * 10 self.correct_editstep7 = 10 - self.x8 = 'TGACGTGC' - self.y8 = 'TCGACGTCA' + self.x8 = "TGACGTGC" + self.y8 = "TCGACGTCA" self.correct_editstep8 = 3 - self.x9 = 'XYZ' - self.y9 = 'XKZ' - self.correct_solution9 = ['align_X', 'align_K', 'align_Z'] + self.x9 = "XYZ" + self.y9 = "XKZ" + self.correct_solution9 = ["align_X", "align_K", "align_Z"] - self.x10 = 'XX' - self.y10 = '' - self.correct_solution10 = ['remove_X', 'remove_X'] + self.x10 = "XX" + self.y10 = "" + self.correct_solution10 = ["remove_X", "remove_X"] - self.x11 = '' - self.y11 = 'XX' - self.correct_solution11 = ['insert_X', 'insert_X'] + self.x11 = "" + self.y11 = "XX" + self.correct_solution11 = ["insert_X", "insert_X"] def test_simplecase(self): sequence_align = SequenceAlignment(self.x1, self.y1) @@ -110,6 +111,7 @@ def test_findsolution_empty_x(self): _, solution = sequence_align.alignment() self.assertEqual(self.correct_solution11, solution) -if __name__ == '__main__': + +if __name__ == "__main__": print("Running Sequence Alignment tests:") - unittest.main() \ No newline at end of file + unittest.main() diff --git a/Algorithm_tests/dynamic_programming_tests/weighted_interval_scheduling/weighted_interval_scheduling_test.py b/Algorithm_tests/dynamic_programming_tests/weighted_interval_scheduling/weighted_interval_scheduling_test.py index aeaa145..d384097 100644 --- a/Algorithm_tests/dynamic_programming_tests/weighted_interval_scheduling/weighted_interval_scheduling_test.py +++ b/Algorithm_tests/dynamic_programming_tests/weighted_interval_scheduling/weighted_interval_scheduling_test.py @@ -3,38 +3,106 @@ # For importing from different folders # OBS: This is supposed to be done with automated testing, hence relative to folder we want to import from -#sys.path.append('Algorithms/dynamic_programming/') +sys.path.append("Algorithms/dynamic_programming/") # If run from local: -sys.path.append('../../../Algorithms/dynamic_programming/') +# sys.path.append('../../../Algorithms/dynamic_programming/') from weighted_interval_scheduling import WeightedIntervalScheduling + class test_weighted_interval_scheduling(unittest.TestCase): def setUp(self): self.I1 = [] self.correct_maxweight1 = 0 + self.correct_intervals1 = [] - self.I2 = [(0,3,10)] + self.I2 = [(0, 3, 10)] self.correct_maxweight2 = 10 + self.correct_intervals2 = [(0, 3, 10)] + + self.I3 = [(0, 3, 5), (2, 5, 15), (4, 6, 5)] + self.correct_maxweight3 = 15 + self.correct_intervals3 = [(2, 5, 15)] + + self.I4 = [(0, 3, 5), (3, 5, 15), (5, 7, 5)] + self.correct_maxweight4 = 25 + self.correct_intervals4 = [(0, 3, 5), (3, 5, 15), (5, 7, 5)] + + self.I5 = [(0, 3, 5), (3, 5, -100), (5, 7, -50)] + self.correct_maxweight5 = 5 + self.correct_intervals5 = [(0, 3, 5)] + + self.I6 = [(0, 50, 1), (0, 49, 1), (0, 48, 1), (15, 20, 10)] + self.correct_maxweight6 = 10 + self.correct_intervals6 = [(15, 20, 10)] + + self.I7 = [(0, 50, 1), (0, 50, 1), (0, 50, 1), (0, 50, 1)] + self.correct_maxweight7 = 1 + self.correct_intervals7 = [(0, 50, 1)] - self.I3 = [(0,3,5), (2,5,15), (4, 6, 5)] - self.correct_maxweight3 = 5 + self.I8 = [(0, 50, 1), (0, 49, 1), (0, 48, 1), (0, 47, 1)] + self.correct_maxweight8 = 1 + self.correct_intervals8 = [(0, 47, 1)] + + self.I9 = [(0, 50, 2), (0, 49, 1), (0, 48, 1), (0, 47, 1)] + self.correct_maxweight9 = 2 + self.correct_intervals9 = [(0, 50, 2)] def test_empty_interval(self): weightedinterval = WeightedIntervalScheduling(self.I1) max_weight, best_intervals = weightedinterval.weighted_interval() self.assertEqual(self.correct_maxweight1, max_weight) + self.assertEqual(self.correct_intervals1, best_intervals) def test_single_interval(self): weightedinterval = WeightedIntervalScheduling(self.I2) max_weight, best_intervals = weightedinterval.weighted_interval() self.assertEqual(self.correct_maxweight2, max_weight) + self.assertEqual(self.correct_intervals2, best_intervals) def test_overlapping_intervals(self): - weightedinterval = WeightedIntervalScheduling(self.I2) + weightedinterval = WeightedIntervalScheduling(self.I3) max_weight, best_intervals = weightedinterval.weighted_interval() - self.assertEqual(self.correct_maxweight2, max_weight) + self.assertEqual(self.correct_maxweight3, max_weight) + self.assertEqual(self.correct_intervals3, best_intervals) + + def test_no_overlapping_intervals(self): + weightedinterval = WeightedIntervalScheduling(self.I4) + max_weight, best_intervals = weightedinterval.weighted_interval() + self.assertEqual(self.correct_maxweight4, max_weight) + self.assertEqual(self.correct_intervals4, best_intervals) + + def test_negative_weights(self): + weightedinterval = WeightedIntervalScheduling(self.I5) + max_weight, best_intervals = weightedinterval.weighted_interval() + self.assertEqual(self.correct_maxweight5, max_weight) + self.assertEqual(self.correct_intervals5, best_intervals) + + def test_interval_contained_in_all_intervals(self): + weightedinterval = WeightedIntervalScheduling(self.I6) + max_weight, best_intervals = weightedinterval.weighted_interval() + self.assertEqual(self.correct_maxweight6, max_weight) + self.assertEqual(self.correct_intervals6, best_intervals) + + def test_all_intervals_same(self): + weightedinterval = WeightedIntervalScheduling(self.I7) + max_weight, best_intervals = weightedinterval.weighted_interval() + self.assertEqual(self.correct_maxweight7, max_weight) + self.assertEqual(self.correct_intervals7, best_intervals) + + def test_earliest_finish_time(self): + weightedinterval = WeightedIntervalScheduling(self.I8) + max_weight, best_intervals = weightedinterval.weighted_interval() + self.assertEqual(self.correct_maxweight8, max_weight) + self.assertEqual(self.correct_intervals8, best_intervals) + + def test_earliest_finish_time_not_best(self): + weightedinterval = WeightedIntervalScheduling(self.I9) + max_weight, best_intervals = weightedinterval.weighted_interval() + self.assertEqual(self.correct_maxweight9, max_weight) + self.assertEqual(self.correct_intervals9, best_intervals) + -if __name__ == '__main__': +if __name__ == "__main__": print("Running Weighted Interval Scheduling tests:") - unittest.main() \ No newline at end of file + unittest.main() diff --git a/Algorithm_tests/graphtheory_tests/BFS_test.py b/Algorithm_tests/graphtheory_tests/BFS_test.py new file mode 100644 index 0000000..3495a97 --- /dev/null +++ b/Algorithm_tests/graphtheory_tests/BFS_test.py @@ -0,0 +1,77 @@ +# Import folder where sorting algorithms +import sys +import unittest +from collections import deque + +# For importing from different folders +# OBS: This is supposed to be done with automated testing, hence relative to folder we want to import from +sys.path.append("Algorithms/graphtheory/breadth-first-search/") + +# If run from local: +# sys.path.append('../../Algorithms/graphtheory/breadth-first-search/') +from BFS_queue_iterative import BFS + + +class test_BFS(unittest.TestCase): + def setUp(self): + self.G1 = {1: [2], 2: [1, 3], 3: [2]} + self.correct_visited1 = [True] * 3 + self.correct_path1 = [1, 2, 3] + + self.G2 = {1: [2], 2: [1, 3, 4], 3: [2], 4: [2, 5], 5: [4]} + self.correct_visited2 = [True] * 5 + + self.G3 = {1: [2], 2: [1, 3, 4], 3: [2], 4: [2], 5: []} + self.correct_visited3 = [True] * 4 + [False] + + self.G4 = {1: [2, 3, 4], 2: [1, 3, 4], 3: [1, 2, 4], 4: [1, 2, 3]} + self.correct_visited4 = [True] * 4 + + self.G5 = { + 1: [2, 3, 4], + 2: [1, 5], + 3: [1, 7], + 4: [1, 6], + 5: [2], + 6: [4], + 7: [3], + } + self.correct_visited5 = [True] * 7 + + def test_linear_graph(self): + visited, path = BFS(self.G1, start_node=1) + self.assertEqual(visited, self.correct_visited1) + self.assertEqual(path, self.correct_path1) + + def test_simple_graph(self): + visited, path = BFS(self.G2, start_node=1) + self.assertTrue(path.index(3) < path.index(5)) + self.assertEqual(visited, self.correct_visited2) + + def test_disconnected_graph(self): + visited, path = BFS(self.G3, start_node=1) + self.assertEqual(visited, self.correct_visited3) + + def test_complete_graph(self): + visited, path = BFS(self.G4, start_node=1) + self.assertEqual(visited, self.correct_visited4) + + def test_breadth_before_depth(self): + visited, path = BFS(self.G5, start_node=1) + self.assertEqual(visited, self.correct_visited5) + + # Make sure it goes breadth first + self.assertTrue(path.index(2) < path.index(5)) + self.assertTrue(path.index(2) < path.index(6)) + self.assertTrue(path.index(2) < path.index(7)) + self.assertTrue(path.index(3) < path.index(5)) + self.assertTrue(path.index(3) < path.index(6)) + self.assertTrue(path.index(3) < path.index(7)) + self.assertTrue(path.index(4) < path.index(5)) + self.assertTrue(path.index(4) < path.index(6)) + self.assertTrue(path.index(4) < path.index(7)) + + +if __name__ == "__main__": + print("Running BFS/DFS tests:") + unittest.main() diff --git a/Algorithm_tests/graphtheory_tests/DFS_test.py b/Algorithm_tests/graphtheory_tests/DFS_test.py new file mode 100644 index 0000000..00518ca --- /dev/null +++ b/Algorithm_tests/graphtheory_tests/DFS_test.py @@ -0,0 +1,67 @@ +# Import folder where sorting algorithms +import sys +import unittest +from collections import deque + +# For importing from different folders +# OBS: This is supposed to be done with automated testing, hence relative to folder we want to import from +sys.path.append("Algorithms/graphtheory/depth-first-search/") + +# If run from local: +# sys.path.append('../../Algorithms/graphtheory/depth-first-search/') +from DFS_recursive import DFS as DFS_rec +from DFS_stack_iterative import DFS as DFS_stack + + +class test_DFS(unittest.TestCase): + def setUp(self): + self.G1 = {1: [2], 2: [1, 3], 3: [2]} + self.correct_visited1 = [True] * 3 + self.correct_path1 = [1, 2, 3] + self.DFS_recursive_visited1 = [False for i in range(1, len(self.G1) + 1)] + + self.G2 = {1: [2], 2: [1, 3, 4], 3: [2], 4: [2, 5], 5: [4]} + self.correct_visited2 = [True] * 5 + self.DFS_recursive_visited2 = [False for i in range(1, len(self.G2) + 1)] + + self.G3 = {1: [2], 2: [1, 3, 4], 3: [2], 4: [2], 5: []} + self.correct_visited3 = [True] * 4 + [False] + self.DFS_recursive_visited3 = [False for i in range(1, len(self.G3) + 1)] + + self.G4 = {1: [2, 3, 4], 2: [1, 3, 4], 3: [1, 2, 4], 4: [1, 2, 3]} + self.correct_visited4 = [True] * 4 + self.DFS_recursive_visited4 = [False for i in range(1, len(self.G4) + 1)] + + def test_linear_graph(self): + visited, path = DFS_stack(self.G1, start_node=1) + self.assertEqual(visited, self.correct_visited1) + self.assertEqual(path, self.correct_path1) + + DFS_rec(self.G1, 1, self.DFS_recursive_visited1) + self.assertEqual(self.DFS_recursive_visited1, self.correct_visited1) + + def test_simple_graph(self): + visited, path = DFS_stack(self.G2, start_node=1) + self.assertEqual(visited, self.correct_visited2) + + DFS_rec(self.G2, 1, self.DFS_recursive_visited2) + self.assertEqual(self.DFS_recursive_visited2, self.correct_visited2) + + def test_disconnected_graph(self): + visited, path = DFS_stack(self.G3, start_node=1) + self.assertEqual(visited, self.correct_visited3) + + DFS_rec(self.G3, 1, self.DFS_recursive_visited3) + self.assertEqual(self.DFS_recursive_visited3, self.correct_visited3) + + def test_complete_graph(self): + visited, path = DFS_stack(self.G4, start_node=1) + self.assertEqual(visited, self.correct_visited4) + + DFS_rec(self.G4, 1, self.DFS_recursive_visited4) + self.assertEqual(self.DFS_recursive_visited4, self.correct_visited4) + + +if __name__ == "__main__": + print("Running BFS/DFS tests:") + unittest.main() diff --git a/Algorithm_tests/graphtheory_tests/Djikstra/djikstra_heap_test.py b/Algorithm_tests/graphtheory_tests/Djikstra/djikstra_heap_test.py new file mode 100644 index 0000000..417e8f7 --- /dev/null +++ b/Algorithm_tests/graphtheory_tests/Djikstra/djikstra_heap_test.py @@ -0,0 +1,56 @@ +# Import folder where sorting algorithms +import sys +import unittest + +# For importing from different folders +# OBS: This is supposed to be done with automated testing, hence relative to folder we want to import from +sys.path.append("Algorithms/graphtheory/dijkstra/") + +# If run from local: +# sys.path.append('../../../Algorithms/graphtheory/dijkstra/') + +from heapdijkstra import dijkstra + + +class test_Dijkstra(unittest.TestCase): + def setUp(self): + self.G1 = {} + self.correct_path1 = [] + self.correct_dist1 = float("inf") + + self.G2 = {1: {2: 1, 4: 10}, 2: {3: 15}, 3: {6: 5}, 4: {5: 1}, 5: {6: 1}, 6: {}} + self.correct_path2 = [1, 4, 5, 6] + self.correct_dist2 = 12 + + self.G3 = {1: {2: 1, 4: 10}, 2: {3: 15}, 3: {}, 4: {5: 1}, 5: {}, 6: {}} + self.correct_path3 = [] + self.correct_dist3 = float("inf") + + self.G4 = {1: {2: 1, 4: 10}, 2: {3: 15}, 3: {6: 5}, 4: {5: 1}, 5: {6: 1}, 6: {}} + self.correct_path4 = [1, 4, 5] + self.correct_dist4 = 11 + + def test_emptygraph(self): + path_to_take, distances = dijkstra(self.G1, 0, 1) + self.assertEqual(distances[1], self.correct_dist1) + self.assertEqual(path_to_take, self.correct_path1) + + def test_simplegraph(self): + path_to_take, distances = dijkstra(self.G2, 1, 6) + self.assertEqual(distances[6], self.correct_dist2) + self.assertEqual(path_to_take, self.correct_path2) + + def test_no_path_exists(self): + path_to_take, distances = dijkstra(self.G3, 1, 6) + self.assertEqual(distances[6], self.correct_dist3) + self.assertEqual(path_to_take, self.correct_path3) + + def test_not_endpoint(self): + path_to_take, distances = dijkstra(self.G4, 1, 5) + self.assertEqual(distances[5], self.correct_dist4) + self.assertEqual(path_to_take, self.correct_path4) + + +if __name__ == "__main__": + print("Running Djikstra tests:") + unittest.main() diff --git a/Algorithm_tests/graphtheory_tests/Djikstra/djikstra_naive_test.py b/Algorithm_tests/graphtheory_tests/Djikstra/djikstra_naive_test.py new file mode 100644 index 0000000..557aeeb --- /dev/null +++ b/Algorithm_tests/graphtheory_tests/Djikstra/djikstra_naive_test.py @@ -0,0 +1,56 @@ +# Import folder where sorting algorithms +import sys +import unittest + +# For importing from different folders +# OBS: This is supposed to be done with automated testing, hence relative to folder we want to import from +sys.path.append("Algorithms/graphtheory/dijkstra/") + +# If run from local: +# sys.path.append('../../../Algorithms/graphtheory/dijkstra/') + +from dijkstra import dijkstra + + +class test_Dijkstra(unittest.TestCase): + def setUp(self): + self.G1 = {} + self.correct_path1 = [] + self.correct_dist1 = float("inf") + + self.G2 = {1: {2: 1, 4: 10}, 2: {3: 15}, 3: {6: 5}, 4: {5: 1}, 5: {6: 1}, 6: {}} + self.correct_path2 = [1, 4, 5, 6] + self.correct_dist2 = 12 + + self.G3 = {1: {2: 1, 4: 10}, 2: {3: 15}, 3: {}, 4: {5: 1}, 5: {}, 6: {}} + self.correct_path3 = [] + self.correct_dist3 = float("inf") + + self.G4 = {1: {2: 1, 4: 10}, 2: {3: 15}, 3: {6: 5}, 4: {5: 1}, 5: {6: 1}, 6: {}} + self.correct_path4 = [1, 4, 5] + self.correct_dist4 = 11 + + def test_emptygraph(self): + path_to_take, distance = dijkstra(self.G1, 0, 1) + self.assertEqual(distance, self.correct_dist1) + self.assertEqual(path_to_take, self.correct_path1) + + def test_simplegraph(self): + path_to_take, distance = dijkstra(self.G2, 1, 6) + self.assertEqual(distance, self.correct_dist2) + self.assertEqual(path_to_take, self.correct_path2) + + def test_no_path_exists(self): + path_to_take, distance = dijkstra(self.G3, 1, 6) + self.assertEqual(distance, self.correct_dist3) + self.assertEqual(path_to_take, self.correct_path3) + + def test_not_endpoint(self): + path_to_take, distance = dijkstra(self.G4, 1, 5) + self.assertEqual(distance, self.correct_dist4) + self.assertEqual(path_to_take, self.correct_path4) + + +if __name__ == "__main__": + print("Running Djikstra tests:") + unittest.main() diff --git a/Algorithm_tests/graphtheory_tests/bellman_ford_test.py b/Algorithm_tests/graphtheory_tests/bellman_ford_test.py index fcec78c..ce27f8d 100644 --- a/Algorithm_tests/graphtheory_tests/bellman_ford_test.py +++ b/Algorithm_tests/graphtheory_tests/bellman_ford_test.py @@ -4,37 +4,34 @@ # For importing from different folders # OBS: This is supposed to be done with automated testing, hence relative to folder we want to import from -sys.path.append('Algorithms/graphtheory/bellman-ford') +sys.path.append("Algorithms/graphtheory/bellman-ford") # If run from local: -#sys.path.append('../../Algorithms/graphtheory/bellman-ford') +# sys.path.append('../../Algorithms/graphtheory/bellman-ford') -from bellman_ford import bellman_ford, make_graph +from bellman_ford import bellman_ford -class test_BellmanFord(unittest.TestCase): - - def test_loadgraph(self): - correct_graph = {1: {2: 5, 3: 10}, 2: {4: -5}, 3: {4: 15}} - graph = make_graph('Algorithm_tests/graphtheory_tests/test_graph.txt') - - self.assertEqual(graph, correct_graph) - - def test_loadmissingfile(self): - with self.assertRaises(IOError): - graph = make_graph('this_file_doesnt_exist.txt') +class test_BellmanFord(unittest.TestCase): def test_negativecycle(self): # Because of negative cycles, we shall denote the shortest path for these # as -infinity. - G = {1: {2: 5, 3: 20}, 2: {4: 10}, 3: {5: 10}, 4:{}, 5: {6: 5}, 6: {3: -20}} - - correct_shortest_dist = {1: 0, 2: 5, 3: -float('inf'), 4: 15, 5: -float('inf'), 6: -float('inf')} + G = {1: {2: 5, 3: 20}, 2: {4: 10}, 3: {5: 10}, 4: {}, 5: {6: 5}, 6: {3: -20}} + + correct_shortest_dist = { + 1: 0, + 2: 5, + 3: -float("inf"), + 4: 15, + 5: -float("inf"), + 6: -float("inf"), + } shortest_dist, _ = bellman_ford(G, 1) self.assertEqual(shortest_dist, correct_shortest_dist) def test_shortestdist(self): - G = {1:{2:100, 3:5}, 2: {4:20}, 3:{2:10}, 4:{}} + G = {1: {2: 100, 3: 5}, 2: {4: 20}, 3: {2: 10}, 4: {}} start_node = 1 shortest_dist, _ = bellman_ford(G, start_node) @@ -47,7 +44,7 @@ def test_shortestdist(self): self.assertEqual(shortest_dist[3], 5) self.assertEqual(shortest_dist[4], 35) - def test_emptygraph(self): + def test_run_emptygraph(self): G = {} start_node = 1 @@ -56,6 +53,6 @@ def test_emptygraph(self): shortest_dist, _ = bellman_ford(G, start_node) -if __name__ == '__main__': +if __name__ == "__main__": print("Running bellman ford tests:") - unittest.main() \ No newline at end of file + unittest.main() diff --git a/Algorithm_tests/graphtheory_tests/kahn_topological_ordering_test.py b/Algorithm_tests/graphtheory_tests/kahn_topological_ordering_test.py new file mode 100644 index 0000000..70b2279 --- /dev/null +++ b/Algorithm_tests/graphtheory_tests/kahn_topological_ordering_test.py @@ -0,0 +1,73 @@ +# Import folder where sorting algorithms +import sys +import unittest + +# For importing from different folders +# OBS: This is supposed to be done with automated testing, hence relative to folder we want to import from +sys.path.append("Algorithms/graphtheory/kahns-toposort/") + +# If run from local: +# sys.path.append('../../Algorithms/graphtheory/kahns-toposort') + +from kahn_topological_ordering import topological_ordering +from collections import defaultdict + + +class test_TopologicalOrdering(unittest.TestCase): + def setUp(self): + self.G1 = {} + self.degree_incoming1 = defaultdict(int, {}) + self.correct_isDAG1 = False + self.correct_path1 = [] + + self.G2 = {"1": ["2"], "2": ["3"], "3": ["4"], "4": ["5"], "5": []} + self.degree_incoming2 = defaultdict(int, {"2": 1, "3": 1, "4": 1, "5": 1}) + self.correct_isDAG2 = True + self.correct_path2 = ["1", "2", "3", "4", "5"] + + self.G3 = { + "1": ["2", "3", "4", "5"], + "2": ["3", "4", "5"], + "3": ["4", "5"], + "4": ["5"], + "5": [], + } + self.degree_incoming3 = defaultdict(int, {"2": 1, "3": 2, "4": 3, "5": 4}) + self.correct_isDAG3 = True + self.correct_path3 = ["1", "2", "3", "4", "5"] + + self.G4 = { + "1": ["2", "3", "4", "5"], + "2": ["3", "4", "5"], + "3": ["2", "4", "5"], + "4": ["5"], + "5": [], + } + self.degree_incoming4 = defaultdict(int, {"2": 2, "3": 2, "4": 3, "5": 4}) + self.correct_isDAG4 = False + self.correct_path4 = [] + + def test_emptygraph(self): + path_to_take, is_DAG = topological_ordering(self.G1, self.degree_incoming1) + self.assertEqual(path_to_take, self.correct_path1) + self.assertEqual(is_DAG, self.correct_isDAG1) + + def test_clear_ordering(self): + path_to_take, is_DAG = topological_ordering(self.G2, self.degree_incoming2) + self.assertEqual(path_to_take, self.correct_path2) + self.assertEqual(is_DAG, self.correct_isDAG2) + + def test_more_complicated_graph(self): + path_to_take, is_DAG = topological_ordering(self.G3, self.degree_incoming3) + self.assertEqual(path_to_take, self.correct_path3) + self.assertEqual(is_DAG, self.correct_isDAG3) + + def test_no_topological_ordering(self): + path_to_take, is_DAG = topological_ordering(self.G4, self.degree_incoming4) + self.assertEqual(path_to_take, self.correct_path4) + self.assertEqual(is_DAG, self.correct_isDAG4) + + +if __name__ == "__main__": + print("Running Topological Ordering tests:") + unittest.main() diff --git a/Algorithm_tests/graphtheory_tests/kruskal_unionfind_test.py b/Algorithm_tests/graphtheory_tests/kruskal_unionfind_test.py new file mode 100644 index 0000000..0aec006 --- /dev/null +++ b/Algorithm_tests/graphtheory_tests/kruskal_unionfind_test.py @@ -0,0 +1,90 @@ +# Import folder where sorting algorithms +import sys +import unittest +from unionfind import unionfind + +# For importing from different folders +# OBS: This is supposed to be done with automated testing, hence relative to folder we want to import from +sys.path.append("Algorithms/graphtheory/kruskal/") + +# If run from local: +# sys.path.append('../../Algorithms/graphtheory/kruskal/') +from kruskal_unionfind import kruskal + + +class test_Kruskal(unittest.TestCase): + def setUp(self): + # self.G1 = {1:[(10, 2, 1)], 2:[(10, 1, 2), (10, 3, 2)], 3:[(10, 2, 3)]} + self.G1 = [(10, 2, 1), (10, 1, 2), (10, 3, 2), (10, 2, 3)] + self.num_nodes1 = 3 + self.correct_cost1 = 20 + self.correct_MST1 = [(1, 2, 10), (2, 3, 10)] + + self.G2 = [ + (10, 2, 1), + (10, 3, 1), + (10, 1, 2), + (100, 3, 2), + (10, 1, 3), + (100, 2, 3), + ] + self.num_nodes2 = 3 + self.correct_cost2 = 20 + self.correct_MST2 = [(1, 2, 10), (1, 3, 10)] + + self.G3 = [ + (1, 2, 1), + (1, 1, 2), + (1, 3, 2), + (1, 4, 3), + (5, 5, 3), + (1, 3, 4), + (1, 5, 4), + (5, 3, 5), + (1, 4, 5), + ] + self.num_nodes3 = 5 + self.correct_cost3 = 4 + self.correct_MST3 = [(1, 2, 1), (2, 3, 1), (3, 4, 1), (4, 5, 1)] + + self.G4 = [(1, 2, 1), (1, 1, 2), (1, 4, 3), (1, 3, 4)] + self.num_nodes4 = 4 + self.correct_cost4 = 2 + self.correct_MST4 = [(1, 2, 1), (3, 4, 1)] + + self.G5 = {} + self.num_nodes5 = 0 + self.correct_cost5 = 0 + self.correct_MST5 = [] + + # Takes as input G which will have {node1: [(cost, to_node, node1), ...], node2:[(...)] } + + def test_linear_graph(self): + MST, cost = kruskal(sorted(self.G1, key=lambda tup: tup[0]), self.num_nodes1) + self.assertEqual(MST, self.correct_MST1) + self.assertEqual(cost, self.correct_cost1) + + def test_triangle_graph(self): + MST, cost = kruskal(sorted(self.G2, key=lambda tup: tup[0]), self.num_nodes2) + self.assertEqual(MST, self.correct_MST2) + self.assertEqual(cost, self.correct_cost2) + + def test_trickier_mst(self): + MST, cost = kruskal(sorted(self.G3, key=lambda tup: tup[0]), self.num_nodes3) + self.assertEqual(MST, self.correct_MST3) + self.assertEqual(cost, self.correct_cost3) + + def test_disconnected_graph(self): + MST, cost = kruskal(sorted(self.G4, key=lambda tup: tup[0]), self.num_nodes4) + self.assertEqual(MST, self.correct_MST4) + self.assertEqual(cost, self.correct_cost4) + + def test_empty_graph(self): + MST, cost = kruskal(self.G5, self.num_nodes5) + self.assertEqual(MST, self.correct_MST5) + self.assertEqual(cost, self.correct_cost5) + + +if __name__ == "__main__": + print("Running Kruskal tests:") + unittest.main() diff --git a/Algorithm_tests/graphtheory_tests/prims_algorithm_test.py b/Algorithm_tests/graphtheory_tests/prims_algorithm_test.py new file mode 100644 index 0000000..8ffebdb --- /dev/null +++ b/Algorithm_tests/graphtheory_tests/prims_algorithm_test.py @@ -0,0 +1,80 @@ +# Import folder where sorting algorithms +import sys +import unittest + +# For importing from different folders +# OBS: This is supposed to be done with automated testing, hence relative to folder we want to import from +sys.path.append("Algorithms/graphtheory/prims/") + +# If run from local: +# sys.path.append('../../Algorithms/graphtheory/prims/') +from prim_heap import prims_algo + + +class test_primsHeap(unittest.TestCase): + def setUp(self): + # How I've decided to construct the graph is confusing, but the reason is because we're using a min heap and + # want first element in the tuple to be the cost of the edge. However when I return the MST, we want it to be + # returned as: from_node, to_node, edge_cost, rather than the reverse for constructing the graph. + + self.G1 = {1: [(10, 2, 1)], 2: [(10, 1, 2), (10, 3, 2)], 3: [(10, 2, 3)]} + self.correct_cost1 = 20 + self.correct_MST1 = [(1, 2, 10), (2, 3, 10)] + + self.G2 = { + 1: [(10, 2, 1), (10, 3, 1)], + 2: [(10, 1, 2), (100, 3, 2)], + 3: [(10, 1, 3), (100, 2, 3)], + } + self.correct_cost2 = 20 + self.correct_MST2 = [(1, 2, 10), (1, 3, 10)] + + self.G3 = { + 1: [(1, 2, 1)], + 2: [(1, 1, 2), (1, 3, 2)], + 3: [(1, 4, 3), (5, 5, 3)], + 4: [(1, 3, 4), (1, 5, 4)], + 5: [(5, 3, 5), (1, 4, 5)], + } + self.correct_cost3 = 4 + self.correct_MST3 = [(1, 2, 1), (2, 3, 1), (3, 4, 1), (4, 5, 1)] + + self.G4 = {1: [(1, 2, 1)], 2: [(1, 1, 2)], 3: [(1, 4, 3)], 4: [(1, 3, 4)]} + self.correct_cost4 = 1 + self.correct_MST4 = [(1, 2, 1)] + + self.G5 = {} + self.correct_cost5 = 0 + self.correct_MST5 = [] + + # Takes as input G which will have {node1: [(cost, to_node, node1), ...], node2:[(...)] } + + def test_linear_graph(self): + MST, cost = prims_algo(self.G1, start=1) + self.assertEqual(MST, self.correct_MST1) + self.assertEqual(cost, self.correct_cost1) + + def test_triangle_graph(self): + MST, cost = prims_algo(self.G2, start=1) + self.assertEqual(MST, self.correct_MST2) + self.assertEqual(cost, self.correct_cost2) + + def test_trickier_mst(self): + MST, cost = prims_algo(self.G3, start=1) + self.assertEqual(MST, self.correct_MST3) + self.assertEqual(cost, self.correct_cost3) + + def test_disconnected_graph(self): + MST, cost = prims_algo(self.G4, start=1) + self.assertEqual(MST, self.correct_MST4) + self.assertEqual(cost, self.correct_cost4) + + def test_empty_graph(self): + MST, cost = prims_algo(self.G5, start=1) + self.assertEqual(MST, self.correct_MST5) + self.assertEqual(cost, self.correct_cost5) + + +if __name__ == "__main__": + print("Running Prims Heap tests:") + unittest.main() diff --git a/Algorithm_tests/graphtheory_tests/test_NN.py b/Algorithm_tests/graphtheory_tests/test_NN.py new file mode 100644 index 0000000..ad8322f --- /dev/null +++ b/Algorithm_tests/graphtheory_tests/test_NN.py @@ -0,0 +1,69 @@ +import unittest +import sys + +# Import from different folder +sys.path.append("Algorithms/graphtheory/nearest-neighbor-tsp/") + +import NearestNeighborTSP + + +class TestNN(unittest.TestCase): + def setUp(self): + self.G1 = [[0,3,-1],[3,0,1],[-1,1,0]] + self.correct_path1 = [0,1,2,0] + + # No possible solution for this one so its a dead end + self.G2 = [[0, 2, -1,-1,-1], [2, 0,5,1,-1], [-1, 5, 0, -1, -1],[-1, 1, -1, 0, 3], [-1, -1, -1, 3, 0]] + self.correct_path2 = [0,1,3,4] + + # No possible solution for this one so its a dead end + self.G3 = [[0, 2, -1,-1,-1], [2, 0,5,1,-1], [-1, 5, 0, -1, -1],[-1, 1, -1, 0, -1], [-1, -1, -1, -1, 0]] + self.correct_path3 = [0, 1, 3] + + # Multiple possible solutions + self.G4 = [[0,1,1,1],[1,0,1,1],[1,1,0,1],[1,1,1,0]] + self.correct_path4 = [0, 1, 2, 3, 0] + + + # adjacency matrix of a graph for testing + adjMatrix = [[0,2,5,-1,3],[2,0,2,4,-1],[5,2,0,5,5],[-1,4,5,0,2],[3,-1,5,2,0]] + # correct rank of each node's neighbors + correctNeighbors = [[1,4,2],[0,2,3],[1,0,3,4],[4,1,2],[3,0,2]] + + + def test_0_rankNeighbors(self): + for i in range(0,4): + self.assertEqual(NearestNeighborTSP.rankNeighbors(i, self.adjMatrix), self.correctNeighbors[i], "Check if order is different.") + + + def test_1_nnTSP(self): + path=NearestNeighborTSP.nnTSP(self.adjMatrix) + # Test if path is null + self.assertIsNotNone(path,"Output is empty") + # Test if path is not complete + self.assertEqual(len(path),len(self.adjMatrix)+1,"Path in incomplete") + + + def test_linear_graph(self): + #print(NearestNeighbor.nnTSP(self.G2)) + path = NearestNeighborTSP.nnTSP(self.G1) + self.assertEqual(path,self.correct_path1) + + + def test_simple_graph(self): + path = NearestNeighborTSP.nnTSP(self.G2) + self.assertEqual(path,self.correct_path2) + + + def test_disconnected_graph(self): + path = NearestNeighborTSP.nnTSP(self.G3) + self.assertEqual(path, self.correct_path3) + + + def test_complete_graph(self): + path = NearestNeighborTSP.nnTSP(self.G4) + self.assertEqual(path, self.correct_path4) + +if __name__ == '__main__': + print("Running Nearest Neighbor TSP solver tests:") + unittest.main() \ No newline at end of file diff --git a/Algorithm_tests/math_tests/intersection_test.py b/Algorithm_tests/math_tests/intersection_test.py index 9ad27ee..43efa50 100644 --- a/Algorithm_tests/math_tests/intersection_test.py +++ b/Algorithm_tests/math_tests/intersection_test.py @@ -4,7 +4,7 @@ # For importing from different folders # OBS: This is supposed to be done with automated testing, hence relative to folder we want to import from -sys.path.append('Algorithms/math/intersection_of_two_sets') +sys.path.append("Algorithms/math/intersection_of_two_sets") # If run from local: # sys.path.append('../../Algorithms/math/intersection_of_two_sets') @@ -13,30 +13,28 @@ class test_intersection(unittest.TestCase): - def setUp(self): # test cases we wish to run - self.L1 = [1,3,5,7,9,10] - self.L2 = [2,4,6,11,12] + self.L1 = [1, 3, 5, 7, 9, 10] + self.L2 = [2, 4, 6, 11, 12] self.L1L2_correct = [] - self.L3 = [1,3,5,10] - self.L4 = [2,4,6,10] + self.L3 = [1, 3, 5, 10] + self.L4 = [2, 4, 6, 10] self.L3L4_correct = [10] - self.L5 = [1,3,5,10] - self.L6 = [1,4,6,11] + self.L5 = [1, 3, 5, 10] + self.L6 = [1, 4, 6, 11] self.L5L6_correct = [1] - self.L7 = [1,2,3,4,5,6,7] - self.L8 = [1,2,3,4,5,6,7] - self.L7L8_correct = [1,2,3,4,5,6,7] + self.L7 = [1, 2, 3, 4, 5, 6, 7] + self.L8 = [1, 2, 3, 4, 5, 6, 7] + self.L7L8_correct = [1, 2, 3, 4, 5, 6, 7] self.L9 = [] self.L10 = [] self.L9L10_correct = [] - def test_intersection_none(self): L1L2_output = intersection(self.L1, self.L2) self.assertEqual(L1L2_output, self.L1L2_correct) @@ -57,6 +55,7 @@ def test_intersection_both_empty(self): L9L10_output = intersection(self.L9, self.L10) self.assertEqual(L9L10_output, self.L9L10_correct) -if __name__ == '__main__': + +if __name__ == "__main__": print("Running sorting tests:") - unittest.main() \ No newline at end of file + unittest.main() diff --git a/Algorithm_tests/math_tests/union_test.py b/Algorithm_tests/math_tests/union_test.py index 867f658..7d2d612 100644 --- a/Algorithm_tests/math_tests/union_test.py +++ b/Algorithm_tests/math_tests/union_test.py @@ -4,38 +4,37 @@ # For importing from different folders # OBS: This is supposed to be done with automated testing, hence relative to folder we want to import from -sys.path.append('Algorithms/math/union_of_two_sets') +sys.path.append("Algorithms/math/union_of_two_sets") # If run from local: # sys.path.append('../../Algorithms/math/union_of_two_sets') from union_of_two_sets import union -class test_union(unittest.TestCase): +class test_union(unittest.TestCase): def setUp(self): # test cases we wish to run - self.L1 = [1,3,5,7,9,10] - self.L2 = [2,4,6,11,12] - self.L1L2_correct = [1,2,3,4,5,6,7,9,10,11,12] + self.L1 = [1, 3, 5, 7, 9, 10] + self.L2 = [2, 4, 6, 11, 12] + self.L1L2_correct = [1, 2, 3, 4, 5, 6, 7, 9, 10, 11, 12] - self.L3 = [1,3,5,10] - self.L4 = [2,4,6,10] - self.L3L4_correct = [1,2,3,4,5,6,10] + self.L3 = [1, 3, 5, 10] + self.L4 = [2, 4, 6, 10] + self.L3L4_correct = [1, 2, 3, 4, 5, 6, 10] - self.L5 = [1,3,5,10] - self.L6 = [1,4,6,11] - self.L5L6_correct = [1,3,4,5,6,10,11] + self.L5 = [1, 3, 5, 10] + self.L6 = [1, 4, 6, 11] + self.L5L6_correct = [1, 3, 4, 5, 6, 10, 11] - self.L7 = [1,2,3,4,5,6,7] - self.L8 = [1,2,3,4,5,6,7] - self.L7L8_correct = [1,2,3,4,5,6,7] + self.L7 = [1, 2, 3, 4, 5, 6, 7] + self.L8 = [1, 2, 3, 4, 5, 6, 7] + self.L7L8_correct = [1, 2, 3, 4, 5, 6, 7] self.L9 = [] self.L10 = [] self.L9L10_correct = [] - def test_union_all(self): L1L2_output = union(self.L1, self.L2) self.assertEqual(L1L2_output, self.L1L2_correct) @@ -56,6 +55,7 @@ def test_union_both_empty(self): L9L10_output = union(self.L9, self.L10) self.assertEqual(L9L10_output, self.L9L10_correct) -if __name__ == '__main__': + +if __name__ == "__main__": print("Running sorting tests:") - unittest.main() \ No newline at end of file + unittest.main() diff --git a/Algorithm_tests/other_tests/test_binarysearch.py b/Algorithm_tests/other_tests/test_binarysearch.py index cfc376d..44f3ffe 100644 --- a/Algorithm_tests/other_tests/test_binarysearch.py +++ b/Algorithm_tests/other_tests/test_binarysearch.py @@ -4,16 +4,15 @@ # For importing from different folders # OBS: This is supposed to be done with automated testing, hence relative to folder we want to import from -sys.path.append('Algorithms/other') +sys.path.append("Algorithms/other") # If run from local: -#sys.path.append('../../Algorithms/other') +# sys.path.append('../../Algorithms/other') from binarysearch import binarysearch_iterative, binarysearch_recursive class test_binarysearch(unittest.TestCase): - def setUp(self): # test cases we wish to run self.L1 = [1, 3, 5, 8, 10, 12] @@ -24,13 +23,13 @@ def setUp(self): self.L2_target = 6 self.L2_correct = False, None - self.L3 = [1,1,1,1,1,1,1,1] + self.L3 = [1, 1, 1, 1, 1, 1, 1, 1] self.L3_target = 1 - self.L3_correct = True, (0+len(self.L3)-1)//2 + self.L3_correct = True, (0 + len(self.L3) - 1) // 2 self.L4 = [1, 3, 6, 11, 16, 21, 25, 27] self.L4_target = 27 - self.L4_correct = True, len(self.L4)-1 + self.L4_correct = True, len(self.L4) - 1 self.L5 = [1, 3, 6, 11, 16, 21, 27] self.L5_target = 1 @@ -40,61 +39,74 @@ def setUp(self): self.L6_target = 10 self.L6_correct = False, None - self.L7 = [11,12,15,19,23,41,173,298] + self.L7 = [11, 12, 15, 19, 23, 41, 173, 298] self.L7_target = 12 self.L7_correct = True, 1 - def test_binarysearch_basic(self): L1_result_iterative = binarysearch_iterative(self.L1, self.L1_target) - L1_result_recursive = binarysearch_recursive(self.L1, self.L1_target, 0, len(self.L1)-1) + L1_result_recursive = binarysearch_recursive( + self.L1, self.L1_target, 0, len(self.L1) - 1 + ) self.assertEqual(L1_result_iterative, self.L1_correct) self.assertEqual(L1_result_recursive, self.L1_correct) def test_binarysearch_nonexistant(self): L2_result_iterative = binarysearch_iterative(self.L2, self.L2_target) - L2_result_recursive = binarysearch_recursive(self.L2, self.L2_target, 0, len(self.L1)-1) + L2_result_recursive = binarysearch_recursive( + self.L2, self.L2_target, 0, len(self.L1) - 1 + ) self.assertEqual(L2_result_iterative, self.L2_correct) self.assertEqual(L2_result_recursive, self.L2_correct) def test_binarysearch_identical(self): L3_result_iterative = binarysearch_iterative(self.L3, self.L3_target) - L3_result_recursive = binarysearch_recursive(self.L3, self.L3_target, 0, len(self.L3) - 1) + L3_result_recursive = binarysearch_recursive( + self.L3, self.L3_target, 0, len(self.L3) - 1 + ) self.assertEqual(L3_result_iterative, self.L3_correct) self.assertEqual(L3_result_recursive, self.L3_correct) def test_binarysearch_lastvalue(self): L4_result_iterative = binarysearch_iterative(self.L4, self.L4_target) - L4_result_recursive = binarysearch_recursive(self.L4, self.L4_target, 0, len(self.L4) - 1) + L4_result_recursive = binarysearch_recursive( + self.L4, self.L4_target, 0, len(self.L4) - 1 + ) self.assertEqual(L4_result_iterative, self.L4_correct) self.assertEqual(L4_result_recursive, self.L4_correct) def test_binarysearch_firstvalue(self): L5_result_iterative = binarysearch_iterative(self.L5, self.L5_target) - L5_result_recursive = binarysearch_recursive(self.L5, self.L5_target, 0, len(self.L5) - 1) + L5_result_recursive = binarysearch_recursive( + self.L5, self.L5_target, 0, len(self.L5) - 1 + ) self.assertEqual(L5_result_iterative, self.L5_correct) self.assertEqual(L5_result_recursive, self.L5_correct) def test_binarysearch_empty(self): L6_result_iterative = binarysearch_iterative(self.L6, self.L6_target) - L6_result_recursive = binarysearch_recursive(self.L6, self.L6_target, 0, len(self.L6) - 1) + L6_result_recursive = binarysearch_recursive( + self.L6, self.L6_target, 0, len(self.L6) - 1 + ) self.assertEqual(L6_result_iterative, self.L6_correct) self.assertEqual(L6_result_recursive, self.L6_correct) def test_binarysearch_standard(self): L7_result_iterative = binarysearch_iterative(self.L7, self.L7_target) - L7_result_recursive = binarysearch_recursive(self.L7, self.L7_target, 0, len(self.L7) - 1) + L7_result_recursive = binarysearch_recursive( + self.L7, self.L7_target, 0, len(self.L7) - 1 + ) self.assertEqual(L7_result_iterative, self.L7_correct) self.assertEqual(L7_result_recursive, self.L7_correct) -if __name__ == '__main__': +if __name__ == "__main__": print("Running sorting tests:") - unittest.main() \ No newline at end of file + unittest.main() diff --git a/Algorithm_tests/other_tests/test_intervalscheduling.py b/Algorithm_tests/other_tests/test_intervalscheduling.py index a23d7e3..e24843a 100644 --- a/Algorithm_tests/other_tests/test_intervalscheduling.py +++ b/Algorithm_tests/other_tests/test_intervalscheduling.py @@ -4,33 +4,31 @@ # For importing from different folders # OBS: This is supposed to be done with automated testing, hence relative to folder we want to import from -sys.path.append('Algorithms/other') +sys.path.append("Algorithms/other") # If run from local: -#sys.path.append('../../Algorithms/other') +# sys.path.append('../../Algorithms/other') from interval_scheduling import interval_scheduling class test_intervalscheduling(unittest.TestCase): - def setUp(self): # test cases we wish to run - self.R1 = [(0, 5), (3, 6),(5, 10)] - self.R1_correct = [(0,5), (5,10)] + self.R1 = [(0, 5), (3, 6), (5, 10)] + self.R1_correct = [(0, 5), (5, 10)] self.R2 = [] self.R2_correct = [] - self.R3 = [(0, 3), (3,6), (6,9), (9, 10)] - self.R3_correct = [(0, 3), (3,6), (6,9), (9, 10)] + self.R3 = [(0, 3), (3, 6), (6, 9), (9, 10)] + self.R3_correct = [(0, 3), (3, 6), (6, 9), (9, 10)] self.R4 = [(1, 3), (0, 2), (1, 4), (2, 5)] - self.R4_correct = [(0,2), (2,5)] - - self.R5 = [(0,3)] - self.R5_correct = [(0,3)] + self.R4_correct = [(0, 2), (2, 5)] + self.R5 = [(0, 3)] + self.R5_correct = [(0, 3)] def test_intervalscheduling_basic(self): O = [] @@ -58,6 +56,6 @@ def test_intervalscheduling_one_element(self): self.assertEqual(O, self.R5_correct) -if __name__ == '__main__': +if __name__ == "__main__": print("Running Interval Scheduling tests:") - unittest.main() \ No newline at end of file + unittest.main() diff --git a/Algorithm_tests/other_tests/test_medianmaintenance.py b/Algorithm_tests/other_tests/test_medianmaintenance.py index 01c1e40..47e1f6c 100644 --- a/Algorithm_tests/other_tests/test_medianmaintenance.py +++ b/Algorithm_tests/other_tests/test_medianmaintenance.py @@ -1,12 +1,13 @@ # Import packages import sys import unittest + # For importing from different folders # OBS: This is supposed to be done with automated testing, hence relative to folder we want to import from -sys.path.append('Algorithms/other') +sys.path.append("Algorithms/other") # If run from local: -#sys.path.append('../../Algorithms/other') +# sys.path.append('../../Algorithms/other') from median_maintenance import Maintain_Median @@ -28,7 +29,6 @@ def setUp(self): self.data5 = [1, 10, 2, 9, 11, 4, 6, 5, 3, 8, 7] self.correct5 = 6 - def test_basic(self): maintain_median = Maintain_Median() median = maintain_median.main(self.data1) @@ -55,6 +55,5 @@ def test_longer_example(self): self.assertEqual(median, self.correct5) - -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/Algorithm_tests/sorting_tests/test_sorting.py b/Algorithm_tests/sorting_tests/test_sorting.py index 8d2fccf..1ebf09a 100644 --- a/Algorithm_tests/sorting_tests/test_sorting.py +++ b/Algorithm_tests/sorting_tests/test_sorting.py @@ -4,7 +4,7 @@ # For importing from different folders # OBS: This is supposed to be done with automated testing, hence relative to folder we want to import from -sys.path.append('Algorithms/sorting') +sys.path.append("Algorithms/sorting") # If run from local: # sys.path.append('../../Algorithms/sorting') @@ -12,34 +12,34 @@ from bubblesort import bubblesort from insertionsort import insertionsort from mergesort import merge_sort, merge -from quicksort import quicksort_firstpivot +from quicksort import quicksort_firstpivot, quicksort_lastpivot from randomized_quicksort import quicksort_randomized from selectionsort import selectionsort # Test cases we wish to run -L1 = [1,2,3,4,5,6,7,8,9] -L1_sorted = [1,2,3,4,5,6,7,8,9] +L1 = [1, 2, 3, 4, 5, 6, 7, 8, 9] +L1_sorted = [1, 2, 3, 4, 5, 6, 7, 8, 9] -L2 = [9,8,7,6,5,4,3,2,1] -L2_sorted = [1,2,3,4,5,6,7,8,9] +L2 = [9, 8, 7, 6, 5, 4, 3, 2, 1] +L2_sorted = [1, 2, 3, 4, 5, 6, 7, 8, 9] -L3 = [1,1,1,1,1,1,1,1,1] -L3_sorted = [1,1,1,1,1,1,1,1,1] +L3 = [1, 1, 1, 1, 1, 1, 1, 1, 1] +L3_sorted = [1, 1, 1, 1, 1, 1, 1, 1, 1] -L4 = [6,7,3,5,1,3] -L4_sorted = [1,3,3,5,6,7] +L4 = [6, 7, 3, 5, 1, 3] +L4_sorted = [1, 3, 3, 5, 6, 7] L5 = [] L5_sorted = [] -L6 = [-1,-2,-3] -L6_sorted = [-3,-2,-1] +L6 = [-1, -2, -3] +L6_sorted = [-3, -2, -1] -L7 = [-5,-7,-1,-3,-4] -L7_sorted = [-7,-5,-4,-3,-1] +L7 = [-5, -7, -1, -3, -4] +L7_sorted = [-7, -5, -4, -3, -1] -class test_sorting(unittest.TestCase): +class test_sorting(unittest.TestCase): def test_bubblesort(self): self.assertEqual(bubblesort(L1), L1_sorted) self.assertEqual(bubblesort(L2), L2_sorted) @@ -77,6 +77,14 @@ def test_quicksort(self): self.assertEqual(quicksort_firstpivot(L6), L6_sorted) self.assertEqual(quicksort_firstpivot(L7), L7_sorted) + self.assertEqual(quicksort_lastpivot(L1), L1_sorted) + self.assertEqual(quicksort_lastpivot(L2), L2_sorted) + self.assertEqual(quicksort_lastpivot(L3), L3_sorted) + self.assertEqual(quicksort_lastpivot(L4), L4_sorted) + self.assertEqual(quicksort_lastpivot(L5), L5_sorted) + self.assertEqual(quicksort_lastpivot(L6), L6_sorted) + self.assertEqual(quicksort_lastpivot(L7), L7_sorted) + def test_selectionsort(self): self.assertEqual(selectionsort(L1), L1_sorted) self.assertEqual(selectionsort(L2), L2_sorted) @@ -96,6 +104,6 @@ def test_quicksort_randomized(self): self.assertEqual(quicksort_randomized(L7), L7_sorted) -if __name__ == '__main__': +if __name__ == "__main__": print("Running sorting tests:") - unittest.main() \ No newline at end of file + unittest.main() diff --git a/Algorithms/cryptology/RSA_algorithm/RSA.py b/Algorithms/cryptology/RSA_algorithm/RSA.py index 04e3891..8231bde 100644 --- a/Algorithms/cryptology/RSA_algorithm/RSA.py +++ b/Algorithms/cryptology/RSA_algorithm/RSA.py @@ -1,16 +1,17 @@ -''' +""" Purpose of the RSA cryptosystem is to have a secure way of transmitting data Programmed by Aladdin Persson * 2019-08-26 Initial programming -''' +""" from math import gcd from sympy import isprime import random from euclid_gcd import extended_euclidean + def generate_pq(bits): # Randomly generate two primes p,q p = random.getrandbits(bits) @@ -21,10 +22,10 @@ def generate_pq(bits): q_isprime = isprime(q) # Keep generating until both are primes - while (not (p_isprime and q_isprime)): - if (not p_isprime): + while not (p_isprime and q_isprime): + if not p_isprime: p = random.getrandbits(bits) - if (not q_isprime): + if not q_isprime: q = random.getrandbits(bits) p_isprime = isprime(p) @@ -32,6 +33,7 @@ def generate_pq(bits): return p, q + def generate_e(totient): # Generate e such that 1 < e < phi(n) # phi(n) in this case is totient @@ -39,36 +41,39 @@ def generate_e(totient): while True: # Should be (2,totient) so if it is stuck in infinite loop then restart or replace 80000 -> totient # Reason why I want e to be a low value is to make encryption faster - e = random.randint(2,50000) + e = random.randint(2, 50000) if gcd(e, totient) == 1: return e + def generate_d(e, totient): _, e_inverse, _ = extended_euclidean(e, totient) - d = (e_inverse % totient) + d = e_inverse % totient return d + def generate_all_values(): num_bits = 1024 - p,q = generate_pq(num_bits) - totient = (p-1) * (q-1) + p, q = generate_pq(num_bits) + totient = (p - 1) * (q - 1) e = generate_e(totient) - d = generate_d(e,totient) + d = generate_d(e, totient) + + print("Generated value n: " + str(p * q)) + print("Generated e and d: " + str(e) + " and " + str(d)) - print('Generated value n: ' + str(p*q)) - print('Generated e and d: ' + str(e) + ' and ' + str(d)) + return p * q, e, d - return p*q,e,d def encrypt(message, n, e): - encrypted = '' + encrypted = "" for letter in message: pad = 3 - len(str(ord(letter))) if pad > 0: - new_letter = '0' * pad + str(ord(letter)) + new_letter = "0" * pad + str(ord(letter)) else: new_letter = ord(letter) @@ -77,12 +82,13 @@ def encrypt(message, n, e): encrypted = pow(int(encrypted), e, n) return encrypted + def decrypt(encrypted, n, d): - decrypted_message = '' + decrypted_message = "" decrypted_code = str(pow(encrypted, d, n)) if len(decrypted_code) % 3 != 0: - decrypted_code = '0' + decrypted_code + decrypted_code = "0" + decrypted_code while len(decrypted_code): decrypted_message += chr(int(decrypted_code[0:3])) @@ -90,37 +96,42 @@ def decrypt(encrypted, n, d): return decrypted_message + def example(): # An example of a test case where we generate all necessary primes, encrypt and then decrypt the message. # Only to show how all parts of the code is working. This is not how it's going to be used in practice. hidden_message = "i really love peanuts" - n,e,d = generate_all_values() + n, e, d = generate_all_values() encrypted_message = encrypt(hidden_message, n, e) decrypted_message = decrypt(encrypted_message, n, d) - print('\n') - print('Original message: ' + hidden_message) - print('Encrypted message: ' + str(encrypted_message)) - print('Decrypted message: ' + decrypted_message) + print("\n") + print("Original message: " + hidden_message) + print("Encrypted message: " + str(encrypted_message)) + print("Decrypted message: " + decrypted_message) + def main(): # Write the values of your RSA encryption (Note: Never give the 'd' to someone that doesn't want it) n = 354089397494626050014776605732143027269473328409397973403863001639624332101789181044818951483060155060788030618162673282176493895463414816015601230408140046833172059490430968956729878861381343553446553025440156523477822105773362480000716985478565013956749662865189691539813391686696182702224364834273144673717742246537383454469146642154754778836797926780437490677663302034284308892191362266103193070200405420180296005388479418941723827243187899338980201782128797489464650981164232057548015010630986959083998487019465357524040595865260220030689502065850060761344148196291328192760801074939658292752592564874822996765430361631210613041006858858506787439506504448316606509551260553919757840169593791152166515571202450662850988377002989153080277915454500432640601643512909764636398157415600050468972065216354878984114648007494687081718749734915103155014825420081658864982423629447913147575382146725524407739875786801876011026010419782863232303861065841801863420557617962438178979549855959377311548527613240676904989886382444381261628076009466895878852398923237601309285642954207693266358989851324012643315688180744573155217352955083176785543099571257338683938756048920161738393295253775030232399282686809347027784971441882883201432807953 e = 9361 - #d = + # d = - enc_or_dec = input("Would you like to encrypt or decrypt a message (input: 'enc' or 'dec'): ") + enc_or_dec = input( + "Would you like to encrypt or decrypt a message (input: 'enc' or 'dec'): " + ) - if enc_or_dec.lower() == 'enc': + if enc_or_dec.lower() == "enc": hidden_message = input("What is your hidden message?: ") - print('Encrypted message: ' + str(encrypt(hidden_message, n, e))) + print("Encrypted message: " + str(encrypt(hidden_message, n, e))) - elif enc_or_dec.lower() == 'dec': - encrypted = input('What is your encrypted message?: ') + elif enc_or_dec.lower() == "dec": + encrypted = input("What is your encrypted message?: ") print(encrypted) - print('Decrypted message: ' + str(decrypt(int(encrypted), n, d))) + print("Decrypted message: " + str(decrypt(int(encrypted), n, d))) else: print("Not sure what you typed") -main() \ No newline at end of file + +main() diff --git a/Algorithms/cryptology/RSA_algorithm/euclid_gcd.py b/Algorithms/cryptology/RSA_algorithm/euclid_gcd.py index 769353d..ec153c7 100644 --- a/Algorithms/cryptology/RSA_algorithm/euclid_gcd.py +++ b/Algorithms/cryptology/RSA_algorithm/euclid_gcd.py @@ -1,20 +1,22 @@ -''' +""" -''' +""" import sys + sys.setrecursionlimit(100000) + def extended_euclidean(a, b): if a == 0: return (b, 0, 1) else: - gcd, x, y = extended_euclidean(b % a, a) - return (gcd, y - (b//a) * x, x) + gcd, x, y = extended_euclidean(b % a, a) + return (gcd, y - (b // a) * x, x) -if __name__ == '__main__': - print(extended_euclidean(5,-2772)) - #print(extended_euclidean(13, 2640)) +if __name__ == "__main__": + print(extended_euclidean(5, -2772)) + # print(extended_euclidean(13, 2640)) diff --git a/Algorithms/cryptology/ceasar_shifting_cipher/ceasar_shift_cipher.py b/Algorithms/cryptology/ceasar_shifting_cipher/ceasar_shift_cipher.py index 06efa87..c9f0623 100644 --- a/Algorithms/cryptology/ceasar_shifting_cipher/ceasar_shift_cipher.py +++ b/Algorithms/cryptology/ceasar_shifting_cipher/ceasar_shift_cipher.py @@ -1,4 +1,4 @@ -''' +""" The Ceasar cipher is one of the simplest and one of the earliest known ciphers. It is a type of substitution cipher that 'shifts' a letter by a fixed amount in the alphabet. @@ -13,16 +13,17 @@ Programmed by Aladdin Persson * 2019-11-07 Initial programming -''' +""" # This alphabet is of 27 letters since I included a space, but normally it is of 26 letters. # If you wish to include more letters you need to expand the alphabet used. For example you cannot use '!', '@' now. -alphabet = 'abcdefghijklmnopqrstuvwxyz ' +alphabet = "abcdefghijklmnopqrstuvwxyz " letter_to_index = dict(zip(alphabet, range(len(alphabet)))) index_to_letter = dict(zip(range(len(alphabet)), alphabet)) + def encrypt(message, shift=3): - cipher = '' + cipher = "" for letter in message: number = (letter_to_index[letter] + shift) % len(letter_to_index) @@ -31,8 +32,9 @@ def encrypt(message, shift=3): return cipher + def decrypt(cipher, shift=3): - decrypted = '' + decrypted = "" for letter in cipher: number = (letter_to_index[letter] - shift) % len(letter_to_index) @@ -41,13 +43,14 @@ def decrypt(cipher, shift=3): return decrypted -def main(): - message = 'attackatnoon' - cipher = encrypt(message, shift=3) - decrypted = decrypt(cipher, shift=3) - - print('Original message: ' + message) - print('Encrypted message: ' + cipher) - print('Decrypted message: ' + decrypted) -main() \ No newline at end of file +# def main(): +# message = 'attackatnoon' +# cipher = encrypt(message, shift=3) +# decrypted = decrypt(cipher, shift=3) +# +# print('Original message: ' + message) +# print('Encrypted message: ' + cipher) +# print('Decrypted message: ' + decrypted) +# +# main() diff --git a/Algorithms/cryptology/hill_cipher/hill_cipher.py b/Algorithms/cryptology/hill_cipher/hill_cipher.py index 9aa57a0..5435d67 100644 --- a/Algorithms/cryptology/hill_cipher/hill_cipher.py +++ b/Algorithms/cryptology/hill_cipher/hill_cipher.py @@ -1,4 +1,4 @@ -''' +""" Implementation of Hill Cipher! Important notation: @@ -11,46 +11,53 @@ Programmed by Aladdin Persson * 2019-11-09 Initial programming -''' +""" import numpy as np -from egcd import egcd # pip install egcd +from egcd import egcd # pip install egcd -alphabet = 'abcdefghijklmnopqrstuvwxyz' +alphabet = "abcdefghijklmnopqrstuvwxyz" letter_to_index = dict(zip(alphabet, range(len(alphabet)))) index_to_letter = dict(zip(range(len(alphabet)), alphabet)) + def matrix_mod_inv(matrix, modulus): - '''We find the matrix modulus inverse by + """We find the matrix modulus inverse by Step 1) Find determinant Step 2) Find determinant value in a specific modulus (usually length of alphabet) Step 3) Take that det_inv times the det*inverted matrix (this will then be the adjoint) in mod 26 - ''' + """ - det = int(np.round(np.linalg.det(matrix))) # Step 1) + det = int(np.round(np.linalg.det(matrix))) # Step 1) det_inv = egcd(det, modulus)[1] % modulus # Step 2) - matrix_modulus_inv = det_inv * np.round(det*np.linalg.inv(matrix)).astype(int) % modulus # Step 3) + matrix_modulus_inv = ( + det_inv * np.round(det * np.linalg.inv(matrix)).astype(int) % modulus + ) # Step 3) return matrix_modulus_inv + def encrypt(message, K): - encrypted = '' + encrypted = "" message_in_numbers = [] for letter in message: message_in_numbers.append(letter_to_index[letter]) - split_P = [message_in_numbers[i:i + int(K.shape[0])] for i in range(0, len(message_in_numbers), int(K.shape[0]))] + split_P = [ + message_in_numbers[i : i + int(K.shape[0])] + for i in range(0, len(message_in_numbers), int(K.shape[0])) + ] for P in split_P: - P = np.transpose(np.asarray(P))[:,np.newaxis] + P = np.transpose(np.asarray(P))[:, np.newaxis] while P.shape[0] != K.shape[0]: - P = np.append(P, letter_to_index[' '])[:,np.newaxis] + P = np.append(P, letter_to_index[" "])[:, np.newaxis] numbers = np.dot(K, P) % len(alphabet) - n = numbers.shape[0] # length of encrypted message (in numbers) + n = numbers.shape[0] # length of encrypted message (in numbers) # Map back to get encrypted text for idx in range(n): @@ -59,17 +66,21 @@ def encrypt(message, K): return encrypted + def decrypt(cipher, Kinv): - decrypted = '' + decrypted = "" cipher_in_numbers = [] for letter in cipher: cipher_in_numbers.append(letter_to_index[letter]) - split_C = [cipher_in_numbers[i:i + int(Kinv.shape[0])] for i in range(0, len(cipher_in_numbers), int(Kinv.shape[0]))] + split_C = [ + cipher_in_numbers[i : i + int(Kinv.shape[0])] + for i in range(0, len(cipher_in_numbers), int(Kinv.shape[0])) + ] for C in split_C: - C = np.transpose(np.asarray(C))[:,np.newaxis] + C = np.transpose(np.asarray(C))[:, np.newaxis] numbers = np.dot(Kinv, C) % len(alphabet) n = numbers.shape[0] @@ -79,20 +90,22 @@ def decrypt(cipher, Kinv): return decrypted + def main(): - #message = 'my life is potato' - message = 'help' + # message = 'my life is potato' + message = "help" - K = np.matrix([[3,3],[2,5]]) + K = np.matrix([[3, 3], [2, 5]]) # K = np.matrix([[6, 24, 1], [13,16,10], [20,17,15]]) # for length of alphabet = 26 - #K = np.matrix([[3,10,20],[20,19,17], [23,78,17]]) # for length of alphabet = 27 + # K = np.matrix([[3,10,20],[20,19,17], [23,78,17]]) # for length of alphabet = 27 Kinv = matrix_mod_inv(K, len(alphabet)) encrypted_message = encrypt(message, K) decrypted_message = decrypt(encrypted_message, Kinv) - print('Original message: ' + message) - print('Encrypted message: ' + encrypted_message) - print('Decrypted message: ' + decrypted_message) + print("Original message: " + message) + print("Encrypted message: " + encrypted_message) + print("Decrypted message: " + decrypted_message) + -main() \ No newline at end of file +main() diff --git a/Algorithms/cryptology/one_time_pad/one_time_pad.py b/Algorithms/cryptology/one_time_pad/one_time_pad.py index ab85e70..a33156e 100644 --- a/Algorithms/cryptology/one_time_pad/one_time_pad.py +++ b/Algorithms/cryptology/one_time_pad/one_time_pad.py @@ -1,4 +1,4 @@ -''' +""" Implementation of the famous one time pad / Vernam Cipher In practice we need a way to generate random keys which I havn't included. @@ -6,58 +6,61 @@ Programmed by Aladdin Persson * 2019-11-12 Initial programming -''' +""" -def xor(s1,s2): + +def xor(s1, s2): xor_result = [] for i in range(min(len(s1), len(s2))): - xor_result.append(int(s1[i]) ^ int(s2[i])) # xor + xor_result.append(int(s1[i]) ^ int(s2[i])) # xor return xor_result + def encrypt(message, key): - binary_message = '' - binary_key = '' - ciphered_text = '' + binary_message = "" + binary_key = "" + ciphered_text = "" for letter in message: - binary_message += format(ord(letter), 'b') + binary_message += format(ord(letter), "b") for letter in key: - binary_key += format(ord(letter), 'b') + binary_key += format(ord(letter), "b") cipher_binary = xor(binary_message, binary_key) - return ''.join(str(e) for e in cipher_binary) + return "".join(str(e) for e in cipher_binary) + def decrypt(cipher_text, key): - binary_key = '' - decrypted_text = '' + binary_key = "" + decrypted_text = "" for letter in key: - binary_key += format(ord(letter), 'b') - + binary_key += format(ord(letter), "b") binary_message = xor(cipher_text, binary_key) - for i in range(0,len(binary_message),7): - letter = ''.join(str(e) for e in binary_message[i:i+7]) + for i in range(0, len(binary_message), 7): + letter = "".join(str(e) for e in binary_message[i : i + 7]) decrypted_text += chr(int(letter, 2)) return decrypted_text def main(): - message = 'cheesecake' # 'secret' message - key = 'randomrandomrandom' #'random' key + message = "cheesecake" # 'secret' message + key = "randomrandomrandom" #'random' key encrypted = encrypt(message, key) decrypted = decrypt(encrypted, key) - print('Original message: ' + str(message)) - print('Encrypted message (in binary): ' + str(encrypted)) - print('Decrypted message: ' + str(decrypted)) + print("Original message: " + str(message)) + print("Encrypted message (in binary): " + str(encrypted)) + print("Decrypted message: " + str(decrypted)) + -if __name__ == '__main__': - main() \ No newline at end of file +if __name__ == "__main__": + main() diff --git a/Algorithms/cryptology/vigenere_cipher/vigenere.py b/Algorithms/cryptology/vigenere_cipher/vigenere.py index 822c23f..69a71af 100644 --- a/Algorithms/cryptology/vigenere_cipher/vigenere.py +++ b/Algorithms/cryptology/vigenere_cipher/vigenere.py @@ -1,4 +1,4 @@ -''' +""" Vigenère cipher is one of the simplest that employs a form of polyalphabetic substitution (each letter is assigned more than one substitute). @@ -9,18 +9,20 @@ Programmed by Aladdin Persson * 2019-11-07 Initial programming -''' +""" -alphabet = 'abcdefghijklmnopqrstuvwxyz ' +alphabet = "abcdefghijklmnopqrstuvwxyz " letter_to_index = dict(zip(alphabet, range(len(alphabet)))) index_to_letter = dict(zip(range(len(alphabet)), alphabet)) def encrypt(message, key): - encrypted = '' - split_message = [message[i:i + len(key)] for i in range(0, len(message), len(key))] + encrypted = "" + split_message = [ + message[i : i + len(key)] for i in range(0, len(message), len(key)) + ] for each_split in split_message: i = 0 @@ -31,9 +33,12 @@ def encrypt(message, key): return encrypted + def decrypt(cipher, key): - decrypted = '' - split_encrypted = [cipher[i:i + len(key)] for i in range(0, len(cipher), len(key))] + decrypted = "" + split_encrypted = [ + cipher[i : i + len(key)] for i in range(0, len(cipher), len(key)) + ] for each_split in split_encrypted: i = 0 @@ -44,14 +49,16 @@ def decrypt(cipher, key): return decrypted + def main(): - message = 'i loove peanuts' - key = 'banana' + message = "i loove peanuts" + key = "banana" encrypted_message = encrypt(message, key) decrypted_message = decrypt(encrypted_message, key) - print('Original message: ' + message) - print('Encrypted message: ' + encrypted_message) - print('Decrypted message: ' + decrypted_message) + print("Original message: " + message) + print("Encrypted message: " + encrypted_message) + print("Decrypted message: " + decrypted_message) + -main() \ No newline at end of file +main() diff --git a/Algorithms/dynamic_programming/__pycache__/weighted_interval_scheduling.cpython-37.pyc b/Algorithms/dynamic_programming/__pycache__/weighted_interval_scheduling.cpython-37.pyc index 07306a3..0cc2427 100644 Binary files a/Algorithms/dynamic_programming/__pycache__/weighted_interval_scheduling.cpython-37.pyc and b/Algorithms/dynamic_programming/__pycache__/weighted_interval_scheduling.cpython-37.pyc differ diff --git a/Algorithms/dynamic_programming/knapsack/knapsack_bottomup.py b/Algorithms/dynamic_programming/knapsack/knapsack_bottomup.py index 423a187..f526381 100644 --- a/Algorithms/dynamic_programming/knapsack/knapsack_bottomup.py +++ b/Algorithms/dynamic_programming/knapsack/knapsack_bottomup.py @@ -1,57 +1,62 @@ -# Bottom up implementation of Knapsack (using loops) +""" +Purpose is if having a bunch of items with a weight and corresponding value to each object. +Which collection of objects should we choose such that we maximize the value restricted to +a specific capacity of weight. Bottom up implementation of Knapsack (using loops) -# Purpose is if having a bunch of items with a weight and corresponding value to each object. -# Which collection of objects should we choose such that we maximize the value restricted to -# a specific capacity of weight +Time Complexity: O(nC), pseudo-polynomial + +Programmed by Aladdin Persson + 2020-02-15 Initial programming +""" -# Programmed by Aladdin Persson -# 2020-02-15 Initial programming def find_opt(i, c, M, values, items, weights): if i <= 0 or c <= 0: return items - if (M[i-1][c] >= (values[i-1] + M[i-1][c - weights[i-1]])) or (c - weights[i-1]) < 0: - find_opt(i-1, c, M, values, items, weights) - + if (M[i - 1][c] >= (values[i - 1] + M[i - 1][c - weights[i - 1]])) or ( + c - weights[i - 1] + ) < 0: + find_opt(i - 1, c, M, values, items, weights) + else: - items.append(i-1) - find_opt(i-1, c-weights[i-1], M, values, items, weights) - + items.append(i - 1) + find_opt(i - 1, c - weights[i - 1], M, values, items, weights) + + def knapsack(n, C, weights, values): # Initialization of matrix of size (n*W) M = [[None for i in range(C + 1)] for j in range(len(values) + 1)] - for c in range(C+1): + for c in range(C + 1): M[0][c] = 0 - for i in range(len(weights)+1): + for i in range(len(weights) + 1): M[i][0] = 0 - for i in range(1, n+1): - for c in range(1, C+1): + for i in range(1, n + 1): + for c in range(1, C + 1): # If current weight exceeds capacity then we cannot take it if weights[i - 1] > c: - M[i][c] = M[i-1][c] + M[i][c] = M[i - 1][c] # Else we can take it, then find what gives us the optimal value, either # taking it or not taking it and we consider what gives us max value of those else: - M[i][c] = max(M[i-1][c], values[i-1] + M[i-1][c-weights[i-1]]) + M[i][c] = max(M[i - 1][c], values[i - 1] + M[i - 1][c - weights[i - 1]]) items = [] find_opt(n, C, M, values, items, weights) - return M[n][C], items[::-1] -if __name__ == '__main__': - # Run small example - weights = [1,2,4,2,5] - values = [5,3,5,3,2] - n = len(weights) - capacity = 3 - total_value, items = knapsack(n, capacity, weights, values) - print('Items at the end: ' + str(items)) - print('With total value: ' + str(total_value)) +# if __name__ == '__main__': +# # Run small example +# weights = [1,2,4,2,5] +# values = [5,3,5,3,2] +# n = len(weights) +# capacity = 3 +# total_value, items = knapsack(n, capacity, weights, values) +# print('Items at the end: ' + str(items)) +# print('With total value: ' + str(total_value)) diff --git a/Algorithms/dynamic_programming/knapsack/knapsack_memoization_recursive_topdown.py b/Algorithms/dynamic_programming/knapsack/knapsack_memoization_recursive_topdown.py index ba64803..d664aaf 100644 --- a/Algorithms/dynamic_programming/knapsack/knapsack_memoization_recursive_topdown.py +++ b/Algorithms/dynamic_programming/knapsack/knapsack_memoization_recursive_topdown.py @@ -8,36 +8,38 @@ # 2019-02-28 Initial programming # 2019-03-04 Made code cleaner and included a tracking of which items to choose + def knapsack(n, C, W, v, items, arr): # if n == 0 we cannot index further (since we look at n-1), further if we have no more capacity # then we cannot obtain more objects if n == 0 or C == 0: return 0, [] - elif arr[n-1][C-1] != None: - return arr[n-1][C-1], items + elif arr[n - 1][C - 1] != None: + return arr[n - 1][C - 1], items # If the weight is higher than our capacity then we can't pick it - elif W[n-1] > C: - result,items = knapsack(n-1,C, W, v, items, arr) + elif W[n - 1] > C: + result, items = knapsack(n - 1, C, W, v, items, arr) # Recursively search through all choices else: - tmp1,items1 = knapsack(n-1,C,W,v,items, arr) # exclude item - tmp2,items2 = knapsack(n-1,C - W[n-1], W, v, items, arr) # include item + tmp1, items1 = knapsack(n - 1, C, W, v, items, arr) # exclude item + tmp2, items2 = knapsack(n - 1, C - W[n - 1], W, v, items, arr) # include item items = items2 + [n - 1] if (tmp2 + v[n - 1] > tmp1) else items1 result = max(tmp1, tmp2 + v[n - 1]) - arr[n-1][C-1]=result + arr[n - 1][C - 1] = result return result, items -if __name__ == '__main__': + +if __name__ == "__main__": # Run a small example - weight = [1,2,4,2,5] - value = [5,3,5,3,2] + weight = [1, 2, 4, 2, 5] + value = [5, 3, 5, 3, 2] num_objects = len(weight) capacity = 3 diff --git a/Algorithms/dynamic_programming/knapsack/knapsack_naive_recursive.py b/Algorithms/dynamic_programming/knapsack/knapsack_naive_recursive.py index e580f3e..6eb1240 100644 --- a/Algorithms/dynamic_programming/knapsack/knapsack_naive_recursive.py +++ b/Algorithms/dynamic_programming/knapsack/knapsack_naive_recursive.py @@ -8,6 +8,7 @@ # 2019-02-28 Initial programming # 2019-03-04 Cleaned up code and included a tracking of which items to choose + def knapsack(n, C, W, v, items): # if n == 0 we cannot index further (since we look at n-1), further if we have no more capacity # then we cannot obtain more objects @@ -15,29 +16,29 @@ def knapsack(n, C, W, v, items): return 0, [] # If the weight is higher than our capacity then we can't pick it - elif W[n-1] > C: - result, items = knapsack(n-1,C, W, v, items) + elif W[n - 1] > C: + result, items = knapsack(n - 1, C, W, v, items) # Recursively search through all choices else: - tmp1, items1 = knapsack(n-1, C, W, v, items) # exclude item - tmp2, items2 = knapsack(n - 1, C - W[n - 1], W, v, items) # include item + tmp1, items1 = knapsack(n - 1, C, W, v, items) # exclude item + tmp2, items2 = knapsack(n - 1, C - W[n - 1], W, v, items) # include item - items=items2+[n-1] if (tmp2+v[n-1] > tmp1) else items1 + items = items2 + [n - 1] if (tmp2 + v[n - 1] > tmp1) else items1 - result=max(tmp1,tmp2+v[n-1]) + result = max(tmp1, tmp2 + v[n - 1]) return result, items -if __name__ == '__main__': +if __name__ == "__main__": # Run small example - weight = [1,2,4,2,5] - value = [5,3,5,3,2] + weight = [1, 2, 4, 2, 5] + value = [5, 3, 5, 3, 2] num_objects = len(weight) capacity = 3 arr = [[None for i in range(capacity)] for j in range(num_objects)] total_val_and_items = knapsack(num_objects, capacity, weight, value, []) - print(total_val_and_items)# items = [] + print(total_val_and_items) # items = [] diff --git a/Algorithms/dynamic_programming/longest_increasing_subsequence.py b/Algorithms/dynamic_programming/longest_increasing_subsequence.py index 7c3e189..8ac59f4 100644 --- a/Algorithms/dynamic_programming/longest_increasing_subsequence.py +++ b/Algorithms/dynamic_programming/longest_increasing_subsequence.py @@ -1,30 +1,30 @@ -''' +""" O(n^2) algorithm, can be faster and done in O(nlogn), but this works ok. To do: Create extensive test cases before adding to algorithm list. -''' +""" def longest_increasing_subsequence(nums): if len(nums) == 0: return 0 - + OPT = [1 for i in range(len(nums))] - + for i in range(1, len(nums)): for j in range(0, i): if nums[j] < nums[i] and OPT[j] + 1 > OPT[i]: OPT[i] = OPT[j] + 1 - + return max(OPT) -if __name__ == '__main__': +if __name__ == "__main__": # test1 = [1,5,-2,10, 50, -10, 10, 1,2,3,4] test2 = [10, 1, 2, 11, 3, 5] # test3 = [10,9,8,5,3,2,1,2,3] # test4 = [1,5,2,3,4,5,6] test5 = [] - + print(test2) - print(longest_increasing_subsequence(test2)) \ No newline at end of file + print(longest_increasing_subsequence(test2)) diff --git a/Algorithms/dynamic_programming/sequence_alignment.py b/Algorithms/dynamic_programming/sequence_alignment.py index 6441a8c..01775ae 100644 --- a/Algorithms/dynamic_programming/sequence_alignment.py +++ b/Algorithms/dynamic_programming/sequence_alignment.py @@ -1,17 +1,23 @@ -# Algorithm for solving sequence alignment -# Input strings x,y of len(x) = m, len(y) = n and find minimum number of -# edit steps and the specific steps to transform x into y. +""" +Algorithm for solving sequence alignment +Input strings x,y of len(x) = m, len(y) = n and find minimum number of +edit steps and the specific steps to transform x into y. -# Video of algorithm explanation: https://youtu.be/bQ7kRW6zo9Y -# Video of code explanation: https://youtu.be/XmyxiSc3LKg +Time Complexity: O(nm) + +Video of algorithm explanation: https://youtu.be/bQ7kRW6zo9Y +Video of code explanation: https://youtu.be/XmyxiSc3LKg + +Programmed by Aladdin Persson + 2020-02-15 Initial coding + 2020-02-16 Improved find_solution and made code cleaner + 2020-03-13 There was an error in the code in function find_solution, + I was working with list indexing as if it was a matrix. + Should be working now. Extensive testing would be good. + + 2020-03-28 Cleaned up code by making SequenceAlignment into class +""" -# Programmed by Aladdin Persson -# 2020-02-15 Initial coding -# 2020-02-16 Improved find_solution and made code cleaner -# 2020-03-13 There was an error in the code in function find_solution, -# I was working with list indexing as if it was a matrix. -# Should be working now. Extensive testing would be good. -# 2020-03-28 Cleaned up code by making SequenceAlignment into class class SequenceAlignment(object): def __init__(self, x, y): @@ -26,48 +32,57 @@ def find_solution(self, OPT, m, n): return # We can only do insert if n != 0, align if there are element in both x, y, etc. - insert = OPT[m][n-1] + 1 if n != 0 else float('inf') - align = OPT[m-1][n-1] + self.delta(self.x, self.y, m-1,n-1) if m != 0 and n != 0 else float('inf') - delete = OPT[m-1][n] + 1 if m != 0 else float('inf') + insert = OPT[m][n - 1] + 1 if n != 0 else float("inf") + align = ( + OPT[m - 1][n - 1] + self.delta(self.x, self.y, m - 1, n - 1) + if m != 0 and n != 0 + else float("inf") + ) + delete = OPT[m - 1][n] + 1 if m != 0 else float("inf") best_choice = min(insert, align, delete) if best_choice == insert: - self.solution.append('insert_'+str(self.y[n-1])) - return self.find_solution(OPT, m, n-1) + self.solution.append("insert_" + str(self.y[n - 1])) + return self.find_solution(OPT, m, n - 1) elif best_choice == align: - self.solution.append('align_' + str(self.y[n-1])) - return self.find_solution(OPT, m-1, n-1) + self.solution.append("align_" + str(self.y[n - 1])) + return self.find_solution(OPT, m - 1, n - 1) elif best_choice == delete: - self.solution.append('remove_'+str(self.x[m-1])) - return self.find_solution(OPT, m-1, n) + self.solution.append("remove_" + str(self.x[m - 1])) + return self.find_solution(OPT, m - 1, n) def alignment(self): n = len(self.y) m = len(self.x) - OPT = [ [0 for i in range(n+1)] for j in range(m+1)] + OPT = [[0 for i in range(n + 1)] for j in range(m + 1)] - for i in range(1,m+1): + for i in range(1, m + 1): OPT[i][0] = i - for j in range(1,n+1): + for j in range(1, n + 1): OPT[0][j] = j - for i in range(1,m+1): - for j in range(1,n+1): - OPT[i][j] = min(OPT[i-1][j-1] + self.delta(self.x,self.y,i-1,j-1), OPT[i-1][j] + 1, OPT[i][j-1] + 1) #align, delete, insert respectively + for i in range(1, m + 1): + for j in range(1, n + 1): + OPT[i][j] = min( + OPT[i - 1][j - 1] + self.delta(self.x, self.y, i - 1, j - 1), + OPT[i - 1][j] + 1, + OPT[i][j - 1] + 1, + ) # align, delete, insert respectively self.find_solution(OPT, m, n) return (OPT[m][n], self.solution[::-1]) -if __name__ == '__main__': - x = 'TGACGTGC' - y = 'TCGACGTCA' - print('We we want to transform: ' + x + ' to: ' + y) - sqalign = SequenceAlignment(x, y) - min_edit, steps = sqalign.alignment() - print('Minimum amount of edit steps are: ' + str(min_edit)) - print('And the way to do it is: ' + str(steps)) \ No newline at end of file + +# if __name__ == '__main__': +# x = 'TGACGTGC' +# y = 'TCGACGTCA' +# print('We we want to transform: ' + x + ' to: ' + y) +# sqalign = SequenceAlignment(x, y) +# min_edit, steps = sqalign.alignment() +# print('Minimum amount of edit steps are: ' + str(min_edit)) +# print('And the way to do it is: ' + str(steps)) diff --git a/Algorithms/dynamic_programming/weighted_interval_scheduling.py b/Algorithms/dynamic_programming/weighted_interval_scheduling.py index 3305773..e5bf456 100644 --- a/Algorithms/dynamic_programming/weighted_interval_scheduling.py +++ b/Algorithms/dynamic_programming/weighted_interval_scheduling.py @@ -1,18 +1,21 @@ -# Weighted Interval Scheduling -# Explained YouTube video: https://www.youtube.com/watch?v=iIX1YvbLbvc -# Implementation walkthrough video: https://www.youtube.com/watch?v=dU-coYsd7zw +""" +Weighted Interval Scheduling +Explained YouTube video: https://www.youtube.com/watch?v=iIX1YvbLbvc +Implementation walkthrough video: https://www.youtube.com/watch?v=dU-coYsd7zw -# Programmed by Aladdin Persson -# 2020-02-13 Initial programming -# 2020-03-28 Cleaned up code by making WeightedIntervalScheduling class +Programmed by Aladdin Persson + 2020-02-13 Initial programming + 2020-03-28 Cleaned up code by making WeightedIntervalScheduling class -# Time complexity: O(nlogn) +Time complexity: O(nlogn) +""" import bisect + class WeightedIntervalScheduling(object): def __init__(self, I): - self.I = sorted(I, key = lambda tup : tup[1]) #(key = lambda tup : tup[1]) + self.I = sorted(I, key=lambda tup: tup[1]) # (key = lambda tup : tup[1]) self.OPT = [] self.solution = [] @@ -31,8 +34,9 @@ def previous_intervals(self): def find_solution(self, j): if j == -1: return + else: - if (self.I[j][2] + self.OPT[self.p[j]]) >= self.OPT[j - 1]: + if (self.I[j][2] + self.compute_opt(self.p[j])) > self.compute_opt(j - 1): self.solution.append(self.I[j]) self.find_solution(self.p[j]) @@ -47,7 +51,9 @@ def compute_opt(self, j): return self.OPT[j] else: - return max(self.I[j][2] + self.compute_opt(self.p[j]), self.compute_opt(j - 1)) + return max( + self.I[j][2] + self.compute_opt(self.p[j]), self.compute_opt(j - 1) + ) def weighted_interval(self): if len(self.I) == 0: @@ -61,23 +67,22 @@ def weighted_interval(self): self.find_solution(len(self.I) - 1) - return self.OPT[-1], self.solution - -if __name__ == '__main__': - # They are labeled as: (start, end, weight) - t1 = (0,3,3) - t2 = (1,4,2) - t3 = (0,5,4) - t4 = (3,6,1) - t5 = (4,7,2) - t6 = (3,9,5) - t7 = (5,10,2) - t8 = (8,10,1) - I = [t1,t2,t3,t4,t5,t6,t7,t8] - - weightedinterval = WeightedIntervalScheduling(I) - max_weight, best_intervals = weightedinterval.weighted_interval() - - #max_weight = weighted_interval(I) - print('Maximum weight: ' + str(max_weight)) - print('The best items to take are: ' + str(best_intervals[::-1])) \ No newline at end of file + return self.OPT[-1], self.solution[::-1] + + +# Small Example +# if __name__ == '__main__': +# # They are labeled as: (start, end, weight) +# t1 = (0,3,3) +# t2 = (1,4,2) +# t3 = (0,5,4) +# t4 = (3,6,1) +# t5 = (4,7,2) +# t6 = (3,9,5) +# t7 = (5,10,2) +# t8 = (8,10,1) +# I = [t1,t2,t3,t4,t5,t6,t7,t8] +# weightedinterval = WeightedIntervalScheduling(I) +# max_weight, best_intervals = weightedinterval.weighted_interval() +# print('Maximum weight: ' + str(max_weight)) +# print('The best items to take are: ' + str(best_intervals)) diff --git a/Algorithms/graphtheory/bellman-ford/bellman_ford.py b/Algorithms/graphtheory/bellman-ford/bellman_ford.py index c7bd97f..82b462f 100644 --- a/Algorithms/graphtheory/bellman-ford/bellman_ford.py +++ b/Algorithms/graphtheory/bellman-ford/bellman_ford.py @@ -1,36 +1,28 @@ -''' +""" Purpose is to find the shortest path between one source node to all other nodes using Bellman-Ford Algorithm. The difference between Dijkstra and this is that this can handle negative edges. We do pay for this as it is a lot slower than Dijkstra. +Time Complexity: O(mn) + Programmed by Aladdin Persson 2019-03-04 Initial programming -''' - -def make_graph(file): - try: - f = open(file, 'r') - except IOError: - raise IOError("File does not exist!") - - line_list = f.readlines() - - G = {int(line.split()[0]): {(int(tup.split(',')[0])): int(tup.split(',')[1]) - for tup in line.split()[1:] if tup} for line in line_list if line} - - f.close() - - return G +""" def bellman_ford(G, start): + """ + :param G: {from_node1: {to_node1, cost1, to_node2, cost2}, from_node2: {etc}} + :param start: node to start from + """ + if len(G) == 0: raise ValueError("There should be something in the graph") # step1: initialize by setting to infinity etc. shortest_distance = {} predecessor = {} - infinity = float('inf') + infinity = float("inf") for node in G: shortest_distance[node] = infinity @@ -42,27 +34,25 @@ def bellman_ford(G, start): for _ in range(num_vertices - 1): for from_node in G: for to_node, weight in G[from_node].items(): - if shortest_distance[from_node] + weight < shortest_distance[to_node]: shortest_distance[to_node] = shortest_distance[from_node] + weight predecessor[to_node] = from_node # step3: check neg. cycles for from_node in G: - for to_node,weight in G[from_node].items(): + for to_node, weight in G[from_node].items(): if shortest_distance[from_node] + weight < shortest_distance[to_node]: shortest_distance[to_node] = -infinity return shortest_distance, predecessor -if __name__ == '__main__': - # G = make_graph('data.txt') - - G = {1: {2: -10, 3: 20}, - 2: {4: 40}, - 3: {4: 5}, - 4: {}} - print(f'Current graph is: {G}') - shortest, predecessor = bellman_ford(G, 1) - print(shortest) \ No newline at end of file +# if __name__ == '__main__': +# G = {1: {2: -10, 3: 20}, +# 2: {4: 40}, +# 3: {4: 5}, +# 4: {}} +# +# print(f'Current graph is: {G}') +# shortest, predecessor = bellman_ford(G, 1) +# print(shortest) diff --git a/Algorithms/graphtheory/breadth-first-search/BFS_queue_iterative.py b/Algorithms/graphtheory/breadth-first-search/BFS_queue_iterative.py index ccfd057..fbdedaa 100644 --- a/Algorithms/graphtheory/breadth-first-search/BFS_queue_iterative.py +++ b/Algorithms/graphtheory/breadth-first-search/BFS_queue_iterative.py @@ -1,19 +1,20 @@ -from collections import deque - -def load_graph(file='exgraph.txt'): - data = open(file, 'r') - G = {} +""" +Programmed by Aladdin Persson + 2019-02-17 Initial programming + 2020-03-29 Cleaned up code, removed load graph, I think a small example is sufficient + and instead only have the BFS function. +""" - for line in data: - lst = [int(x) for x in line.split()] - G[lst[0]] = lst[1:] - - num_nodes = len(G) +from collections import deque - return G, num_nodes def BFS(G, start_node=1): - visited = [False for i in range(1,len(G)+1)] + """ + :param G: Graph with G = {from_node1:[to_node1, to_node2], from_node2: [to_node,] etc} + :param start_node: starting node to run BFS from + :return: returns visited boolean array and path in which order it visited them + """ + visited = [False for i in range(1, len(G) + 1)] Q = deque() Q.append(start_node) @@ -25,25 +26,22 @@ def BFS(G, start_node=1): path.append(curr_node) if not visited[curr_node - 1]: - visited[curr_node-1]=True + visited[curr_node - 1] = True for connected_node in G[curr_node]: - if not visited[connected_node-1]: + if not visited[connected_node - 1]: Q.append(connected_node) return visited, path +# Small Example Run +# if __name__ == '__main__': +# G = {1:[2,3], 2:[1,4], 3:[1,4],4:[]} +# visited, path = BFS(G) +# +# if all(visited) == True: +# print("Return: This graph is connected!") -if __name__ == '__main__': - G, num_nodes = load_graph() - #G = {1:[2,3], 2:[1,4], 3:[1,4],4:[]} - - visited, path = BFS(G) - - if all(visited) == True: - print("Return: This graph is connected!") - else: - print("Not all nodes were reachable, i.e the graph is not connected.") - - +# else: +# print("Not all nodes were reachable, i.e the graph is not connected.") diff --git a/Algorithms/graphtheory/breadth-first-search/__pycache__/BFS_queue_iterative.cpython-37.pyc b/Algorithms/graphtheory/breadth-first-search/__pycache__/BFS_queue_iterative.cpython-37.pyc new file mode 100644 index 0000000..00bb444 Binary files /dev/null and b/Algorithms/graphtheory/breadth-first-search/__pycache__/BFS_queue_iterative.cpython-37.pyc differ diff --git a/Algorithms/graphtheory/depth-first-search/DFS_recursive.py b/Algorithms/graphtheory/depth-first-search/DFS_recursive.py index 20e9f20..0c70523 100644 --- a/Algorithms/graphtheory/depth-first-search/DFS_recursive.py +++ b/Algorithms/graphtheory/depth-first-search/DFS_recursive.py @@ -4,48 +4,33 @@ # Programmed by Aladdin Persson # 2019-02-16 Initial programming -# Improvements: -# * Check implementation with stack/queue -def load_graph(file='exgraph.txt'): - data = open(file, 'r') - G = {} - - for line in data: - lst = [int(x) for x in line.split()] - G[lst[0]] = lst[1:] - - num_nodes = len(G) - - return G, num_nodes - - -def DFS(curr_node): +def DFS(G, curr_node, visited): + """ + :param G: G = {from_node1:[to_node1, to_node2], from_node2: [to_node,] etc} + :param curr_node: Node currently at, run from beginning this is the starting node + :param visited: since it is recursive, visited is updated and needs to be sent in on recursive call + :return: visited is initialized outside of DFS and updates this boolean array with which nodes has been visited + """ if visited[curr_node - 1]: return - visited[curr_node-1] = True + visited[curr_node - 1] = True - # G is a dictionary neighbours = G[curr_node] for next_node in neighbours: - DFS(next_node) - -if __name__ == '__main__': - print('Loading graph and print:') - - try: - G, num_nodes = load_graph() - print(G) - - except TypeError: - raise("Error loading graph.") - - visited = [False for i in range(1, num_nodes + 1)] - start_node = 1 - - DFS(start_node) - - if any(visited) == False: - print("Result: This graph is connected!") \ No newline at end of file + DFS(G, next_node, visited) + + +# Small Eaxmple +# if __name__ == '__main__': +# G = {1: [2], 2: [1, 3, 4], 3: [2], 4: [2, 5], 5: [4]} +# +# visited = [False for i in range(1, len(G) + 1)] +# start_node = 1 +# +# DFS(G, start_node, visited) +# +# if any(visited) == False: +# print("Result: This graph is connected!") diff --git a/Algorithms/graphtheory/depth-first-search/DFS_stack_iterative.py b/Algorithms/graphtheory/depth-first-search/DFS_stack_iterative.py index 437e3bc..91fcce6 100644 --- a/Algorithms/graphtheory/depth-first-search/DFS_stack_iterative.py +++ b/Algorithms/graphtheory/depth-first-search/DFS_stack_iterative.py @@ -1,28 +1,19 @@ -# Purpose of Depth first search is (mainly from my understanding) to find if a graph G is connected. -# Identify all nodes that are reachable from a given starting node. +""" +Depth first search has many applications,for example finding if a graph G is connected. +Identify all nodes that are reachable from a given starting node. -# Programmed by Aladdin Persson -# 2019-02-17 Initial programming +Programmed by Aladdin Persson + 2019-02-17 Initial programming + 2020-03-29 Cleaned up code, made test cases +""" -# Improvements: think this should work pretty well (include tests would be good) - -import time - -def load_graph(file='exgraph.txt'): - data = open(file, 'r') - G = {} - - for line in data: - lst = [int(x) for x in line.split()] - G[lst[0]] = lst[1:] - - num_nodes = len(G) - - return G, num_nodes def DFS(G, start_node): - start = time.time() - + """ + :param G: Graph with G = {from_node1:[to_node1, to_node2], from_node2: [to_node,] etc} + :param start_node: starting node to run BFS from + :return: returns visited boolean array and path in which order it visited them + """ visited = [False for i in range(1, len(G) + 1)] path = [start_node] stack = [] @@ -30,26 +21,23 @@ def DFS(G, start_node): while stack: v = stack.pop() - if not visited[v-1]: - visited[v-1]=True + if not visited[v - 1]: + visited[v - 1] = True for connected_node in G[v]: - if not visited[connected_node-1]: + if not visited[connected_node - 1]: stack.append(connected_node) path.append(connected_node) - total_time = (time.time() - start) - print(f'Total time: {total_time} seconds') - return visited, path -if __name__ == '__main__': - G, num_nodes = load_graph() - start_node = 1 - #G = {1:[2,3], 2:[1,4], 3:[1,4],4:[]} - visited = DFS(G, start_node) - if all(visited) == True: - print("Return: This graph is connected!") - else: - print("Not all nodes were reachable, i.e the graph is not connected.") \ No newline at end of file +# if __name__ == '__main__': +# G = {1: [2, 3], 2: [1, 4], 3: [1, 4], 4: []} +# start_node = 1 +# visited = DFS(G, start_node) +# +# if all(visited) == True: +# print("Return: This graph is connected!") +# else: +# print("Not all nodes were reachable, i.e the graph is not connected.") diff --git a/Algorithms/graphtheory/depth-first-search/__pycache__/DFS_recursive.cpython-37.pyc b/Algorithms/graphtheory/depth-first-search/__pycache__/DFS_recursive.cpython-37.pyc new file mode 100644 index 0000000..a09c3cc Binary files /dev/null and b/Algorithms/graphtheory/depth-first-search/__pycache__/DFS_recursive.cpython-37.pyc differ diff --git a/Algorithms/graphtheory/depth-first-search/__pycache__/DFS_stack_iterative.cpython-37.pyc b/Algorithms/graphtheory/depth-first-search/__pycache__/DFS_stack_iterative.cpython-37.pyc new file mode 100644 index 0000000..f022b34 Binary files /dev/null and b/Algorithms/graphtheory/depth-first-search/__pycache__/DFS_stack_iterative.cpython-37.pyc differ diff --git a/Algorithms/graphtheory/dijkstra/__pycache__/dijkstra.cpython-37.pyc b/Algorithms/graphtheory/dijkstra/__pycache__/dijkstra.cpython-37.pyc new file mode 100644 index 0000000..fd049da Binary files /dev/null and b/Algorithms/graphtheory/dijkstra/__pycache__/dijkstra.cpython-37.pyc differ diff --git a/Algorithms/graphtheory/dijkstra/__pycache__/heapdijkstra.cpython-37.pyc b/Algorithms/graphtheory/dijkstra/__pycache__/heapdijkstra.cpython-37.pyc new file mode 100644 index 0000000..e63d035 Binary files /dev/null and b/Algorithms/graphtheory/dijkstra/__pycache__/heapdijkstra.cpython-37.pyc differ diff --git a/Algorithms/graphtheory/dijkstra/dijkstra.py b/Algorithms/graphtheory/dijkstra/dijkstra.py new file mode 100644 index 0000000..6c52add --- /dev/null +++ b/Algorithms/graphtheory/dijkstra/dijkstra.py @@ -0,0 +1,71 @@ +""" +Dijkstra's algorithm for finding the shortest path in a graph, this implementation +is a naive implementation, check my Heap implementation for a more efficient algorithm + +Programmed by Aladdin Persson + 2019-01-28 Initial programming + 2020-03-28 Cleaned up code +""" + + +def dijkstra(G, start, end): + """ + :param G: {from_node1: {to_node1:cost1, to_node2:cost2}, from_node2 : {.., etc.}, ...} + :param start: starting node + :param end: ending node where we want to find path to + :return: path from starting node to end node and the cost to get between them + """ + + if start not in G or end not in G: + return [], float("inf") + + shortest_distance = {} + predecessor = {} + unseenNodes = G + infinity = float("inf") + path = [] + + for node in unseenNodes: + shortest_distance[node] = infinity + + shortest_distance[start] = 0 + + while unseenNodes: + minNode = None + + for node in unseenNodes: + if minNode is None: + minNode = node + elif shortest_distance[node] < shortest_distance[minNode]: + minNode = node + + for childNode, weight in G[minNode].items(): + if weight + shortest_distance[minNode] < shortest_distance[childNode]: + shortest_distance[childNode] = weight + shortest_distance[minNode] + predecessor[childNode] = minNode + + unseenNodes.pop(minNode) + + # Find path from start node s to end node t + currentNode = end + + while currentNode != start: + try: + path.insert(0, currentNode) + currentNode = predecessor[currentNode] + except KeyError: + return [], float("inf") + + path.insert(0, start) + + return path, shortest_distance[end] + + +if __name__ == "__main__": + G = {1: {2: 10, 3: 20}, 2: {4: 40}, 3: {4: 5}, 4: {}} + + print(f"Current graph is: {G}") + path, shortest = dijkstra(G, 1, 4) + + print(path) + print(shortest) diff --git a/Algorithms/graphtheory/dijkstra/djikstra.py b/Algorithms/graphtheory/dijkstra/djikstra.py deleted file mode 100644 index 380a910..0000000 --- a/Algorithms/graphtheory/dijkstra/djikstra.py +++ /dev/null @@ -1,80 +0,0 @@ -# Dijkstra's algorithm for finding the shortest path in a graph -# Programmed 2019-01-28 - -def make_graph(file): - try: - f = open(file, 'r') - except IOError: - raise("File does not exist!") - - line_list = f.readlines() - - # Found on mukeshmithrakumar github, thought this was clean.. - # populate the graph using data from the text file via dictionary comprehensions - G = {int(line.split()[0]): {(int(tup.split(',')[0])): int(tup.split(',')[1]) - for tup in line.split()[1:] if tup} for line in line_list if line} - - f.close() - return G - - -def dijkstra(G, start, end): - shortest_distance = {} - predecessor = {} - unseenNodes = G - infinity = float('inf') - path = [] - - for node in unseenNodes: - shortest_distance[node] = infinity - - shortest_distance[start] = 0 - - - while unseenNodes: - minNode = None - - # priority-queue? -> Hittar bästa noden hittils - for node in unseenNodes: - if minNode is None: - minNode = node - elif shortest_distance[node] < shortest_distance[minNode]: - minNode = node - - - for childNode, weight in G[minNode].items(): - if weight + shortest_distance[minNode] < shortest_distance[childNode]: - shortest_distance[childNode] = weight + shortest_distance[minNode] - predecessor[childNode] = minNode - - unseenNodes.pop(minNode) - - print("pred") - print(predecessor) - currentNode = end - - while currentNode != start: - try: - path.insert(0, currentNode) - currentNode = predecessor[currentNode] - except KeyError: - print('Path not reachable') - break - path.insert(0,start) - - return path, shortest_distance[end] - - - - -#G = make_graph('dijkstraData.txt') - -G = {1:{2:10, 3:20}, - 2:{4:40}, - 3:{4:5}, - 4:{}} -print(f'Current graph is: {G}') -path, shortest = dijkstra(G, 1, 4) - -print(path) -print(shortest) diff --git a/Algorithms/graphtheory/dijkstra/heapdijkstra.py b/Algorithms/graphtheory/dijkstra/heapdijkstra.py index def99fa..3a593c6 100644 --- a/Algorithms/graphtheory/dijkstra/heapdijkstra.py +++ b/Algorithms/graphtheory/dijkstra/heapdijkstra.py @@ -1,31 +1,46 @@ -# Dijkstra's algorithm for finding the shortest path. -# Improved version with the usage of heaps. +""" +Dijkstra's algorithm for finding the shortest path. +Improved version with the usage of heaps. -# Programmed by Aladdin Persson -# 2019-02-15 Initial coding +Programmed by Aladdin Persson + 2019-02-15 Initial coding + 2020-03-28 Small code changes, fixed for edge cases not covered + +""" import heapq + def make_graph(file): try: - f = open(file, 'r') + f = open(file, "r") except IOError: - raise("File does not exist!") + raise ("File does not exist!") line_list = f.readlines() - # Kinda messy. - # populate the graph using data from the text file via dictionary comprehensions - G = {int(line.split()[0]): {(int(tup.split(',')[0])): int(tup.split(',')[1]) - for tup in line.split()[1:] if tup} for line in line_list if line} + # Kinda messy graph loading + G = { + int(line.split()[0]): { + (int(tup.split(",")[0])): int(tup.split(",")[1]) + for tup in line.split()[1:] + if tup + } + for line in line_list + if line + } f.close() return G + def dijkstra(G, start, end=None): + if start not in G or (end != None and end not in G): + return [], {end: float("inf")} + distance, visited, history, heap, path = {}, {}, {}, [], [] for node in G.keys(): - distance[node] = float('inf') + distance[node] = float("inf") visited[node] = False distance[start], visited[start] = 0, True @@ -48,19 +63,24 @@ def dijkstra(G, start, end=None): try: path.insert(0, current_node) current_node = history[current_node] + except KeyError: - print('Path not reachable') - break + return [], distance path.insert(0, start) - return distance, path + return path, distance + +if __name__ == "__main__": + # start, end = 1, 160 + # print(f'Goal is to find the path from node {start} to node {end}') + # G = make_graph('dijkstraData.txt') -if __name__ == '__main__': - start, end = 1, 160 - print(f'Goal is to find the path from node {start} to node {end}') - G = make_graph('dijkstraData.txt') + G = {1: {2: 10, 3: 20}, 2: {4: 40}, 3: {4: 5}, 4: {}} + start = 1 + end = 2 - dist, path = dijkstra(G, start, end) - print(f'Path found: {path}') \ No newline at end of file + path, dist = dijkstra(G, start, end) + print(f"Path found: {path}") + print(dist) diff --git a/Algorithms/graphtheory/floyd-warshall/floyd-warshall.py b/Algorithms/graphtheory/floyd-warshall/floyd-warshall.py index dcbcbfe..e65d495 100644 --- a/Algorithms/graphtheory/floyd-warshall/floyd-warshall.py +++ b/Algorithms/graphtheory/floyd-warshall/floyd-warshall.py @@ -1,11 +1,12 @@ -''' +""" Purpose is to the find shortest path from all nodes to all other nodes, O(n^3). Can be used with negative weights, however to check if it has negative cycles, bellman ford should prob. be used first. Programmed by Aladdin Persson * 2019-03-08 Initial programming -''' +""" + def load_graph(file_name): try: @@ -16,21 +17,38 @@ def load_graph(file_name): except IOError: raise IOError("File does not exist") - G = {int(line.split()[0]): {(int(tup.split(',')[0])): int(tup.split(',')[1]) - for tup in line.split()[1:] if tup} for line in line_list if line} + G = { + int(line.split()[0]): { + (int(tup.split(",")[0])): int(tup.split(",")[1]) + for tup in line.split()[1:] + if tup + } + for line in line_list + if line + } # If we have path set path else make value infinity - adjacency_matrix = [[G[i][j] if (i in G and j in G[i]) else float('inf') for j in range(1, len(G) + 1)] for i in range(1, len(G) + 1)] + adjacency_matrix = [ + [ + G[i][j] if (i in G and j in G[i]) else float("inf") + for j in range(1, len(G) + 1) + ] + for i in range(1, len(G) + 1) + ] # Make diagonal values all 0 for i in range(len(G)): adjacency_matrix[i][i] = 0 # next will be matrix showing which path to take for the shortest path - next = [[j if adjacency_matrix[i][j] != float('inf') else None for j in range(len(G))] for i in range(len(G))] + next = [ + [j if adjacency_matrix[i][j] != float("inf") else None for j in range(len(G))] + for i in range(len(G)) + ] return adjacency_matrix, next + def floyd_warshall(adj_matrix, next): n = len(adj_matrix) # make a copy of adj_matrix, dp will contain APSP (All-Path-Shortest-Path) solutions, @@ -49,6 +67,7 @@ def floyd_warshall(adj_matrix, next): # return APSP (All-Path-Shortest-Path) matrix return APSP, next + def construct_path_to_take(next, start, end): go_through = start path = [start] @@ -61,12 +80,13 @@ def construct_path_to_take(next, start, end): return path -if __name__ == '__main__': - adj_matrix, next = load_graph('test_graph.txt') + +if __name__ == "__main__": + adj_matrix, next = load_graph("test_graph.txt") APSP, next = floyd_warshall(adj_matrix, next) - print(f'The shortest paths are {APSP}') - print(f'The path to take is given by {next}') + print(f"The shortest paths are {APSP}") + print(f"The path to take is given by {next}") path_to_take = construct_path_to_take(next, 0, 3) - print(path_to_take) \ No newline at end of file + print(path_to_take) diff --git a/Algorithms/graphtheory/kahns-toposort/__pycache__/kahn_topological_ordering.cpython-37.pyc b/Algorithms/graphtheory/kahns-toposort/__pycache__/kahn_topological_ordering.cpython-37.pyc new file mode 100644 index 0000000..31bc0d6 Binary files /dev/null and b/Algorithms/graphtheory/kahns-toposort/__pycache__/kahn_topological_ordering.cpython-37.pyc differ diff --git a/Algorithms/graphtheory/kahns-toposort/kahn_topological_ordering.py b/Algorithms/graphtheory/kahns-toposort/kahn_topological_ordering.py new file mode 100644 index 0000000..572e175 --- /dev/null +++ b/Algorithms/graphtheory/kahns-toposort/kahn_topological_ordering.py @@ -0,0 +1,59 @@ +""" +Purpose of this algorithm is finding a path that is in topological ordering for a directed acyclic graph (DAG). +If the graph is not a DAG and includes cycles it will return is_DAG = 'False' meaning it has cycles. + +Time Complexity: O(n + m), n = |V|, m = |E| + +Programmed by Aladdin Persson +* 2019-03-17 Intitial programming +* 2020-03-28 Removed load graph function, just having topological sort seems to make more sense + The loading of the graph is so varying depending on input, doesn't make much sense to include it + +""" + +from collections import defaultdict, deque + + +def topological_ordering(graph, degree_incoming): + if len(graph) == 0: + return [], False + + curr_accessible_nodes = deque() + + for node in graph: + if degree_incoming[node] == 0: + curr_accessible_nodes.append(node) + + path = [] + + while curr_accessible_nodes: + node_topological = curr_accessible_nodes.popleft() + path.append(node_topological) + + for connected_node in graph[node_topological]: + degree_incoming[connected_node] -= 1 + + if degree_incoming[connected_node] == 0: + curr_accessible_nodes.append(connected_node) + + # Check if there are still incoming edges (meaning it will have cycles) + is_DAG = True + + for val in degree_incoming.values(): + if val != 0: + is_DAG = False + path = [] + + return path, is_DAG + + +if __name__ == "__main__": + G = {"A": ["B"], "B": ["C", "D"], "C": ["D"], "D": []} + degree_incoming = defaultdict(int, {"B": 1, "C": 1, "D": 2}) + + print("The graph to check is : " + str(G)) + print("Which has incoming edges: " + str(degree_incoming)) + print("\n") + path_to_take, is_DAG = topological_ordering(G, degree_incoming) + print("The graph has a topological ordering <--> G is a DAG: " + str(is_DAG)) + print(f'One path to take that is a topological ordering is {"".join(path_to_take)}') diff --git a/Algorithms/graphtheory/kahns-toposort/kahns.py b/Algorithms/graphtheory/kahns-toposort/kahns.py deleted file mode 100644 index 4f68b39..0000000 --- a/Algorithms/graphtheory/kahns-toposort/kahns.py +++ /dev/null @@ -1,57 +0,0 @@ -''' -Purpose of this algorithm is finding a path that is in topological ordering for a directed acyclic graph (DAG). - -Programmed by Aladdin Persson -* 2019-03-17 Intitial programming - -''' - -from collections import defaultdict, deque -import re - -def load_graph(filename): - graph = defaultdict(list) - degree_incoming = defaultdict(int) - - for line in open(filename): - from_node, to_node = [char.replace(' ', '') for char in re.findall(r' . ', line)] - graph[from_node].append(to_node) - degree_incoming[to_node] += 1 - - return graph, degree_incoming - -def toposort(graph, degree_incoming): - curr_accessible_nodes = deque() - - for node in graph: - if degree_incoming[node] == 0: curr_accessible_nodes.append(node) - - path = [] - - while curr_accessible_nodes: - node_topological = curr_accessible_nodes.popleft() - path.append(node_topological) - - for connected_node in graph[node_topological]: - degree_incoming[connected_node] -= 1 - - if degree_incoming[connected_node] == 0: - curr_accessible_nodes.append(connected_node) - - # Check if there are still incoming edges (meaning it will have cycles) - for val in degree_incoming.values(): - if val != 0: - raise ValueError("There are cycles in this graph!") - - return path - -def main(): - # Note: The load graph WILL differ depending on the input graph you have. - # There are many ways to have a textfile to describe a topological graph, and one needs to - # adapt to the specific problem. - graph, degree_incoming = load_graph('example.txt') - path_to_take = toposort(graph, degree_incoming) - print(f'One path to take that is a topological ordering is {"".join(path_to_take)}') - -if __name__ == '__main__': - main() \ No newline at end of file diff --git a/Algorithms/graphtheory/kargers/kargermincut.py b/Algorithms/graphtheory/kargers/kargermincut.py index 3b3fc36..3d754c5 100644 --- a/Algorithms/graphtheory/kargers/kargermincut.py +++ b/Algorithms/graphtheory/kargers/kargermincut.py @@ -13,8 +13,9 @@ random.seed(1) + def load_graph(): - data = open('data.txt', 'r') + data = open("data.txt", "r") G = {} for line in data: @@ -66,5 +67,6 @@ def main(): return count + val = main() -print(val) \ No newline at end of file +print(val) diff --git a/Algorithms/graphtheory/kruskal/__pycache__/kruskal.cpython-37.pyc b/Algorithms/graphtheory/kruskal/__pycache__/kruskal.cpython-37.pyc new file mode 100644 index 0000000..6222719 Binary files /dev/null and b/Algorithms/graphtheory/kruskal/__pycache__/kruskal.cpython-37.pyc differ diff --git a/Algorithms/graphtheory/kruskal/__pycache__/kruskal_unionfind.cpython-37.pyc b/Algorithms/graphtheory/kruskal/__pycache__/kruskal_unionfind.cpython-37.pyc new file mode 100644 index 0000000..c2ad871 Binary files /dev/null and b/Algorithms/graphtheory/kruskal/__pycache__/kruskal_unionfind.cpython-37.pyc differ diff --git a/Algorithms/graphtheory/kruskal/kruskal.py b/Algorithms/graphtheory/kruskal/kruskal.py index 55f8334..d2e9ad1 100644 --- a/Algorithms/graphtheory/kruskal/kruskal.py +++ b/Algorithms/graphtheory/kruskal/kruskal.py @@ -1,27 +1,28 @@ -# Kruskal's algorithm for finding minimal spanning tree (MST) of a graph. +""" +Kruskal's algorithm for finding minimal spanning tree (MST) of a graph. -# Aladdin Persson -# 2019-02-16 Initial programming +Aladdin Persson + 2019-02-16 Initial programming -# Improvements: -# * The for-loop inside Kruskal can be improved for sure. Not sure exactly how to do it though +""" import sys -sys.path.append('graphtheory/depth-first-search') + +sys.path.append("../depth-first-search") from DFS_stack_iterative import DFS -def load_graph(file='edges.txt'): +def load_graph(file="edges.txt"): G = [] try: - f = open(file, 'r') + f = open(file, "r") except IOError: - raise("File does not exist!") + raise ("File does not exist!") line_list = f.readlines() - num_nodes, num_edges = map(int,line_list[0].split()) + num_nodes, num_edges = map(int, line_list[0].split()) for line in line_list[1:]: G.append(tuple(map(int, line.split()))[::-1]) @@ -29,7 +30,7 @@ def load_graph(file='edges.txt'): return sorted(G), num_nodes -def kruskal(G): +def kruskal(G, num_nodes): MST = [] tot_cost = 0 temp_G = {key: [] for key in range(1, num_nodes + 1)} @@ -55,16 +56,14 @@ def kruskal(G): return MST, tot_cost -if __name__ == '__main__': - print('---- Computing minimal spanning tree using Kruskal\'s Algorithm ----') +if __name__ == "__main__": + print("---- Computing minimal spanning tree using Kruskal's Algorithm ----") print() G, num_nodes = load_graph() - print(f'Our loaded graph is: {G}') - print() - + print(f"Our loaded graph is: {G}") MST, total_cost = kruskal(G) - print(f'Our minimum spanning tree is: {MST}') - print(f'Total cost is: {total_cost}') \ No newline at end of file + print(f"Our minimum spanning tree is: {MST}") + print(f"Total cost is: {total_cost}") diff --git a/Algorithms/graphtheory/kruskal/kruskal_unionfind.py b/Algorithms/graphtheory/kruskal/kruskal_unionfind.py index 50fbf7e..63af644 100644 --- a/Algorithms/graphtheory/kruskal/kruskal_unionfind.py +++ b/Algorithms/graphtheory/kruskal/kruskal_unionfind.py @@ -5,17 +5,18 @@ from unionfind import unionfind -def load_graph(file='edges.txt'): + +def load_graph(file="edges.txt"): G = [] try: - f = open(file, 'r') + f = open(file, "r") except IOError: - raise("File does not exist!") + raise ("File does not exist!") line_list = f.readlines() - num_nodes, num_edges = map(int,line_list[0].split()) + num_nodes, num_edges = map(int, line_list[0].split()) for line in line_list[1:]: G.append(tuple(map(int, line.split()))[::-1]) @@ -23,30 +24,31 @@ def load_graph(file='edges.txt'): return sorted(G), num_nodes -def kruskal(G): +def kruskal(G, num_nodes): uf = unionfind(num_nodes) tot_cost, MST = 0, [] for each_edge in G: cost, to_node, from_node = each_edge[0], each_edge[1], each_edge[2] - if not uf.issame(from_node-1, to_node-1): + if not uf.issame(from_node - 1, to_node - 1): tot_cost += cost - uf.unite(from_node-1, to_node-1) - MST.extend([from_node, to_node]) + uf.unite(from_node - 1, to_node - 1) + MST.append((from_node, to_node, cost)) return MST, tot_cost -if __name__ == '__main__': - print('---- Computing minimal spanning tree using Kruskal\'s Algorithm ----') + +if __name__ == "__main__": + print("---- Computing minimal spanning tree using Kruskal's Algorithm ----") print() G, num_nodes = load_graph() - print(f'Our loaded graph is: {G}') + print(f"Our loaded graph is: {G}") print() MST, total_cost = kruskal(G) - print(f'Our minimum spanning tree is: {MST}') - print(f'Total cost is: {total_cost}') \ No newline at end of file + print(f"Our minimum spanning tree is: {MST}") + print(f"Total cost is: {total_cost}") diff --git a/Algorithms/graphtheory/nearest-neighbor-tsp/NearestNeighborTSP.py b/Algorithms/graphtheory/nearest-neighbor-tsp/NearestNeighborTSP.py new file mode 100644 index 0000000..fc6b658 --- /dev/null +++ b/Algorithms/graphtheory/nearest-neighbor-tsp/NearestNeighborTSP.py @@ -0,0 +1,126 @@ +""" +Author: Philip Andreadis +e-mail: philip_andreadis@hotmail.com + + This script implements a simple heuristic solver for the Traveling Salesman Problem. + It is not guaranteed that an optimal solution will be found. + + Format of input text file must be as follows: + 1st line - number of nodes + each next line is an edge and its respective weight + + The program is executed via command line with the graph in the txt format as input. + +""" + +import time +import sys + + + +""" +This function reads the text file and returns a 2d list which represents +the adjacency matrix of the given graph +""" +def parseGraph(path): + # Read number of vertices and create adjacency matrix + f = open(path, "r") + n = int(f.readline()) + adjMatrix = [[-1 for i in range(n)] for j in range(n)] + #Fill adjacency matrix with the correct edges + for line in f: + edge = line.split(" ") + edge = list(map(int, edge)) + adjMatrix[edge[0]][edge[1]] = edge[2] + adjMatrix[edge[1]][edge[0]] = edge[2] + for i in range(len(adjMatrix)): + adjMatrix[i][i] = 0 + return adjMatrix + + + +""" +Returns all the neighboring nodes of a node sorted based on the distance between them. +""" +def rankNeighbors(node,adj): + sortednn = {} + nList = [] + for i in range(len(adj[node])): + if adj[node][i]>0: + sortednn[i] = adj[node][i] + sortednn = {k: v for k, v in sorted(sortednn.items(), key=lambda item: item[1])} + nList = list(sortednn.keys()) + return nList + + +""" +Function implementing the logic of nearest neighbor TSP. +Generate two lists a and b, placing the starting node in list a and the rest in list b. +While b is not empty append to a the closest neighboring node of the last node in a and remove it from b. +Repeat until a full path has been added to a and b is empty. +Returns list a representing the shortest path of the graph. +""" +def nnTSP(adj): + nodes = list(range(0, len(adj))) + #print(nodes) + weight = 0 + global length + # Starting node is 0 + a = [] + a.append(nodes[0]) + b = nodes[1:] + while b: + # Take last placed node in a + last = a[-1] + # Find its nearest neighbor + sortedNeighbors = rankNeighbors(last,adj) + # If node being checked has no valid neighbors and the path is not complete a dead end is reached + if (not sortedNeighbors) and len(a) + 2019-02-16 Initial programming + 2020-03-29 Changed few lines to be able to handle empty graphs, etc, and changed how MST is computed (now correctly) +""" + +import heapq + + +def load_graph(file="edges.txt"): + try: + f = open(file, "r") + except IOError: + raise ("File does not exist!") + + line_list = f.readlines() + + num_nodes, num_edges = line_list[0].split() + + # We want to have edge cost first because the min heap will be based on edge cost + # concretely that's why we do [::-1], a bit confusing maybe + G = { + line: [ + tuple(map(int, tup.split()))[::-1] + for tup in line_list[1:] + if (int(tup.split()[0]) == line or int(tup.split()[1]) == line) + ] + for line in range(1, int(num_nodes) + 1) + } + + f.close() + return G + + +# Takes as input G which will have {node1: [(cost, to_node, node1), ...], node2:[(...)] } +def prims_algo(G, start=1): + if len(G) == 0: + return [], 0 + + unvisited_nodes = [i for i in range(1, len(G) + 1)] + visited_nodes = [] + tot_cost = 0 + + unvisited_nodes.remove(start) + visited_nodes.append(start) + MST = [] + + heap = G[start] + heapq.heapify(heap) + + while unvisited_nodes: + if len(heap) == 0: + # there is no MST because graph is disconnected then return MST of subgraph + return MST, tot_cost + + (cost, n2, n1) = heapq.heappop(heap) + new_node = None + + if n1 in unvisited_nodes and n2 in visited_nodes: + new_node = n1 + MST.append((n2, n1, cost)) + + elif n1 in visited_nodes and n2 in unvisited_nodes: + new_node = n2 + MST.append((n1, n2, cost)) + + if new_node != None: + unvisited_nodes.remove(new_node) + visited_nodes.append(new_node) + tot_cost += cost + + for each in G[new_node]: + heapq.heappush(heap, each) + + return MST, tot_cost + + +if __name__ == "__main__": + print("---- Computing minimal spanning tree using Prims Algorithm ---- \n") + + G = load_graph() + MST, tot_cost = prims_algo(G) + + # print(f'The minimum spanning tree is: {MST}') + print(f"Total cost of minimum spanning tree is {tot_cost}") diff --git a/Algorithms/graphtheory/prims/prims_algorithm.py b/Algorithms/graphtheory/prims/prim_naive.py similarity index 77% rename from Algorithms/graphtheory/prims/prims_algorithm.py rename to Algorithms/graphtheory/prims/prim_naive.py index ef55a83..6b9304b 100644 --- a/Algorithms/graphtheory/prims/prims_algorithm.py +++ b/Algorithms/graphtheory/prims/prim_naive.py @@ -7,7 +7,8 @@ # Improvement: Want to implement using heap datastructure -def load_graph(path = 'edges.txt'): + +def load_graph(path="edges.txt"): edge_list = [] with open(path) as f: @@ -15,28 +16,24 @@ def load_graph(path = 'edges.txt'): num_nodes, num_edges = [int(i) for i in lines[0].split()] for line in lines[1:]: - node1,node2,edge_cost = [int(i) for i in line.split()] - edge_list.append( (node1,node2,edge_cost)) - + node1, node2, edge_cost = [int(i) for i in line.split()] + edge_list.append((node1, node2, edge_cost)) return edge_list, num_nodes, num_edges def prims_algo(edge_list, num_nodes): - X = [] V = [i for i in range(1, num_nodes + 1)] E = [] total_cost = 0 - start = 1 X.append(start) V.remove(start) - while len(V) != 0: - lowest_cost = float('inf') + lowest_cost = float("inf") nodeX = None nodeV = None @@ -62,14 +59,9 @@ def prims_algo(edge_list, num_nodes): return E, total_cost -if __name__ == '__main__': - print('Computing minimal spanning tree using Prims Algorithm') +if __name__ == "__main__": + print("Computing minimal spanning tree using Prims Algorithm") edge_list, num_nodes, num_edges = load_graph() - E,tot_cost = prims_algo(edge_list, num_nodes) - - #print(E) - print(f'total cost of minimum spanning tree is {tot_cost}') - - + E, tot_cost = prims_algo(edge_list, num_nodes) diff --git a/Algorithms/graphtheory/prims/primheap.py b/Algorithms/graphtheory/prims/primheap.py deleted file mode 100644 index 66dc898..0000000 --- a/Algorithms/graphtheory/prims/primheap.py +++ /dev/null @@ -1,74 +0,0 @@ -# Prims algorithm for finding minimal spanning tree (MST) of a graph. Optimized version using Heaps! - -# This code is functional but needs: -# * Comments -# * Clean up code a bit (loadgraph especially and new_node != None part could perhaps be cleaner) - -# Aladdin Persson -# 2019-02-16 Initial programming - -import heapq - -def load_graph(file='edges.txt'): - try: - f = open(file, 'r') - except IOError: - raise("File does not exist!") - - line_list = f.readlines() - - num_nodes, num_edges = line_list[0].split() - - G = {line: [tuple(map(int, tup.split()))[::-1] for tup in line_list[1:] - if (int(tup.split()[0]) == line or int(tup.split()[1]) == line)] - for line in range(1, int(num_nodes) + 1)} - - f.close() - return G, int(num_nodes) - - -def prims_algo(G, num_nodes): - unvisited_nodes = [i for i in range(1, num_nodes + 1)] - visited_nodes = [] - - start = 1 - tot_cost = 0 - - unvisited_nodes.remove(start) - visited_nodes.append(start) - MST = [start] - - heap = G[start] - heapq.heapify(heap) - - while unvisited_nodes: - (cost, n2, n1) = heapq.heappop(heap) - new_node = None - - if n1 in unvisited_nodes and n2 in visited_nodes: - new_node = n1 - - elif n1 in visited_nodes and n2 in unvisited_nodes: - new_node = n2 - - if new_node != None: - unvisited_nodes.remove(new_node) - visited_nodes.append(new_node) - MST.append(new_node) - tot_cost += cost - - for each in G[new_node]: - heapq.heappush(heap, each) - - return MST, tot_cost - -if __name__ == '__main__': - print('---- Computing minimal spanning tree using Prims Algorithm ---- \n') - - G, num_nodes = load_graph() - print(f'Our loaded graph is: {G} \n') - - MST, tot_cost = prims_algo(G, num_nodes) - - print(f'The minimum spanning tree is: {MST}') - print(f'Total cost of minimum spanning tree is {tot_cost}') diff --git a/Algorithms/math/euclid_gcd/euclid_gcd.py b/Algorithms/math/euclid_gcd/euclid_gcd.py index 3a08b3e..12591d1 100644 --- a/Algorithms/math/euclid_gcd/euclid_gcd.py +++ b/Algorithms/math/euclid_gcd/euclid_gcd.py @@ -6,6 +6,7 @@ # Programmed by Aladdin Persson # 2019-02-19 Initial programming + def gcd_recursive(a, b): if b == 0: return a @@ -13,11 +14,12 @@ def gcd_recursive(a, b): return gcd_recursive(b, a % b) -def gcd_iterative(a,b): +def gcd_iterative(a, b): while b != 0: - a, b = b, a%b + a, b = b, a % b return a -if __name__ == '__main__': - print(gcd_iterative(65,14)) \ No newline at end of file + +if __name__ == "__main__": + print(gcd_iterative(65, 14)) diff --git a/Algorithms/math/extended_euclidean_algorithm/euclid_gcd.py b/Algorithms/math/extended_euclidean_algorithm/euclid_gcd.py index 22b7dcc..e456de1 100644 --- a/Algorithms/math/extended_euclidean_algorithm/euclid_gcd.py +++ b/Algorithms/math/extended_euclidean_algorithm/euclid_gcd.py @@ -1,19 +1,22 @@ -''' +""" -''' +""" import sys + sys.setrecursionlimit(100000) + def extended_euclidean(a, b): if a == 0: return (b, 0, 1) else: - gcd, x, y = extended_euclidean(b % a, a) - return (gcd, y - (b//a) * x, x) + gcd, x, y = extended_euclidean(b % a, a) + return (gcd, y - (b // a) * x, x) + -if __name__ == '__main__': - #print(extended_euclidean(5,-2772)) - print(extended_euclidean(1635, 26)) \ No newline at end of file +if __name__ == "__main__": + # print(extended_euclidean(5,-2772)) + print(extended_euclidean(1635, 26)) diff --git a/Algorithms/math/intersection_of_two_sets/intersection_of_two_sets.py b/Algorithms/math/intersection_of_two_sets/intersection_of_two_sets.py index cec55ce..d3ce2ae 100644 --- a/Algorithms/math/intersection_of_two_sets/intersection_of_two_sets.py +++ b/Algorithms/math/intersection_of_two_sets/intersection_of_two_sets.py @@ -1,4 +1,4 @@ -''' +""" Purpose is given two sets A,B find the intersection of them. I know there must be more efficients ways than what I, am doing here but havn't figured out how yet. @@ -7,36 +7,38 @@ Programmed by Aladdin Persson * 2019-03-13 Initial programming -''' +""" -def intersection(A,B): + +def intersection(A, B): # n = length of A, m = length of B # O(nlog(n) + mlog(n)) cost for sorting A = sorted(A) B = sorted(B) - i, j = 0,0 + i, j = 0, 0 AB_intersection = [] # complexity O(n+m) while i < len(A) and j < len(B): if A[i] < B[j]: - i+=1 + i += 1 elif B[j] < A[i]: - j+=1 + j += 1 elif A[i] == B[j]: AB_intersection.append(A[i]) - i+=1 - j+=1 + i += 1 + j += 1 return AB_intersection -if __name__ == '__main__': - A = [1,3,5,8,12] - B = [2,3,5,10,12] - intersection_AB = intersection(A,B) +if __name__ == "__main__": + A = [1, 3, 5, 8, 12] + B = [2, 3, 5, 10, 12] + + intersection_AB = intersection(A, B) print(intersection_AB) diff --git a/Algorithms/math/karatsuba/karatsuba.py b/Algorithms/math/karatsuba/karatsuba.py index d4d113b..e22e827 100644 --- a/Algorithms/math/karatsuba/karatsuba.py +++ b/Algorithms/math/karatsuba/karatsuba.py @@ -5,36 +5,38 @@ # import random to check if implementation is correct import random -def karatsuba(x,y): + +def karatsuba(x, y): # handle negative numbers multiplication if x < 0: - return -1 * karatsuba(-x,y) + return -1 * karatsuba(-x, y) if y < 0: - return -1 * karatsuba(x,-y) + return -1 * karatsuba(x, -y) # Base case (two numbers from 1-9 multiplication) if len(str(x)) == 1 or len(str(y)) == 1: - return (x*y) + return x * y n = max(len(str(x)), len(str(y))) # split about middle (can be done in multiple ways, found on github, thought was rly clever) - a = x // 10**(n // 2) - b = x % 10**(n // 2) - c = y // 10**(n // 2) - d = y % 10**(n // 2) + a = x // 10 ** (n // 2) + b = x % 10 ** (n // 2) + c = y // 10 ** (n // 2) + d = y % 10 ** (n // 2) # Compute the terms using recursion - ac = karatsuba(a,c) - bd = karatsuba(b,d) - ad_bc = karatsuba(a + b,c + d) - ac - bd + ac = karatsuba(a, c) + bd = karatsuba(b, d) + ad_bc = karatsuba(a + b, c + d) - ac - bd # calculate x * y - product = ac * (10**(2*(n//2))) + ad_bc * (10**(n // 2)) + bd + product = ac * (10 ** (2 * (n // 2))) + ad_bc * (10 ** (n // 2)) + bd # return x * y return product + # Following checks if implementation is correct # if __name__ == '__main__': # for _ in range(500): @@ -51,9 +53,9 @@ def karatsuba(x,y): # For Programming Assignment 1 -if __name__ == '__main__': +if __name__ == "__main__": x = 3141592653589793238462643383279502884197169399375105820974944592 y = 2718281828459045235360287471352662497757247093699959574966967627 - print(karatsuba(x,y)) - print(x*y) + print(karatsuba(x, y)) + print(x * y) diff --git a/Algorithms/math/pollard_p1/pollard_p1.py b/Algorithms/math/pollard_p1/pollard_p1.py index 89d032e..5124529 100644 --- a/Algorithms/math/pollard_p1/pollard_p1.py +++ b/Algorithms/math/pollard_p1/pollard_p1.py @@ -1,4 +1,4 @@ -''' +""" Purpose is given a number write it only as a factorization of primes. Time complexity: @@ -6,22 +6,24 @@ Programmed by Aladdin Persson * 2019-08-22 Initial programming -''' +""" from math import gcd + def pollard_p1(n): r = 2 - for i in range(2,100): - r = r**i % n + for i in range(2, 100): + r = r ** i % n if gcd(r - 1, n) != 1: - print('One factor found was: ' + str(gcd(r - 1,n))) - print('Number of iterations was: ' + str(i)) - return gcd(r - 1,n) + print("One factor found was: " + str(gcd(r - 1, n))) + print("Number of iterations was: " + str(i)) + return gcd(r - 1, n) print("No factorization was found") -if __name__ == '__main__': - pollard_p1(703425623) \ No newline at end of file + +if __name__ == "__main__": + pollard_p1(703425623) diff --git a/Algorithms/math/prime_factorization/primefactorization.py b/Algorithms/math/prime_factorization/primefactorization.py index 2d95d3a..1a0ea36 100644 --- a/Algorithms/math/prime_factorization/primefactorization.py +++ b/Algorithms/math/prime_factorization/primefactorization.py @@ -1,20 +1,21 @@ -''' +""" Purpose is given a number write it only as a factorization of primes. -Time complexity: O(sqrt(n)) +Time complexity: O(sqrt(n)) PSEUDO-POLYNOMIAL, actually exponential! Programmed by Aladdin Persson * 2019-03-18 Initial programming * 2019-08-19 Noticed a bug when using. Should be correct now, but I need to implement more robust tests to be certain that this is a correct implementation. -''' +""" + def primefactorization(n): original_value = n values = [] - for i in range(2, int(n**0.5) + 1): + for i in range(2, int(n ** 0.5) + 1): # Will not pass this if statement if i is not a prime number. # (This is because all numbers have a prime factorization) if n % i == 0: @@ -24,11 +25,16 @@ def primefactorization(n): values.append(i) if len(values) != 0: - values.append(int(n)) # if we have found one factor <= sqrt(n), then there will be another factor. - print(f'Prime factorization of {original_value} is: {values}') + values.append( + int(n) + ) # if we have found one factor <= sqrt(n), then there will be another factor. + print(f"Prime factorization of {original_value} is: {values}") else: - print(f'There is no prime factorization because the number {original_value} is a prime') + print( + f"There is no prime factorization because the number {original_value} is a prime" + ) + -if __name__ == '__main__': - #primefactorization(2**2**6 + 1) - primefactorization(137) \ No newline at end of file +if __name__ == "__main__": + # primefactorization(2**2**6 + 1) + primefactorization(123) diff --git a/Algorithms/math/sieve_of_eratosthenes/sieve_eratosthenes.py b/Algorithms/math/sieve_of_eratosthenes/sieve_eratosthenes.py index 5779a75..2eac767 100644 --- a/Algorithms/math/sieve_of_eratosthenes/sieve_eratosthenes.py +++ b/Algorithms/math/sieve_of_eratosthenes/sieve_eratosthenes.py @@ -3,22 +3,24 @@ # Programmed by Aladdin Persson # 2019-02-27 Initial programming + def eratosthenes(n): - primes, sieve = [], [True] * (n+1) - sieve[0], sieve[1] = 'Zero', 'One' + primes, sieve = [], [True] * (n + 1) + sieve[0], sieve[1] = "Zero", "One" - for num in range(2,n+1): + for num in range(2, n + 1): if sieve[num]: primes.append(num) # "Optimized" loop here because we dont have to go up 1,2,3,4 in this # we can jump num instead - for i in range(num*num, n+1, num): + for i in range(num * num, n + 1, num): sieve[i] = False return primes -if __name__ == '__main__': - n = 10**6 + +if __name__ == "__main__": + n = 10 ** 6 primes = eratosthenes(n) print(primes) diff --git a/Algorithms/math/union_of_two_sets/union_of_two_sets.py b/Algorithms/math/union_of_two_sets/union_of_two_sets.py index 0c6625a..f9881cd 100644 --- a/Algorithms/math/union_of_two_sets/union_of_two_sets.py +++ b/Algorithms/math/union_of_two_sets/union_of_two_sets.py @@ -1,4 +1,4 @@ -''' +""" Purpose is given two sets A,B find the union of them. I know there must be more efficients ways than what I, am doing here but havn't figured out how yet. @@ -7,15 +7,16 @@ Programmed by Aladdin Persson * 2019-03-13 Initial programming -''' +""" -def union(A,B): + +def union(A, B): # n = length of A, m = length of B # O(nlog(n) + mlog(n)) cost for sorting A = sorted(A) B = sorted(B) - i, j = 0,0 + i, j = 0, 0 AB_union = [] @@ -23,25 +24,26 @@ def union(A,B): while i < len(A) and j < len(B): if A[i] < B[j]: AB_union.append(A[i]) - i+=1 + i += 1 elif B[j] < A[i]: AB_union.append(B[j]) - j+=1 + j += 1 elif A[i] == B[j]: AB_union.append(A[i]) - i+=1 - j+=1 + i += 1 + j += 1 AB_union.extend(A[i:]) AB_union.extend(B[j:]) return AB_union -if __name__ == '__main__': - A = [1,3,5,8,12] - B = [2,3,5,10,12] - AB_union = union(A,B) +if __name__ == "__main__": + A = [1, 3, 5, 8, 12] + B = [2, 3, 5, 10, 12] + + AB_union = union(A, B) print(AB_union) diff --git a/Algorithms/numerical_methods/bisection.py b/Algorithms/numerical_methods/bisection.py index 8c091bc..580f5d3 100644 --- a/Algorithms/numerical_methods/bisection.py +++ b/Algorithms/numerical_methods/bisection.py @@ -1,24 +1,27 @@ -''' +""" # Purpose of the bisection method is to find an interval where there exists a root # Programmed by Aladdin Persson # 2019-10-07 Initial programming -''' +""" + + def function(x): - #return (x**2 - 2) - return (x**2 + 2*x - 1) + # return (x**2 - 2) + return x ** 2 + 2 * x - 1 + def bisection(a0, b0, eps, delta, maxit): # Initialize search bracket s.t a <= b - alpha = min(a0,b0) - beta = max(a0,b0) + alpha = min(a0, b0) + beta = max(a0, b0) a = [] b = [] fa = function(alpha) fb = function(beta) - if function(alpha)*function(beta) > 0: + if function(alpha) * function(beta) > 0: print("Needs to have one f(a) > 0 and f(b) < 0") exit() @@ -27,13 +30,13 @@ def bisection(a0, b0, eps, delta, maxit): b.append(beta) # Carefully compute the midpoint in an effort to avoid numerical roundoff errors - midpoint = alpha + (beta - alpha)/2 + midpoint = alpha + (beta - alpha) / 2 fc = function(midpoint) # Check for small residual if abs(fc) <= eps: print("Very small function value -> we're close enough to a root") - return alpha,beta + return alpha, beta # check for small bracket if abs(beta - alpha) <= delta: @@ -41,22 +44,23 @@ def bisection(a0, b0, eps, delta, maxit): return alpha, beta # Now we know we need to run more iterations - if fa*fc < 0: + if fa * fc < 0: beta = midpoint fb = fc else: alpha = midpoint fa = fc - return alpha,beta + return alpha, beta + def main(): a = 0 b = 1 - #print(function(a)) - #print(function(b)) - alpha, beta = bisection(a,b,eps=1e-8,delta=1e-8, maxit=3) - print("Bracket is (" + str(alpha)+ ', ' + str(beta) + ')') + # print(function(a)) + # print(function(b)) + alpha, beta = bisection(a, b, eps=1e-8, delta=1e-8, maxit=3) + print("Bracket is (" + str(alpha) + ", " + str(beta) + ")") -main() +main() diff --git a/Algorithms/numerical_methods/fixpoint.py b/Algorithms/numerical_methods/fixpoint.py index 9e660e3..69b22c6 100644 --- a/Algorithms/numerical_methods/fixpoint.py +++ b/Algorithms/numerical_methods/fixpoint.py @@ -1,14 +1,14 @@ -''' +""" # Purpose of the fixpoint method is to solve equations of the form x = g(x) # Note that many equations can be rewritten in this form, to solve for example for roots. # Programmed by Aladdin Persson # 2019-10-07 Initial programming -''' +""" # Rewrite x^2 = 2 def function(x): - return (x + (2/x))/2 + return (x + (2 / x)) / 2 def fixpoint(x0, tol): @@ -21,9 +21,11 @@ def fixpoint(x0, tol): return y + def main(): x0 = 2 val = fixpoint(x0, tol=1e-8) print(val) + main() diff --git a/Algorithms/other/Huffman/Huffman.py b/Algorithms/other/Huffman/Huffman.py index 807374d..9573687 100644 --- a/Algorithms/other/Huffman/Huffman.py +++ b/Algorithms/other/Huffman/Huffman.py @@ -1,6 +1,7 @@ import heapq from bitarray import bitarray + class Node(object): def __init__(self, ch, freq, left=None, right=None): self.ch = ch @@ -11,9 +12,10 @@ def __init__(self, ch, freq, left=None, right=None): def __lt__(self, other): return self.freq < other.freq + def make_frequency_dict(file="huffman.txt"): - freq ={} - text = '' + freq = {} + text = "" with open(file) as f: for line in f: @@ -26,6 +28,7 @@ def make_frequency_dict(file="huffman.txt"): return freq, text + def make_heap(freq): heap = [] for char in freq: @@ -34,20 +37,21 @@ def make_heap(freq): return heap + def build_tree(heap): # Create our binary tree - while (len(heap) > 1): + while len(heap) > 1: nodeL = heapq.heappop(heap) nodeR = heapq.heappop(heap) - tot_freq = nodeL.freq+nodeR.freq + tot_freq = nodeL.freq + nodeR.freq - heapq.heappush(heap, Node('', tot_freq, nodeL, nodeR)) + heapq.heappush(heap, Node("", tot_freq, nodeL, nodeR)) return heap -def create_mapping(root, map={}, binarytext=''): +def create_mapping(root, map={}, binarytext=""): # Create a mapping from each character to a binary string if root == None: @@ -57,13 +61,14 @@ def create_mapping(root, map={}, binarytext=''): # if we are a leaf map[root.ch] = binarytext - left = create_mapping(root.left, map, binarytext+'0') - right = create_mapping(root.right, map, binarytext+'1') + left = create_mapping(root.left, map, binarytext + "0") + right = create_mapping(root.right, map, binarytext + "1") return map + def decode(binarystring, root): - decoded_msg = '' + decoded_msg = "" curr_node = root i = 0 @@ -73,43 +78,46 @@ def decode(binarystring, root): decoded_msg += str(curr_node.ch) curr_node = root - if i == len(binarystring): i += 1 + if i == len(binarystring): + i += 1 # If 1 walk right - elif binarystring[i] == '1': + elif binarystring[i] == "1": curr_node = curr_node.right i += 1 # If 0 walk left - elif binarystring[i] == '0': + elif binarystring[i] == "0": curr_node = curr_node.left i += 1 return decoded_msg + def main(): freq, text = make_frequency_dict(file="Huffman.txt") - #print(f"Our message that we wish to decompress using Huffman is: \n{text}") + # print(f"Our message that we wish to decompress using Huffman is: \n{text}") heap = make_heap(freq) tree = build_tree(heap) mapping = create_mapping(tree[0]) - print(f'Our mapping is: \n{mapping}') + print(f"Our mapping is: \n{mapping}") # Get encoded message - encoded = '' + encoded = "" for letter in text: encoded += mapping[letter] - print(f'Our encoded message is: \n{encoded}') + print(f"Our encoded message is: \n{encoded}") out = bitarray(encoded) - with open('compressed_file.bin', 'wb') as f: + with open("compressed_file.bin", "wb") as f: out.tofile(f) - #original_text = decode(encoded, tree[0]) + # original_text = decode(encoded, tree[0]) + -if __name__ == '__main__': - main() \ No newline at end of file +if __name__ == "__main__": + main() diff --git a/Algorithms/other/Kadanes_algorithm.py b/Algorithms/other/Kadanes_algorithm.py index 2fc6abc..304bb8f 100644 --- a/Algorithms/other/Kadanes_algorithm.py +++ b/Algorithms/other/Kadanes_algorithm.py @@ -1,4 +1,4 @@ -''' +""" Purpose of the Kadane's Algorithm is to the find the sum of the maximum contigous subarray of an array. Ex: [-2,1,-3,4,-1,2,1,-5,4] has [4,-1,2,1] with the largest sum = 6 @@ -6,18 +6,19 @@ Programmed by Aladdin Persson # 2020-03-08 Initial programming -''' +""" + def kadane_algorithm(array): max_current, max_global = array[0], array[0] - + for val in array[1:]: max_current = max(val, max_current + val) - + if max_current > max_global: max_global = max_current - + return max_global -print(kadane_algorithm([-2,1,-3,4,-1,2,1,-5,4])) - + +print(kadane_algorithm([-2, 1, -3, 4, -1, 2, 1, -5, 4])) diff --git a/Algorithms/other/binarysearch.py b/Algorithms/other/binarysearch.py index 651880b..ba1cdcd 100644 --- a/Algorithms/other/binarysearch.py +++ b/Algorithms/other/binarysearch.py @@ -1,11 +1,11 @@ -''' +""" Purpose of this is to find a target element in an already sorted list L. We use the fact that it is already sorted and get a O(log(n)) search algorithm. Programmed by Aladdin Persson # 2019-03-12 Initial programming -''' +""" def binarysearch_iterative(L, target): @@ -13,7 +13,7 @@ def binarysearch_iterative(L, target): high = len(L) - 1 while low <= high: - middle = (low+high)//2 + middle = (low + high) // 2 if target == L[middle]: return True, middle @@ -26,8 +26,9 @@ def binarysearch_iterative(L, target): return False, None + def binarysearch_recursive(L, target, low, high): - middle = (low+high)//2 + middle = (low + high) // 2 if low > high: return False, None @@ -36,20 +37,18 @@ def binarysearch_recursive(L, target, low, high): return True, middle elif target < L[middle]: - return binarysearch_recursive(L, target, low, middle-1) + return binarysearch_recursive(L, target, low, middle - 1) else: - return binarysearch_recursive(L, target, middle+1, high) - + return binarysearch_recursive(L, target, middle + 1, high) -if __name__ == '__main__': +if __name__ == "__main__": target = 1 - sorted_array = [1,1,1,1,1,1,1,1] + sorted_array = [1, 1, 1, 1, 1, 1, 1, 1] exists, idx = binarysearch_iterative(sorted_array, target) - print(f'The target {target} exists in array: {exists}. The idx of it is: {idx}') - - exists, idx = binarysearch_recursive(sorted_array, target, 0, len(sorted_array)-1) - print(f'The target {target} exists in array: {exists}. The idx of it is: {idx}') + print(f"The target {target} exists in array: {exists}. The idx of it is: {idx}") + exists, idx = binarysearch_recursive(sorted_array, target, 0, len(sorted_array) - 1) + print(f"The target {target} exists in array: {exists}. The idx of it is: {idx}") diff --git a/Algorithms/other/counting_inversions.py b/Algorithms/other/counting_inversions.py index e14600a..ce0f870 100644 --- a/Algorithms/other/counting_inversions.py +++ b/Algorithms/other/counting_inversions.py @@ -1,16 +1,17 @@ def merge_sort(array): total_inversions = 0 if len(array) <= 1: - return (array,0) + return (array, 0) midpoint = int(len(array) / 2) (left, left_inversions) = merge_sort(array[:midpoint]) (right, right_inversions) = merge_sort(array[midpoint:]) (merged_array, merge_inversions) = merge_and_count(left, right) - + return (merged_array, left_inversions + right_inversions + merge_inversions) + def merge_and_count(left, right): count_inversions = 0 result = [] @@ -24,7 +25,7 @@ def merge_and_count(left, right): left_pointer += 1 elif right[right_pointer] < left[left_pointer]: - count_inversions += (left_len - left_pointer) + count_inversions += left_len - left_pointer result.append(right[right_pointer]) right_pointer += 1 @@ -33,6 +34,7 @@ def merge_and_count(left, right): return (result, count_inversions) + if __name__ == "__main__": array = [9, 2, 1, 5, 2, 3, 5, 1, 2, 32, 12, 11] print(array) diff --git a/Algorithms/other/interval_scheduling.py b/Algorithms/other/interval_scheduling.py index 67d6381..d02adc9 100644 --- a/Algorithms/other/interval_scheduling.py +++ b/Algorithms/other/interval_scheduling.py @@ -7,6 +7,7 @@ # Video: https://youtu.be/SmPxC8m0yIY + def interval_scheduling(R, O): R.sort(key=lambda x: x[1]) # sort by finish times f1 <= f2 <= ... <= fn @@ -20,6 +21,7 @@ def interval_scheduling(R, O): return O + # def interval_scheduling_complicated_version(R, O): # while R: # keep going while R still has elements # (si, fi) = R[0] @@ -36,7 +38,7 @@ def interval_scheduling(R, O): # idx += 1 # return O -if __name__ == '__main__': +if __name__ == "__main__": # run small example # request is: (start, end) r1 = (0, 3) @@ -48,8 +50,8 @@ def interval_scheduling(R, O): r7 = (5, 10) r8 = (8, 10) - R = [r1,r2,r3,r4,r5,r6,r7,r8] + R = [r1, r2, r3, r4, r5, r6, r7, r8] O = [] O = interval_scheduling(R, O) - print('The intervals to choose are: ' + str(O)) \ No newline at end of file + print("The intervals to choose are: " + str(O)) diff --git a/Algorithms/other/median_maintenance.py b/Algorithms/other/median_maintenance.py index 31b4669..2d53127 100644 --- a/Algorithms/other/median_maintenance.py +++ b/Algorithms/other/median_maintenance.py @@ -14,13 +14,14 @@ import heapq + class Maintain_Median(object): def __init__(self): self.maxheap = [] self.minheap = [] def medmain_insert(self, x): - if (len(self.maxheap) == 0): + if len(self.maxheap) == 0: heapq.heappush(self.maxheap, -x) else: @@ -39,8 +40,11 @@ def medmain_insert(self, x): y = -heapq.heappop(self.maxheap) heapq.heappush(self.minheap, y) - return (-self.maxheap[0] + self.minheap[0])/2 if len(self.maxheap) == len(self.minheap) else -self.maxheap[0] - + return ( + (-self.maxheap[0] + self.minheap[0]) / 2 + if len(self.maxheap) == len(self.minheap) + else -self.maxheap[0] + ) def main(self, data): if len(data) < 1: @@ -51,8 +55,9 @@ def main(self, data): return median -if __name__ == '__main__': + +if __name__ == "__main__": data = [1, 3, 8, 5, 10] maintain_median = Maintain_Median() median = maintain_median.main(data) - print(median) \ No newline at end of file + print(median) diff --git a/Algorithms/sorting/bubblesort.py b/Algorithms/sorting/bubblesort.py index 6f27ff9..625c80c 100644 --- a/Algorithms/sorting/bubblesort.py +++ b/Algorithms/sorting/bubblesort.py @@ -1,11 +1,12 @@ -''' +""" Bubblesort sorting algorithm. This is not a very efficient sorting algorithm, T(n) = O(n^2). Programmed by Aladdin Persson * 2019-01-23 Initial code * 2019-03-05 Improved code by having swapped, while-loop and raise error -''' +""" + def bubblesort(L): swapped = True @@ -14,12 +15,13 @@ def bubblesort(L): swapped = False for j in range(len(L) - 1): - if L[j] > L[j+1]: - L[j], L[j + 1] = L[j+1], L[j] + if L[j] > L[j + 1]: + L[j], L[j + 1] = L[j + 1], L[j] swapped = True return L -if __name__ == '__main__': - unsorted = [5,2,4,6,1,3] + +if __name__ == "__main__": + unsorted = [5, 2, 4, 6, 1, 3] sorted = bubblesort(unsorted) - print(sorted) \ No newline at end of file + print(sorted) diff --git a/Algorithms/sorting/hopesort.py b/Algorithms/sorting/hopesort.py index 567b455..c265b19 100644 --- a/Algorithms/sorting/hopesort.py +++ b/Algorithms/sorting/hopesort.py @@ -1,18 +1,20 @@ -''' +""" Hopesort algorithm. If it works, then it works in constant time. Use at own risk :) Programmed by Aladdin Persson * 2019-03-08 Initial code -''' +""" + def hopesort(L): # hope it is sorted return L -if __name__ == '__main__': - unsorted = [1,2,3,4,5,6] + +if __name__ == "__main__": + unsorted = [1, 2, 3, 4, 5, 6] # constant time hopefully_sorted = hopesort(unsorted) - print(hopefully_sorted) \ No newline at end of file + print(hopefully_sorted) diff --git a/Algorithms/sorting/insertionsort.py b/Algorithms/sorting/insertionsort.py index 7fa76fb..d466e2d 100644 --- a/Algorithms/sorting/insertionsort.py +++ b/Algorithms/sorting/insertionsort.py @@ -1,10 +1,11 @@ -''' +""" Insertion sort algorithm O(n^2). Programmed by Aladdin Persson * 2019-01-23 Initial programming * 2019-03-05 Made code cleaner -''' +""" + def insertionsort(L): # loop through all except first element in list @@ -15,7 +16,7 @@ def insertionsort(L): # inserts the key into the correct place while position >= 0 and L[position] > value: - L[position+1] = L[position] + L[position + 1] = L[position] position -= 1 L[position + 1] = value @@ -23,7 +24,7 @@ def insertionsort(L): return L -if __name__ == '__main__': - unsorted = [7,2,4,1,5,3] +if __name__ == "__main__": + unsorted = [7, 2, 4, 1, 5, 3] sorted = insertionsort(unsorted) - print(sorted) \ No newline at end of file + print(sorted) diff --git a/Algorithms/sorting/mergesort.py b/Algorithms/sorting/mergesort.py index 3387701..3a87d26 100644 --- a/Algorithms/sorting/mergesort.py +++ b/Algorithms/sorting/mergesort.py @@ -19,7 +19,7 @@ def merge(left, right): result.append(left[left_pointer]) left_pointer += 1 - elif right[right_pointer] < left[left_pointer] : + elif right[right_pointer] < left[left_pointer]: result.append(right[right_pointer]) right_pointer += 1 @@ -28,9 +28,10 @@ def merge(left, right): return result -if __name__ == "__main__": - array = [5, 4, 3, 2, 1] - print(array) - result = merge_sort(array) - print(result) +# if __name__ == "__main__": +# array = [5, 4, 3, 2, 1] +# print(array) +# +# result = merge_sort(array) +# print(result) diff --git a/Algorithms/sorting/quicksort.py b/Algorithms/sorting/quicksort.py index 722d18e..6d2ee26 100644 --- a/Algorithms/sorting/quicksort.py +++ b/Algorithms/sorting/quicksort.py @@ -1,109 +1,48 @@ # Quicksort with pivot always using first index as pivot -def quicksort_firstpivot(L): - # global count_comparisons +def quicksort_firstpivot(L): if len(L) <= 1: return L pivot = L[0] - # count_comparisons += len(x) - 1 - i = 0 for j in range(1, len(L)): if L[j] < pivot: - L[j], L[i+1] = L[i+1], L[j] + L[j], L[i + 1] = L[i + 1], L[j] i += 1 L[0], L[i] = L[i], L[0] left = quicksort_firstpivot(L[:i]) - right = quicksort_firstpivot(L[i+1:]) + right = quicksort_firstpivot(L[i + 1 :]) left.append(L[i]) result = left + right return result + # Quicksort with pivot always using last index as pivot def quicksort_lastpivot(x): - global count_comparisons - if len(x) <= 1: return x x[0], x[-1] = x[-1], x[0] pivot = x[0] - count_comparisons += len(x) - 1 - i = 0 for j in range(1, len(x)): if x[j] < pivot: - x[j], x[i+1] = x[i+1], x[j] + x[j], x[i + 1] = x[i + 1], x[j] i += 1 x[0], x[i] = x[i], x[0] left = quicksort_lastpivot(x[:i]) - right = quicksort_lastpivot(x[i+1:]) + right = quicksort_lastpivot(x[i + 1 :]) left.append(x[i]) result = left + right return result - -def find_median(x): - # this is horrible. - - if len(x) % 2 == 0: - middle_idx = len(x) // 2 - 1 - if len(x) % 2 != 0: - middle_idx = len(x) // 2 - - a = x[0] - b = x[middle_idx] - c = x[-1] - - if a > b: - if a < c: - median = 0 - elif b > c: - median = middle_idx - else: - median = len(x) - 1 - else: - if a > c: - median = 0 - elif b < c: - median = middle_idx - else: - median = len(x) - 1 - - return median - -def quicksort_median(x): - global count_comparisons - - if len(x) <= 1: - return x - - k = find_median(x) - x[0], x[k] = x[k], x[0] - pivot = x[0] - - count_comparisons += len(x) - 1 - - i = 0 - - for j in range(1, len(x)): - if x[j] < pivot: - x[j], x[i+1] = x[i+1], x[j] - i += 1 - - x[0], x[i] = x[i], x[0] - left = quicksort_median(x[:i]) - right = quicksort_median(x[i+1:]) - left.append(x[i]) - result = left + right - return result diff --git a/Algorithms/sorting/randomized_quicksort.py b/Algorithms/sorting/randomized_quicksort.py index 75e58fd..e47aa47 100644 --- a/Algorithms/sorting/randomized_quicksort.py +++ b/Algorithms/sorting/randomized_quicksort.py @@ -1,4 +1,4 @@ -''' +""" Purpose is to sort a list. The reason why randomizing is better is that on average, regardless on the input randomized quicksort will have a running time of O(n*logn). This is regardless of the ordering of the inputted list. This is in constrast to first pivot, median pivot, etc Quicksort. @@ -6,16 +6,17 @@ Programmed by Aladdin Persson * 2019-03-08 Initial programming -''' +""" import random + def quicksort_randomized(L): if len(L) <= 1: return L # Randomly choose a pivot idx - pivot_idx = random.randint(0, len(L)-1) + pivot_idx = random.randint(0, len(L) - 1) pivot = L[pivot_idx] # Swap pivot_idx to the first position @@ -25,20 +26,21 @@ def quicksort_randomized(L): # range(1, len(L)) because we the pivot element is the first element for j in range(1, len(L)): if L[j] < pivot: - L[j], L[i+1] = L[i+1], L[j] + L[j], L[i + 1] = L[i + 1], L[j] i += 1 L[0], L[i] = L[i], L[0] left = quicksort_randomized(L[:i]) - right = quicksort_randomized(L[i + 1:]) + right = quicksort_randomized(L[i + 1 :]) left.append(L[i]) result = left + right return result -if __name__ == '__main__': - l = [6,7,3,4,5,1,3,7,123] + +if __name__ == "__main__": + l = [6, 7, 3, 4, 5, 1, 3, 7, 123] sorted_l = quicksort_randomized(l) - print(sorted_l) \ No newline at end of file + print(sorted_l) diff --git a/Algorithms/sorting/selectionsort.py b/Algorithms/sorting/selectionsort.py index c3f3f8e..8d3aa5a 100644 --- a/Algorithms/sorting/selectionsort.py +++ b/Algorithms/sorting/selectionsort.py @@ -1,10 +1,10 @@ -''' +""" Selection sort algorithm. T(n) = O(n^2) Programmed by Aladdin Persson * 2019-03-05 Initial coding -''' +""" # Selection sort that creates a copy. More "intuitive" but requires extra memory. def selectionsort_intuitive(L): @@ -14,7 +14,7 @@ def selectionsort_intuitive(L): min = L[0] for element in L: - min=element if element < min else min + min = element if element < min else min correctly_sorted.append(min) L.remove(min) @@ -23,11 +23,11 @@ def selectionsort_intuitive(L): def selectionsort(L): - for i in range(len(L)-1): + for i in range(len(L) - 1): min_index = i # Look through entire list, look which is the smallest element - for j in range(i+1, len(L)): + for j in range(i + 1, len(L)): if L[j] < L[min_index]: min_index = j @@ -37,7 +37,8 @@ def selectionsort(L): return L -if __name__ == '__main__': - unsorted = [10000,2,7,4,1,5,3,15,13,169] + +if __name__ == "__main__": + unsorted = [10000, 2, 7, 4, 1, 5, 3, 15, 13, 169] sorted = selectionsort_intuitive(unsorted) - print(sorted) \ No newline at end of file + print(sorted) diff --git a/README.md b/README.md index f7462f0..115182e 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -[![Build Status](https://travis-ci.com/AladdinPerzon/Algorithms-Collection-Python.svg?branch=master)](https://travis-ci.com/AladdinPerzon/Algorithms-Collection-Python) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) +[![Build Status](https://travis-ci.com/AladdinPersson/Algorithms-Collection-Python.svg?branch=master)](https://travis-ci.com/AladdinPersson/Algorithms-Collection-Python) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) [![codecov](https://codecov.io/gh/aladdinpersson/Algorithms-Collection-Python/branch/master/graph/badge.svg)](https://codecov.io/gh/aladdinpersson/Algorithms-Collection-Python) # Algorithms Collection Python Whenever I face an interesting problem I document the algorithm that I learned to solve it. The goals of this repository is to have clean, efficient and most importantly correct code. @@ -7,64 +7,64 @@ Whenever I face an interesting problem I document the algorithm that I learned t :small_red_triangle:: If the algorithm is untested # Dynamic Programming -* :white_check_mark: [Knapsack 0/1](https://github.com/AladdinPerzon/Algorithms-Collection-Python/blob/master/Algorithms/dynamic_programming/knapsack/knapsack_bottomup.py) **O(nC) Bottom-Up implementation (Loops)** -* :white_check_mark: [:movie_camera:](https://youtu.be/XmyxiSc3LKg)[Sequence Alignment](https://github.com/AladdinPerzon/Algorithms-Collection-Python/blob/master/Algorithms/dynamic_programming/sequence_alignment.py) **O(nm)** -* :small_red_triangle: [:movie_camera:](https://youtu.be/dU-coYsd7zw)[Weighted Interval Scheduling](https://github.com/AladdinPerzon/Algorithms-Collection-Python/blob/master/Algorithms/dynamic_programming/weighted_interval_scheduling.py) **O(nlog(n))** +* :white_check_mark: [Knapsack 0/1](https://github.com/aladdinpersson/Algorithms-Collection-Python/blob/master/Algorithms/dynamic_programming/knapsack/knapsack_bottomup.py) **- O(nC) Bottom-Up implementation (Loops)** +* :white_check_mark: [:movie_camera:](https://youtu.be/XmyxiSc3LKg)[Sequence Alignment](https://github.com/aladdinpersson/Algorithms-Collection-Python/blob/master/Algorithms/dynamic_programming/sequence_alignment.py) **- O(nm)** +* :white_check_mark: [:movie_camera:](https://youtu.be/dU-coYsd7zw)[Weighted Interval Scheduling](https://github.com/aladdinpersson/Algorithms-Collection-Python/blob/master/Algorithms/dynamic_programming/weighted_interval_scheduling.py) **- O(nlog(n))** # Graph theory -* :small_red_triangle: [Kahns Topological Sort](https://github.com/AladdinPerzon/Algorithms-Collection-Python/blob/master/Algorithms/graphtheory/kahns-toposort/kahns.py) **- O(n + m)** -* :small_red_triangle: [Bellman-Ford Shortest Path](https://github.com/AladdinPerzon/Algorithms-Collection-Python/blob/master/Algorithms/graphtheory/bellman-ford/bellman_ford.py) **- Unoptimized implementation** -* :small_red_triangle: [Floyd-Warshall Shortest Path](https://github.com/AladdinPerzon/Algorithms-Collection-Python/blob/master/Algorithms/graphtheory/floyd-warshall/floyd-warshall.py) **- O(n3)** -* :small_red_triangle: [Dijkstra Shortest Path](https://github.com/AladdinPerzon/Algorithms-Collection-Python/blob/master/Algorithms/graphtheory/dijkstra/djikstra.py) **- Naive implementation** -* :small_red_triangle: [Dijkstra Shortest Path](https://github.com/AladdinPerzon/Algorithms-Collection-Python/blob/master/Algorithms/graphtheory/dijkstra/heapdijkstra.py) **O(mlog(n)) - Heap implementation** -* :small_red_triangle: [Karger's Minimum cut](https://github.com/AladdinPerzon/Algorithms-Collection-Python/blob/master/Algorithms/graphtheory/kargers/kargermincut.py) -* :small_red_triangle: [Prim's Algorithm](https://github.com/AladdinPerzon/Algorithms-Collection-Python/blob/master/Algorithms/graphtheory/prims/prims_algorithm.py) **- Naive implementation** -* :small_red_triangle: [Prim's Algorithm](https://github.com/AladdinPerzon/Algorithms-Collection-Python/blob/master/Algorithms/graphtheory/prims/primheap.py) **- mlog(n)** -* :small_red_triangle: [Kruskal's Algorithm](https://github.com/AladdinPerzon/Algorithms-Collection-Python/blob/master/Algorithms/graphtheory/kruskal/kruskal.py) **- Naive implementation** -* :small_red_triangle: [Kruskal's Algorithm](https://github.com/AladdinPerzon/Algorithms-Collection-Python/blob/master/Algorithms/graphtheory/kruskal/kruskal_unionfind.py) **- mlog(n)** -* :small_red_triangle: [Breadth First Search](https://github.com/AladdinPerzon/Algorithms-Collection-Python/blob/master/Algorithms/graphtheory/breadth-first-search/BFS_queue_iterative.py) **O(n + m) - Queue Implementation** -* :small_red_triangle: [Depth First Search](https://github.com/AladdinPerzon/Algorithms-Collection-Python/blob/master/Algorithms/graphtheory/depth-first-search/DFS_stack_iterative.py) **O(n + m) - Stack Implementation** -* :small_red_triangle: [Depth First Search](https://github.com/AladdinPerzon/Algorithms-Collection-Python/blob/master/Algorithms/graphtheory/depth-first-search/DFS_recursive.py) **O(n + m) - Recursive Implementation** +* :white_check_mark: [Kahns Topological Sort](https://github.com/aladdinpersson/Algorithms-Collection-Python/blob/master/Algorithms/graphtheory/kahns-toposort/kahn_topological_ordering.py) **- O(n + m)** +* :white_check_mark: [Bellman-Ford Shortest Path](https://github.com/aladdinpersson/Algorithms-Collection-Python/blob/master/Algorithms/graphtheory/bellman-ford/bellman_ford.py) **- O(mn)** +* :small_red_triangle: [Floyd-Warshall Shortest Path](https://github.com/aladdinpersson/Algorithms-Collection-Python/blob/master/Algorithms/graphtheory/floyd-warshall/floyd-warshall.py) **- O(n3)** +* :white_check_mark: [Dijkstra Shortest Path](https://github.com/aladdinpersson/Algorithms-Collection-Python/blob/master/Algorithms/graphtheory/dijkstra/dijkstra.py) **- Naive implementation** +* :white_check_mark: [Dijkstra Shortest Path](https://github.com/aladdinpersson/Algorithms-Collection-Python/blob/master/Algorithms/graphtheory/dijkstra/heapdijkstra.py) **- O(mlog(n)) - Heap implementation** +* :small_red_triangle: [Karger's Minimum cut](https://github.com/aladdinpersson/Algorithms-Collection-Python/blob/master/Algorithms/graphtheory/kargers/kargermincut.py) +* :small_red_triangle: [Prim's Algorithm](https://github.com/aladdinpersson/Algorithms-Collection-Python/blob/master/Algorithms/graphtheory/prims/prim_naive.py) **- O(mn) Naive implementation** +* :white_check_mark: [Prim's Algorithm](https://github.com/aladdinpersson/Algorithms-Collection-Python/blob/master/Algorithms/graphtheory/prims/prim_heap.py) **- O(mlog(n)) Heap implementation** +* :small_red_triangle: [Kruskal's Algorithm](https://github.com/aladdinpersson/Algorithms-Collection-Python/blob/master/Algorithms/graphtheory/kruskal/kruskal.py) **- O(mn) implementation** +* :white_check_mark: [Kruskal's Algorithm](https://github.com/aladdinpersson/Algorithms-Collection-Python/blob/master/Algorithms/graphtheory/kruskal/kruskal_unionfind.py) **- O(mlog(n))** +* :white_check_mark: [Breadth First Search](https://github.com/aladdinpersson/Algorithms-Collection-Python/blob/master/Algorithms/graphtheory/breadth-first-search/BFS_queue_iterative.py) **- O(n + m) - Queue Implementation** +* :white_check_mark: [Depth First Search](https://github.com/aladdinpersson/Algorithms-Collection-Python/blob/master/Algorithms/graphtheory/depth-first-search/DFS_stack_iterative.py) **- O(n + m) - Stack Implementation** +* :white_check_mark: [Depth First Search](https://github.com/aladdinpersson/Algorithms-Collection-Python/blob/master/Algorithms/graphtheory/depth-first-search/DFS_recursive.py) **- O(n + m) - Recursive Implementation** # Mathematics ### Algebra -* :small_red_triangle: [Karatsuba Multiplication](https://github.com/AladdinPerzon/Algorithms-Collection-Python/blob/master/Algorithms/math/karatsuba/karatsuba.py) **- O(n1.585)** -* :small_red_triangle: [Intersection of two sets](https://github.com/AladdinPerzon/Algorithms-Collection-Python/blob/master/Algorithms/math/intersection_of_two_sets/intersection_of_two_sets.py) **- O(nlog(n)) + O(mlog(m))** -* :small_red_triangle: [Union of two sets](https://github.com/AladdinPerzon/Algorithms-Collection-Python/blob/master/Algorithms/math/union_of_two_sets/union_of_two_sets.py) **- O(nlog(n)) + O(mlog(m))** +* :small_red_triangle: [Karatsuba Multiplication](https://github.com/aladdinpersson/Algorithms-Collection-Python/blob/master/Algorithms/math/karatsuba/karatsuba.py) **- O(n1.585)** +* :white_check_mark: [Intersection of two sets](https://github.com/aladdinpersson/Algorithms-Collection-Python/blob/master/Algorithms/math/intersection_of_two_sets/intersection_of_two_sets.py) **- O(nlog(n)) + O(mlog(m))** +* :white_check_mark: [Union of two sets](https://github.com/aladdinpersson/Algorithms-Collection-Python/blob/master/Algorithms/math/union_of_two_sets/union_of_two_sets.py) **- O(nlog(n)) + O(mlog(m))** ### Number Theory -* :small_red_triangle: [Pollard p-1 factorization](https://github.com/AladdinPerzon/Algorithms-Collection-Python/blob/master/Algorithms/math/pollard_p1/pollard_p1.py) -* :small_red_triangle: [Euclidean Algorithm](https://github.com/AladdinPerzon/Algorithms-Collection-Python/blob/master/Algorithms/math/euclid_gcd/euclid_gcd.py) **- O(log(n))** -* :small_red_triangle: [Extended Euclidean Algorithm](https://github.com/AladdinPerzon/Algorithms-Collection-Python/blob/master/Algorithms/math/extended_euclidean_algorithm/euclid_gcd.py) **- O(log(n))** -* :small_red_triangle: [Sieve of Eratosthenes](https://github.com/AladdinPerzon/Algorithms-Collection-Python/blob/master/Algorithms/math/sieve_of_eratosthenes/sieve_eratosthenes.py) **- O(nlog(log(n)))** -* :small_red_triangle: [Prime factorization](https://github.com/AladdinPerzon/Algorithms-Collection-Python/blob/master/Algorithms/math/prime_factorization/primefactorization.py) **- O(sqrt(n))** +* :small_red_triangle: [Pollard p-1 factorization](https://github.com/aladdinpersson/Algorithms-Collection-Python/blob/master/Algorithms/math/pollard_p1/pollard_p1.py) +* :small_red_triangle: [Euclidean Algorithm](https://github.com/aladdinpersson/Algorithms-Collection-Python/blob/master/Algorithms/math/euclid_gcd/euclid_gcd.py) **- O(log(n))** +* :small_red_triangle: [Extended Euclidean Algorithm](https://github.com/aladdinpersson/Algorithms-Collection-Python/blob/master/Algorithms/math/extended_euclidean_algorithm/euclid_gcd.py) **- O(log(n))** +* :small_red_triangle: [Sieve of Eratosthenes](https://github.com/aladdinpersson/Algorithms-Collection-Python/blob/master/Algorithms/math/sieve_of_eratosthenes/sieve_eratosthenes.py) **- O(nlog(log(n)))** +* :small_red_triangle: [Prime factorization](https://github.com/aladdinpersson/Algorithms-Collection-Python/blob/master/Algorithms/math/prime_factorization/primefactorization.py) **- O(sqrt(n))** ### Cryptography -* :small_red_triangle: [Ceasar Cipher](https://github.com/AladdinPerzon/Algorithms-Collection-Python/blob/master/Algorithms/cryptology/ceasar_shifting_cipher/ceasar_shift_cipher.py) -* :small_red_triangle: [Hill Cipher](https://github.com/AladdinPerzon/Algorithms-Collection-Python/blob/master/Algorithms/cryptology/hill_cipher/hill_cipher.py) -* :small_red_triangle: [Vigenere Cipher](https://github.com/AladdinPerzon/Algorithms-Collection-Python/blob/master/Algorithms/cryptology/vigenere_cipher/vigenere.py)* -* :small_red_triangle: [One time pad](https://github.com/AladdinPerzon/Algorithms-Collection-Python/blob/master/Algorithms/cryptology/one_time_pad/one_time_pad.py) -* :small_red_triangle: [RSA-Algorithm](https://github.com/AladdinPerzon/Algorithms-Collection-Python/blob/master/Algorithms/cryptology/RSA_algorithm/RSA.py) +* :white_check_mark: [Ceasar Cipher](https://github.com/aladdinpersson/Algorithms-Collection-Python/blob/master/Algorithms/cryptology/ceasar_shifting_cipher/ceasar_shift_cipher.py) +* :small_red_triangle: [Hill Cipher](https://github.com/aladdinpersson/Algorithms-Collection-Python/blob/master/Algorithms/cryptology/hill_cipher/hill_cipher.py) +* :small_red_triangle: [Vigenere Cipher](https://github.com/aladdinpersson/Algorithms-Collection-Python/blob/master/Algorithms/cryptology/vigenere_cipher/vigenere.py)* +* :small_red_triangle: [One time pad](https://github.com/aladdinpersson/Algorithms-Collection-Python/blob/master/Algorithms/cryptology/one_time_pad/one_time_pad.py) +* :small_red_triangle: [RSA-Algorithm](https://github.com/aladdinpersson/Algorithms-Collection-Python/blob/master/Algorithms/cryptology/RSA_algorithm/RSA.py) ### Numerical Methods -* :small_red_triangle: [Bisection Method](https://github.com/AladdinPerzon/Algorithms-Collection-Python/blob/master/Algorithms/numerical_methods/bisection.py) -* :small_red_triangle: [(simple) Fixpoint iteration](https://github.com/AladdinPerzon/Algorithms-Collection-Python/blob/master/Algorithms/numerical_methods/fixpoint.py) +* :small_red_triangle: [Bisection Method](https://github.com/aladdinpersson/Algorithms-Collection-Python/blob/master/Algorithms/numerical_methods/bisection.py) +* :small_red_triangle: [(simple) Fixpoint iteration](https://github.com/aladdinpersson/Algorithms-Collection-Python/blob/master/Algorithms/numerical_methods/fixpoint.py) # Other -* :small_red_triangle: [Maintaining Median](https://github.com/AladdinPerzon/Algorithms-Collection-Python/blob/master/Algorithms/other/median_maintenance.py) -* :small_red_triangle: [Huffman Algorithm](https://github.com/AladdinPerzon/Algorithms-Collection-Python/blob/master/Algorithms/other/Huffman/Huffman.py) -* :white_check_mark: [:movie_camera:](https://youtu.be/SmPxC8m0yIY)[Interval Scheduling](https://github.com/AladdinPerzon/Algorithms-Collection-Python/blob/master/Algorithms/other/interval_scheduling.py) **- O(nlog(n))** -* :white_check_mark: [Binary Search](https://github.com/AladdinPerzon/Algorithms-Collection-Python/blob/master/Algorithms/search/binarysearch.py) **- O(log(n))** +* :white_check_mark: [Maintaining Median](https://github.com/aladdinpersson/Algorithms-Collection-Python/blob/master/Algorithms/other/median_maintenance.py) **- O(nlog(n))** +* :small_red_triangle: [Huffman Algorithm](https://github.com/aladdinpersson/Algorithms-Collection-Python/blob/master/Algorithms/other/Huffman/Huffman.py) +* :white_check_mark: [:movie_camera:](https://youtu.be/SmPxC8m0yIY)[Interval Scheduling](https://github.com/aladdinpersson/Algorithms-Collection-Python/blob/master/Algorithms/other/interval_scheduling.py) **- O(nlog(n))** +* :white_check_mark: [Binary Search](https://github.com/aladdinpersson/Algorithms-Collection-Python/blob/master/Algorithms/other/binarysearch.py) **- O(log(n))** # Sorting algorithms -* :white_check_mark: [Bubble sort](https://github.com/AladdinPerzon/Algorithms-Collection-Python/blob/master/Algorithms/sorting/bubblesort.py) **- O(n2)** -* :small_red_triangle: [Hope sort](https://github.com/AladdinPerzon/Algorithms-Collection-Python/blob/master/Algorithms/sorting/hopesort.py) **- O(1), hopefully ** -* :white_check_mark: [Insertion sort](https://github.com/AladdinPerzon/Algorithms-Collection-Python/blob/master/Algorithms/sorting/insertionsort.py) **- O(n2)** -* :white_check_mark: [Selection sort](https://github.com/AladdinPerzon/Algorithms-Collection-Python/blob/master/Algorithms/sorting/selectionsort.py) **- O(n2)** -* :white_check_mark: [Merge sort](https://github.com/AladdinPerzon/Algorithms-Collection-Python/blob/master/Algorithms/sorting/mergesort.py) **- O(nlog(n))** -* :white_check_mark: [Randomized Quick sort](https://github.com/AladdinPerzon/Algorithms-Collection-Python/blob/master/Algorithms/sorting/randomized_quicksort.py) **- Average O(nlogn) (Input independent!)** -* :white_check_mark: [Quick sort](https://github.com/AladdinPerzon/Algorithms-Collection-Python/blob/master/Algorithms/sorting/quicksort.py) **- Average O(nlog(n))** +* :white_check_mark: [Bubble sort](https://github.com/aladdinpersson/Algorithms-Collection-Python/blob/master/Algorithms/sorting/bubblesort.py) **- O(n2)** +* :small_red_triangle: [Hope sort](https://github.com/aladdinpersson/Algorithms-Collection-Python/blob/master/Algorithms/sorting/hopesort.py) **- O(1), hopefully** +* :white_check_mark: [Insertion sort](https://github.com/aladdinpersson/Algorithms-Collection-Python/blob/master/Algorithms/sorting/insertionsort.py) **- O(n2)** +* :white_check_mark: [Selection sort](https://github.com/aladdinpersson/Algorithms-Collection-Python/blob/master/Algorithms/sorting/selectionsort.py) **- O(n2)** +* :white_check_mark: [Merge sort](https://github.com/aladdinpersson/Algorithms-Collection-Python/blob/master/Algorithms/sorting/mergesort.py) **- O(nlog(n))** +* :white_check_mark: [Randomized Quick sort](https://github.com/aladdinpersson/Algorithms-Collection-Python/blob/master/Algorithms/sorting/randomized_quicksort.py) **- Average O(nlogn) (Input independent!)** +* :white_check_mark: [Quick sort](https://github.com/aladdinpersson/Algorithms-Collection-Python/blob/master/Algorithms/sorting/quicksort.py) **- Average O(nlog(n))** # Contributing I appreciate feedback on potential improvements and/or if you see an error that I've made! Also if you would like to contribute then do a pull request with algorithm & tests! :) diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..c833bdb --- /dev/null +++ b/requirements.txt @@ -0,0 +1,5 @@ +unionfind==0.0.10 +numpy==1.18.1 +bitarray==1.2.1 +egcd==0.0.1.1 +sympy==1.5.1 diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..4369946 --- /dev/null +++ b/setup.py @@ -0,0 +1,9 @@ +from distutils.core import setup + +setup( + name="Algorithms", + packages=["Algorithm_test"], + version="0.0.7", + description="Testing Algorithm Collection", + url="https://github.com/aladdinpersson/Algorithms-Collection-Python", +)