├── README.md ├── ch-01-arrays-and-strings ├── 01-is-unique.py ├── 02-is-permutation.py ├── 03-urlify.py ├── 04-is-palindrome-permutation.py ├── 05-one-away.py ├── 06-string-compression.py ├── 07-rotate-matrix.py ├── 08-zero-matrix.py └── 09-is-rotation.py ├── ch-02-linked-lists ├── 01-remove-duplicates.py ├── 02-kth-to-last.py ├── 03-delete-middle.py ├── 04-partition.py ├── 05-sum-lists.py ├── 06-palindrome.py ├── 07-intersection.py └── 08-loop-detection.py ├── ch-03-stacks-and-queues ├── 01-three-in-one.py ├── 02-stack-min.py ├── 03-stack-of-plates.py ├── 04-queue-via-stacks.py ├── 05-sort-stack.py └── 06-animal-shelter.py ├── ch-04-trees-and-graphs ├── 01-route-between-nodes.py ├── 02-minimal-tree.py ├── 03-list-of-depths.py ├── 04-check-balanced.py ├── 05-validate-bst.py ├── 06-successor.py ├── 07-build-order.py ├── 08-first-ancestor.py ├── 09-bst-sequences.py ├── 10-is-subtree.py ├── 11-random-node.py └── 12-paths-with-sum.py ├── ch-05-bit-manipulation ├── 01-insertion.py ├── 02-binary-to-string.py ├── 03-flip-bit-to-win.py ├── 04-next-number.py ├── 05-debugger.py ├── 06-conversion.py ├── 07-pairwise-swap.py └── 08-draw-line.py ├── ch-06-math-and-logic-puzzles ├── 01-pill-bottles.py ├── 02-basketball.py ├── 03-dominoes.py ├── 04-ants-on-a-triangle.py ├── 05-jugs-of-water.py ├── 06-blue-eyed-island.py ├── 07-the-apocalypse.py ├── 08-the-egg-drop-problem.py ├── 09-lockers.py └── 10-poison.py ├── ch-07-object-oriented-design ├── 01-deck-of-cards.py ├── 02-call-center.py ├── 03-jukebox.py ├── 04-parking-lot.py ├── 05-online-book-reader.py ├── 06-jigsaw.py ├── 07-chat-server.py ├── 08-othello.py ├── 09-circular-array.py ├── 10-minesweeper.py ├── 11-file-system.py └── 12-hash-table.py ├── ch-08-recursion-and-dynamic-programming ├── 01-triple-step.py ├── 02-robot-in-a-grid.py ├── 03-magic-index.py ├── 04-power-set.py ├── 05-recursive-multiply.py ├── 06-towers-of-hanoi.py ├── 07-permutations-without-dups.py ├── 08-permutations-with-dups.py ├── 09-parens.py ├── 10-paint-fill.py ├── 11-coins.py ├── 12-eight-queens.py ├── 13-stacks-of-boxes.py └── 14-boolean-evaluation.py ├── ch-09-system-design-and-scalability ├── .gitignore └── 01-stock-data.py ├── ch-10-sorting-and-searching ├── 01-sorted-merge.py ├── 02-group-anagrams.py ├── 03-search-in-rotated-array.py ├── 04-sorted-search-no-size.py ├── 05-sparse-search.py ├── 06-sort-big-file.py ├── 07-missing-int.py ├── 08-find-duplicates.py ├── 09-sorted-matrix-search.py ├── 10-rank-from-stream.py └── 11-peaks-and-valleys.py ├── ch-11-testing ├── 01-mistake.py ├── 02-random-crashes.py ├── 03-chess-test.py ├── 04-no-test-tools.py ├── 05-test-a-pen.py └── 06-test-an-atm.py ├── ch-12-c-and-c-plus-plus ├── .gitignore ├── 01-last-k-lines.py ├── Makefile └── lastklines.cpp ├── ch-13-java ├── .gitignore ├── 08-lambda-random.py ├── RandoSet.java └── build.xml ├── ch-14-databases ├── .gitignore ├── 04-joins.py └── join-example.db ├── ch-15-threads-and-locks └── 03-dining-philosophers.py ├── ch-16-moderate ├── 01-number-swapper.py ├── 02-word-frequencies.py ├── 03-intersection.py ├── 04-tic-tac-win.py ├── 05-factorial-zeros.py ├── 06-smallest-difference.py ├── 07-number-max.py ├── 08-english-int.py ├── 09-operations.py ├── 10-living-people.py ├── 11-diving-board.py ├── 12-xml-encoding.py ├── 13-bisect-squares.py ├── 14-best-line.py ├── 15-mastermind.py ├── 16-sub-sort.py ├── 17-contiguous-sequence.py ├── 18-pattern-matching.py ├── 19-pond-sizes.py ├── 20-t-nine.py ├── 21-sum-swap.py ├── 22-langtons-ant.py ├── 23-rand7-from-rand5.py ├── 24-pairs-with-sum.py ├── 25-lru-cache.py └── 26-calculator.py └── ch-17-hard └── 01-add-without-plus.py /README.md: -------------------------------------------------------------------------------- 1 | # CtCI Solutions 2 | 3 | This repository will contain Python solutions to the problems in the sixth 4 | edition of 5 | [Cracking the Coding Interview](http://www.crackingthecodinginterview.com/) 6 | by [Gayle Lackmann McDowell](http://www.gayle.com/). 7 | Note that [many other solutions](https://github.com/careercup/CtCI-6th-Edition) 8 | are available from CareerCup. 9 | 10 | Problems 4 and 8 from chapter 7 are particularly noteworthy. 11 | Problem 4 allows you to park cars, and problem 8 provides a simple Othello bot. 12 | You can play against the Othello bot with `python 08-othello.py play`. 13 | Good luck! 14 | 15 | Some of these solutions may be asymptotically slower than the official 16 | solutions. Please report any errors you find! At least one (8.11 `coins2`) 17 | is asymptotically faster than the solution given in the book. 18 | -------------------------------------------------------------------------------- /ch-01-arrays-and-strings/01-is-unique.py: -------------------------------------------------------------------------------- 1 | # Determine whether or not a given string contains no duplicate characters. 2 | 3 | def contains_no_duplicates(string): 4 | letters = {} 5 | for letter in string: 6 | if letter in letters: 7 | return False 8 | letters[letter] = True 9 | return True 10 | 11 | if __name__ == "__main__": 12 | import sys 13 | print(contains_no_duplicates(sys.argv[-1])) 14 | 15 | -------------------------------------------------------------------------------- /ch-01-arrays-and-strings/02-is-permutation.py: -------------------------------------------------------------------------------- 1 | # Determine whether or not one string is a permutation of another. 2 | 3 | def is_permutation(str1, str2): 4 | counter = Counter() 5 | for letter in str1: 6 | counter[letter] += 1 7 | for letter in str2: 8 | if not letter in counter: 9 | return False 10 | counter[letter] -= 1 11 | if counter[letter] == 0: 12 | del counter[letter] 13 | return len(counter) == 0 14 | 15 | class Counter(dict): 16 | def __missing__(self, key): 17 | return 0 18 | 19 | if __name__ == "__main__": 20 | import sys 21 | print(is_permutation(sys.argv[-2], sys.argv[-1])) 22 | -------------------------------------------------------------------------------- /ch-01-arrays-and-strings/03-urlify.py: -------------------------------------------------------------------------------- 1 | # Replace spaces in the middle of a string with "%20" assuming the end of the 2 | # string contains twice as many spaces as are in the middle. 3 | 4 | def escape_spaces_1(string): 5 | return string.strip().replace(" ", "%20") 6 | 7 | def escape_spaces_2(string): 8 | # Convert string to list to prepare to be modified 9 | letters = list(string) 10 | i = len(letters) - 1 11 | j = i 12 | while letters[i] == " ": 13 | i -= 1 14 | while j != i: 15 | # Replace space with %20 16 | if letters[i] == " ": 17 | letters[j-2] = "%" 18 | letters[j-1] = "2" 19 | letters[j] = "0" 20 | j -= 2 21 | # Copy the original character 22 | else: 23 | letters[j] = letters[i] 24 | i -= 1 25 | j -= 1 26 | return ''.join(letters) 27 | 28 | if __name__ == "__main__": 29 | import sys 30 | print(escape_spaces_1(sys.argv[-1])) 31 | print(escape_spaces_2(sys.argv[-1])) 32 | -------------------------------------------------------------------------------- /ch-01-arrays-and-strings/04-is-palindrome-permutation.py: -------------------------------------------------------------------------------- 1 | # Determine whether or not a string is a permutation of a palindrome, 2 | # ignoring spaces. 3 | 4 | def is_palindrome_permutation(string): 5 | counter = Counter() 6 | for letter in string.replace(" ", ""): 7 | counter[letter] += 1 8 | #return sum([count % 2 for count in counter.values()]) < 2 9 | odd_counts = 0 10 | for count in counter.values(): 11 | odd_counts += count % 2 12 | if odd_counts > 1: 13 | return False 14 | return True 15 | 16 | class Counter(dict): 17 | def __missing__(self, key): 18 | return 0 19 | 20 | if __name__ == "__main__": 21 | import sys 22 | print(is_palindrome_permutation(sys.argv[-1])) 23 | -------------------------------------------------------------------------------- /ch-01-arrays-and-strings/05-one-away.py: -------------------------------------------------------------------------------- 1 | # Determine whether the edit distance between two strings is less than 2. 2 | 3 | def one_away(str1, str2): 4 | len_diff = abs(len(str1) - len(str2)) 5 | if len_diff > 1: 6 | return False 7 | elif len_diff == 0: 8 | difference_count = 0 9 | for i in xrange(len(str1)): 10 | if str1[i] != str2[i]: 11 | difference_count += 1 12 | if difference_count > 1: 13 | return False 14 | return True 15 | else: 16 | if len(str1) > len(str2): 17 | longer, shorter = str1, str2 18 | else: 19 | longer, shorter = str2, str1 20 | shift = 0 21 | for i in xrange(len(shorter)): 22 | if shorter[i] != longer[i + shift]: 23 | if shift or (shorter[i] != longer[i + 1]): 24 | return False 25 | shift = 1 26 | return True 27 | 28 | if __name__ == "__main__": 29 | import sys 30 | print(one_away(sys.argv[-2], sys.argv[-1])) 31 | -------------------------------------------------------------------------------- /ch-01-arrays-and-strings/06-string-compression.py: -------------------------------------------------------------------------------- 1 | # Compress a string made up of letters by replacing each substring containing 2 | # a single type of letter by that letter followed by the count if the result 3 | # is shorter than the original. 4 | 5 | def compress(string): 6 | if len(string) == 0: 7 | return string 8 | parts = [] 9 | current_letter = string[0] 10 | current_count = 1 11 | for letter in string[1:]: 12 | if current_letter == letter: 13 | current_count += 1 14 | else: 15 | parts.append(current_letter + str(current_count)) 16 | current_letter = letter 17 | current_count = 1 18 | parts.append(current_letter + str(current_count)) 19 | compressed = "".join(parts) 20 | if len(compressed) < len(string): 21 | return compressed 22 | else: 23 | return string 24 | 25 | if __name__ == "__main__": 26 | import sys 27 | print(compress(sys.argv[-1])) 28 | -------------------------------------------------------------------------------- /ch-01-arrays-and-strings/07-rotate-matrix.py: -------------------------------------------------------------------------------- 1 | # Rotate a square matrix by 90 degrees counter-clockwise about its center. 2 | 3 | # TODO Use a matrix instead of a two-dimensional list. 4 | 5 | import unittest 6 | 7 | def rotate_matrix(m): 8 | n = len(m) 9 | rotm = [None] * n 10 | for row in xrange(n): 11 | rotm[row] = [None] * n 12 | for row in xrange(n): 13 | for col in xrange(n): 14 | rotm[n - col - 1][row] = m[row][col] 15 | return rotm 16 | 17 | def rotate_matrix_in_place(m): 18 | n = len(m) 19 | for col in xrange(n/2): 20 | for row in xrange(col, n - col - 1): 21 | temp1 = m[n - col - 1][row] 22 | m[n - col - 1][row] = m[row][col] 23 | temp2 = m[n - row - 1][n - col - 1] 24 | m[n - row - 1][n - col - 1] = temp1 25 | temp1 = m[col][n - row - 1] 26 | m[col][n - row - 1] = temp2 27 | m[row][col] = temp1 28 | 29 | class Test(unittest.TestCase): 30 | def test_rotate_matrix(self): 31 | mat1 = [[1,2],[3,4]] 32 | mat2 = [[2,4],[1,3]] 33 | self.assertEqual(rotate_matrix(mat1), mat2) 34 | mat3 = [[1,2,3],[4,5,6],[7,8,9]] 35 | mat4 = [[3,6,9],[2,5,8],[1,4,7]] 36 | self.assertEqual(rotate_matrix(mat3), mat4) 37 | mat5 = [[1,2,3,4],[5,6,7,8],[9,10,11,12],[13,14,15,16]] 38 | mat6 = [[4,8,12,16],[3,7,11,15],[2,6,10,14],[1,5,9,13]] 39 | self.assertEqual(rotate_matrix(mat5), mat6) 40 | 41 | def test_rotate_matrix_in_place(self): 42 | mat1 = [[1,2],[3,4]] 43 | mat2 = [[2,4],[1,3]] 44 | rotate_matrix_in_place(mat1) 45 | self.assertEqual(mat1, mat2) 46 | mat3 = [[1,2,3],[4,5,6],[7,8,9]] 47 | mat4 = [[3,6,9],[2,5,8],[1,4,7]] 48 | rotate_matrix_in_place(mat3) 49 | self.assertEqual(mat3, mat4) 50 | mat5 = [[1,2,3,4],[5,6,7,8],[9,10,11,12],[13,14,15,16]] 51 | mat6 = [[4,8,12,16],[3,7,11,15],[2,6,10,14],[1,5,9,13]] 52 | rotate_matrix_in_place(mat5) 53 | self.assertEqual(mat5, mat6) 54 | 55 | if __name__ == "__main__": 56 | unittest.main() 57 | -------------------------------------------------------------------------------- /ch-01-arrays-and-strings/08-zero-matrix.py: -------------------------------------------------------------------------------- 1 | # Given a matrix, zero out every row and column that contains a zero. 2 | 3 | import unittest 4 | 5 | def zero_out_row_col(mat): 6 | n = len(mat) 7 | if n == 0: 8 | return mat 9 | m = len(mat[0]) 10 | zero_rows, zero_cols = [], [] 11 | for r in xrange(n): 12 | for c in xrange(m): 13 | if mat[r][c] == 0: 14 | zero_rows.append(r) 15 | zero_cols.append(c) 16 | break 17 | for r in zero_rows: 18 | for c in xrange(m): 19 | mat[r][c] = 0 20 | for c in zero_cols: 21 | for r in xrange(n): 22 | mat[r][c] = 0 23 | 24 | class Test(unittest.TestCase): 25 | def test_zero_out_row_col_matrix(self): 26 | mat1 = [[1,1,1,1,1],[1,0,1,1,1],[1,1,1,1,1],[1,1,1,0,1],[2,3,4,5,6]] 27 | mat2 = [[1,0,1,0,1],[0,0,0,0,0],[1,0,1,0,1],[0,0,0,0,0],[2,0,4,0,6]] 28 | zero_out_row_col(mat1) 29 | self.assertEqual(mat1, mat2) 30 | 31 | if __name__ == "__main__": 32 | unittest.main() 33 | -------------------------------------------------------------------------------- /ch-01-arrays-and-strings/09-is-rotation.py: -------------------------------------------------------------------------------- 1 | # Determine whether or not a given string is a rotation of another string. 2 | 3 | import unittest 4 | 5 | def is_rotation(s1, s2): 6 | if len(s1) != len(s2): 7 | return False 8 | return is_substring(s1 + s1, s2) 9 | 10 | def is_substring(s1, s2): 11 | if len(s2) > len(s1): 12 | return False 13 | for i in xrange(len(s1) - len(s2) + 1): 14 | is_substring_here = True 15 | for j in xrange(len(s2)): 16 | if s1[i + j] != s2[j]: 17 | is_substring_here = False 18 | break 19 | if is_substring_here: 20 | return True 21 | return False 22 | 23 | class Test(unittest.TestCase): 24 | def test_is_rotation(self): 25 | s1 = "tabletop" 26 | s2 = "toptable" 27 | s3 = "optalbet" 28 | self.assertTrue(is_rotation(s1, s2)) 29 | self.assertFalse(is_rotation(s1, s3)) 30 | 31 | def test_is_substring(self): 32 | s1 = "cat in the hat" 33 | s2 = "cat" 34 | s3 = "hat" 35 | s4 = "cats" 36 | self.assertTrue(is_substring(s1, s2)) 37 | self.assertTrue(is_substring(s1, s3)) 38 | self.assertFalse(is_substring(s1, s4)) 39 | 40 | if __name__ == "__main__": 41 | unittest.main() 42 | -------------------------------------------------------------------------------- /ch-02-linked-lists/01-remove-duplicates.py: -------------------------------------------------------------------------------- 1 | # Remove the duplicate values from a linked list. 2 | 3 | import unittest 4 | 5 | def remove_duplicates(head): 6 | node = head 7 | if node: 8 | values = {node.data: True} 9 | while node.next: 10 | if node.next.data in values: 11 | node.next = node.next.next 12 | else: 13 | values[node.next.data] = True 14 | node = node.next 15 | return head 16 | 17 | class Node(): 18 | def __init__(self, data, next): 19 | self.data = data 20 | self.next = next 21 | 22 | class Test(unittest.TestCase): 23 | def test_remove_duplicates(self): 24 | head = Node(1,Node(3,Node(3,Node(1,Node(5,None))))) 25 | remove_duplicates(head) 26 | self.assertEqual(head.data, 1) 27 | self.assertEqual(head.next.data, 3) 28 | self.assertEqual(head.next.next.data, 5) 29 | self.assertEqual(head.next.next.next, None) 30 | 31 | if __name__ == "__main__": 32 | unittest.main() 33 | 34 | -------------------------------------------------------------------------------- /ch-02-linked-lists/02-kth-to-last.py: -------------------------------------------------------------------------------- 1 | # Return the k^{th} to last node in a linked list. 2 | 3 | import unittest 4 | 5 | def kth_to_last(head, k): 6 | lead, follow = head, head 7 | for _ in xrange(k): 8 | if not lead: 9 | return None 10 | lead = lead.next 11 | while lead: 12 | lead, follow = lead.next, follow.next 13 | return follow 14 | 15 | class Node(): 16 | def __init__(self, data, next=None): 17 | self.data, self.next = data, next 18 | 19 | class Test(unittest.TestCase): 20 | def test_kth_to_last(self): 21 | head = Node(1,Node(2,Node(3,Node(4,Node(5,Node(6,Node(7))))))) 22 | self.assertEqual(None, kth_to_last(head, 0)); 23 | self.assertEqual(7, kth_to_last(head, 1).data); 24 | self.assertEqual(4, kth_to_last(head, 4).data); 25 | self.assertEqual(2, kth_to_last(head, 6).data); 26 | self.assertEqual(1, kth_to_last(head, 7).data); 27 | self.assertEqual(None, kth_to_last(head, 8)); 28 | 29 | if __name__ == "__main__": 30 | unittest.main() 31 | -------------------------------------------------------------------------------- /ch-02-linked-lists/03-delete-middle.py: -------------------------------------------------------------------------------- 1 | # Delete the given nonterminal node from the containing linked list. 2 | 3 | import unittest 4 | 5 | def delete_middle(node): 6 | next = node.next 7 | node.data = next.data 8 | node.next = next.next 9 | 10 | class Node(): 11 | def __init__(self, data, next=None): 12 | self.data, self.next = data, next 13 | 14 | class Test(unittest.TestCase): 15 | def test_delete_middle(self): 16 | head = Node(1,Node(2,Node(3,Node(4)))) 17 | delete_middle(head.next.next) 18 | self.assertEqual(head.data, 1) 19 | self.assertEqual(head.next.data, 2) 20 | self.assertEqual(head.next.next.data, 4) 21 | 22 | if __name__ == "__main__": 23 | unittest.main() 24 | -------------------------------------------------------------------------------- /ch-02-linked-lists/04-partition.py: -------------------------------------------------------------------------------- 1 | # Partition a linked list so that all of the nodes containing values less than 2 | # a pivot value occur before all of the nodes containing values greater than 3 | # or equal to the pivot value. 4 | 5 | import unittest 6 | 7 | def partition(head, pivot): 8 | a_head, a_tail = None, None 9 | b_head, b_tail = None, None 10 | node = head 11 | while node: 12 | if node.data < pivot: 13 | if a_head: 14 | a_tail.next, a_tail = node, node 15 | else: 16 | a_head, a_tail = node, node 17 | else: 18 | if b_head: 19 | b_tail.next, b_tail = node, node 20 | else: 21 | b_head, b_tail = node, node 22 | node = node.next 23 | a_tail.next = b_head 24 | return a_head 25 | 26 | class Node(): 27 | def __init__(self, data, next=None): 28 | self.data, self.next = data, next 29 | 30 | def __str__(self): 31 | string = str(self.data) 32 | if self.next: 33 | string += ',' + str(self.next) 34 | return string 35 | 36 | class Test(unittest.TestCase): 37 | def test_partition(self): 38 | head1 = Node(7,Node(2,Node(9,Node(1,Node(6,Node(3,Node(8))))))) 39 | head2 = partition(head1, 6) 40 | self.assertEqual(str(head2), "2,1,3,7,9,6,8") 41 | head3 = partition(head2, 7) 42 | self.assertEqual(str(head3), "2,1,3,6,7,9,8") 43 | 44 | if __name__ == "__main__": 45 | unittest.main() 46 | 47 | -------------------------------------------------------------------------------- /ch-02-linked-lists/05-sum-lists.py: -------------------------------------------------------------------------------- 1 | # Sum two numbers that are represented with linked lists with decimal digits 2 | # in reverse order of magnitude. 3 | 4 | import unittest 5 | 6 | def sum_lists(num1, num2): 7 | node1, node2 = num1, num2 8 | carry = 0 9 | result_head, result_node = None, None 10 | while node1 or node2 or carry: 11 | value = carry 12 | if node1: 13 | value += node1.data 14 | node1 = node1.next 15 | if node2: 16 | value += node2.data 17 | node2 = node2.next 18 | if result_node: 19 | result_node.next = Node(value % 10) 20 | result_node = result_node.next 21 | else: 22 | result_node = Node(value % 10) 23 | result_head = result_node 24 | carry = value / 10 25 | return result_head 26 | 27 | class Node(): 28 | def __init__(self, data, next=None): 29 | self.data, self.next = data, next 30 | 31 | def __str__(self): 32 | string = str(self.data) 33 | if self.next: 34 | string += ',' + str(self.next) 35 | return string 36 | 37 | class Test(unittest.TestCase): 38 | def test_sum_lists(self): 39 | num1 = Node(1,Node(2,Node(3))) 40 | num2 = Node(4,Node(9,Node(5))) 41 | self.assertEqual(str(sum_lists(num1, num2)), "5,1,9") 42 | num1 = Node(9,Node(2,Node(3,Node(4,Node(1))))) 43 | num2 = Node(4,Node(9,Node(8))) 44 | self.assertEqual(str(sum_lists(num1, num2)), "3,2,2,5,1") 45 | 46 | if __name__ == "__main__": 47 | unittest.main() 48 | 49 | -------------------------------------------------------------------------------- /ch-02-linked-lists/06-palindrome.py: -------------------------------------------------------------------------------- 1 | # Determine whether or not a linked list is a palindrome. 2 | 3 | import unittest 4 | 5 | def is_palindrome(head): 6 | forward, backward = head, copy_reverse(head) 7 | while forward: 8 | if forward.data != backward.data: 9 | return False 10 | forward, backward = forward.next, backward.next 11 | return True 12 | 13 | def copy_reverse(head): 14 | prev = None 15 | node = copy(head) 16 | while node: 17 | next = node.next 18 | node.next = prev 19 | prev = node 20 | node = copy(next) 21 | return prev 22 | 23 | def copy(node): 24 | if node: 25 | return Node(node.data, node.next) 26 | else: 27 | return None 28 | 29 | class Node(): 30 | def __init__(self, data, next=None): 31 | self.data, self.next = data, next 32 | 33 | def __str__(self): 34 | string = str(self.data) 35 | if self.next: 36 | string += ',' + str(self.next) 37 | return string 38 | 39 | class Test(unittest.TestCase): 40 | def test_palindrome(self): 41 | list1 = Node(10) 42 | self.assertTrue(is_palindrome(list1)) 43 | list2 = Node(10,Node(10)) 44 | self.assertTrue(is_palindrome(list2)) 45 | list3 = Node(10,Node(20)) 46 | self.assertFalse(is_palindrome(list3)) 47 | list4 = Node(10,Node(70,Node(30,Node(70,Node(10))))) 48 | self.assertTrue(is_palindrome(list4)) 49 | 50 | def test_copy_reverse(self): 51 | head = Node(10,Node(20,Node(30,Node(40)))) 52 | self.assertEqual(str(copy_reverse(head)), "40,30,20,10") 53 | 54 | if __name__ == "__main__": 55 | unittest.main() 56 | 57 | -------------------------------------------------------------------------------- /ch-02-linked-lists/07-intersection.py: -------------------------------------------------------------------------------- 1 | # Return an intersecting node if two linked lists intersect. 2 | 3 | import unittest 4 | 5 | def intersection(head1, head2): 6 | nodes = {} 7 | node = head1 8 | while node: 9 | nodes[node] = True 10 | node = node.next 11 | node = head2 12 | while node: 13 | if node in nodes: 14 | return node 15 | node = node.next 16 | return None 17 | 18 | class Node(): 19 | def __init__(self, data, next=None): 20 | self.data, self.next = data, next 21 | 22 | def __str__(self): 23 | string = str(self.data) 24 | if self.next: 25 | string += ',' + str(self.next) 26 | return string 27 | 28 | class Test(unittest.TestCase): 29 | def test_intersection(self): 30 | head1 = Node(10,Node(20,Node(30))) 31 | head2 = Node(20,Node(30,Node(40))) 32 | self.assertEqual(intersection(head1, head2), None) 33 | node = Node(70,Node(80)) 34 | head3 = Node(50,Node(20,node)) 35 | head4 = Node(60,Node(90,Node(10,node))) 36 | self.assertEqual(intersection(head3, head4), node) 37 | 38 | if __name__ == "__main__": 39 | unittest.main() 40 | 41 | -------------------------------------------------------------------------------- /ch-02-linked-lists/08-loop-detection.py: -------------------------------------------------------------------------------- 1 | # Detect whether or not a linked list contains a cycle. 2 | 3 | import unittest 4 | 5 | def detect_cycle(head): 6 | nodes = {} 7 | node = head 8 | while node: 9 | if node in nodes: 10 | return node 11 | nodes[node] = True 12 | node = node.next 13 | return None 14 | 15 | class Node(): 16 | def __init__(self, data, next=None): 17 | self.data, self.next = data, next 18 | 19 | class Test(unittest.TestCase): 20 | def test_detect_cycle(self): 21 | head1 = Node(100,Node(200,Node(300))) 22 | self.assertEqual(detect_cycle(head1), None) 23 | node1 = Node(600) 24 | node2 = Node(700,Node(800,Node(900,node1))) 25 | node1.next = node2 26 | head2 = Node(500,node1) 27 | self.assertEqual(detect_cycle(head2), node1) 28 | 29 | if __name__ == "__main__": 30 | unittest.main() 31 | -------------------------------------------------------------------------------- /ch-03-stacks-and-queues/01-three-in-one.py: -------------------------------------------------------------------------------- 1 | # Use a single array to implement three stacks. 2 | 3 | class ThreeStacks(): 4 | def __init__(self): 5 | self.array = [None, None, None] 6 | self.current = [0, 1, 2] 7 | 8 | def push(self, item, stack_number): 9 | if not stack_number in [0, 1, 2]: 10 | raise Exception("Bad stack number") 11 | while len(self.array) <= self.current[stack_number]: 12 | self.array += [None] * len(self.array) 13 | self.array[self.current[stack_number]] = item 14 | self.current[stack_number] += 3 15 | 16 | def pop(self, stack_number): 17 | if not stack_number in [0, 1, 2]: 18 | raise Exception("Bad stack number") 19 | if self.current[stack_number] > 3: 20 | self.current[stack_number] -= 3 21 | item = self.array[self.current[stack_number]] 22 | self.array[self.current[stack_number]] = None 23 | return item 24 | 25 | import unittest 26 | 27 | class Test(unittest.TestCase): 28 | def test_three_stacks(self): 29 | three_stacks = ThreeStacks() 30 | three_stacks.push(101, 0) 31 | three_stacks.push(102, 0) 32 | three_stacks.push(103, 0) 33 | three_stacks.push(201, 1) 34 | self.assertEqual(three_stacks.pop(0), 103) 35 | self.assertEqual(three_stacks.pop(1), 201) 36 | self.assertEqual(three_stacks.pop(1), None) 37 | self.assertEqual(three_stacks.pop(2), None) 38 | three_stacks.push(301, 2) 39 | three_stacks.push(302, 2) 40 | self.assertEqual(three_stacks.pop(2), 302) 41 | self.assertEqual(three_stacks.pop(2), 301) 42 | self.assertEqual(three_stacks.pop(2), None) 43 | 44 | if __name__ == "__main__": 45 | unittest.main() 46 | -------------------------------------------------------------------------------- /ch-03-stacks-and-queues/02-stack-min.py: -------------------------------------------------------------------------------- 1 | # Implement a stack with a function that returns the current minimum item. 2 | 3 | class MinStack(): 4 | def __init__(self): 5 | self.top, self._min = None, None 6 | 7 | def min(self): 8 | if not self._min: 9 | return None 10 | return self._min.data 11 | 12 | def push(self, item): 13 | if self._min and (self._min.data < item): 14 | self._min = Node(data=self._min.data, next=self._min) 15 | else: 16 | self._min = Node(data=item, next=self._min) 17 | self.top = Node(data=item, next=self.top) 18 | 19 | def pop(self): 20 | if not self.top: 21 | return None 22 | self._min = self._min.next 23 | item = self.top.data 24 | self.top = self.top.next 25 | return item 26 | 27 | class Node(): 28 | def __init__(self, data=None, next=None): 29 | self.data, self.next = data, next 30 | 31 | def __str__(self): 32 | string = str(self.data) 33 | if self.next: 34 | string += ',' + str(self.next) 35 | return string 36 | 37 | import unittest 38 | 39 | class Test(unittest.TestCase): 40 | def test_min_stack(self): 41 | min_stack = MinStack() 42 | self.assertEqual(min_stack.min(), None) 43 | min_stack.push(7) 44 | self.assertEqual(min_stack.min(), 7) 45 | min_stack.push(6) 46 | min_stack.push(5) 47 | self.assertEqual(min_stack.min(), 5) 48 | min_stack.push(10) 49 | self.assertEqual(min_stack.min(), 5) 50 | self.assertEqual(min_stack.pop(), 10) 51 | self.assertEqual(min_stack.pop(), 5) 52 | self.assertEqual(min_stack.min(), 6) 53 | self.assertEqual(min_stack.pop(), 6) 54 | self.assertEqual(min_stack.pop(), 7) 55 | self.assertEqual(min_stack.min(), None) 56 | 57 | if __name__ == "__main__": 58 | unittest.main() 59 | 60 | -------------------------------------------------------------------------------- /ch-03-stacks-and-queues/03-stack-of-plates.py: -------------------------------------------------------------------------------- 1 | # Implement a class that acts as a single stack made out of multiple stacks 2 | # which each have a set capacity. 3 | 4 | class MultiStack(): 5 | def __init__(self, capacity): 6 | self.capacity = capacity 7 | self.stacks = [] 8 | 9 | def push(self, item): 10 | if len(self.stacks) and (len(self.stacks[-1]) < self.capacity): 11 | self.stacks[-1].append(item) 12 | else: 13 | self.stacks.append([item]) 14 | 15 | def pop(self): 16 | while len(self.stacks) and (len(self.stacks[-1]) == 0): 17 | self.stacks.pop() 18 | if len(self.stacks) == 0: 19 | return None 20 | item = self.stacks[-1].pop() 21 | if len(self.stacks[-1]) == 0: 22 | self.stacks.pop() 23 | return item 24 | 25 | def pop_at(self, stack_number): 26 | if (stack_number < 0) or (len(self.stacks) <= stack_number): 27 | return None 28 | if len(self.stacks[stack_number]) == 0: 29 | return None 30 | return self.stacks[stack_number].pop() 31 | 32 | import unittest 33 | 34 | class Test(unittest.TestCase): 35 | def test_multi_stack(self): 36 | stack = MultiStack(3) 37 | stack.push(11) 38 | stack.push(22) 39 | stack.push(33) 40 | stack.push(44) 41 | stack.push(55) 42 | stack.push(66) 43 | stack.push(77) 44 | stack.push(88) 45 | self.assertEqual(stack.pop(), 88) 46 | self.assertEqual(stack.pop_at(1), 66) 47 | self.assertEqual(stack.pop_at(0), 33) 48 | self.assertEqual(stack.pop_at(1), 55) 49 | self.assertEqual(stack.pop_at(1), 44) 50 | self.assertEqual(stack.pop_at(1), None) 51 | stack.push(99) 52 | self.assertEqual(stack.pop(), 99) 53 | self.assertEqual(stack.pop(), 77) 54 | self.assertEqual(stack.pop(), 22) 55 | self.assertEqual(stack.pop(), 11) 56 | self.assertEqual(stack.pop(), None) 57 | 58 | if __name__ == "__main__": 59 | unittest.main() 60 | 61 | -------------------------------------------------------------------------------- /ch-03-stacks-and-queues/04-queue-via-stacks.py: -------------------------------------------------------------------------------- 1 | # Implement a queue using two stacks. 2 | 3 | class QueueViaStacks(): 4 | def __init__(self): 5 | self.in_stack = Stack() 6 | self.out_stack = Stack() 7 | 8 | def add(self, item): 9 | self.in_stack.push(item) 10 | 11 | def remove(self): 12 | if len(self.out_stack) == 0: 13 | while len(self.in_stack): 14 | self.out_stack.push(self.in_stack.pop()) 15 | return self.out_stack.pop() 16 | 17 | class Stack(): 18 | def __init__(self): 19 | self.array = [] 20 | 21 | def __len__(self): 22 | return len(self.array) 23 | 24 | def push(self, item): 25 | self.array.append(item) 26 | 27 | def pop(self): 28 | if not len(self.array): 29 | return None 30 | return self.array.pop() 31 | 32 | import unittest 33 | 34 | class Test(unittest.TestCase): 35 | def test_queue_via_stacks(self): 36 | queue = QueueViaStacks() 37 | queue.add(11) 38 | queue.add(22) 39 | queue.add(33) 40 | self.assertEqual(queue.remove(), 11) 41 | queue.add(44) 42 | queue.add(55) 43 | queue.add(66) 44 | self.assertEqual(queue.remove(), 22) 45 | self.assertEqual(queue.remove(), 33) 46 | self.assertEqual(queue.remove(), 44) 47 | self.assertEqual(queue.remove(), 55) 48 | queue.add(77) 49 | self.assertEqual(queue.remove(), 66) 50 | self.assertEqual(queue.remove(), 77) 51 | self.assertEqual(queue.remove(), None) 52 | 53 | if __name__ == "__main__": 54 | unittest.main() 55 | 56 | -------------------------------------------------------------------------------- /ch-03-stacks-and-queues/05-sort-stack.py: -------------------------------------------------------------------------------- 1 | # Sort a stack with the smallest on top using only a single temporary stack. 2 | 3 | def sort_stack(stack): 4 | temp = Stack() 5 | previous = stack.pop() 6 | current = stack.pop() 7 | while current: 8 | if current < previous: 9 | temp.push(current) 10 | else: 11 | temp.push(previous) 12 | previous = current 13 | current = stack.pop() 14 | is_sorted = True 15 | current = temp.pop() 16 | while current: 17 | if current > previous: 18 | is_sorted = False 19 | stack.push(current) 20 | else: 21 | stack.push(previous) 22 | previous = current 23 | current = temp.pop() 24 | stack.push(previous) 25 | if is_sorted: 26 | return stack 27 | return sort_stack(stack) 28 | 29 | class Stack(): 30 | def __init__(self): 31 | self.top = None 32 | 33 | def __str__(self): 34 | return str(self.top) 35 | 36 | def push(self, item): 37 | self.top = current(item, self.top) 38 | 39 | def pop(self): 40 | if not self.top: 41 | return None 42 | item = self.top 43 | self.top = self.top.next 44 | return item.data 45 | 46 | class current(): 47 | def __init__(self, data=None, next=None): 48 | self.data, self.next = data, next 49 | 50 | def __str__(self): 51 | return str(self and self.data) + ',' + str(self and self.next) 52 | 53 | import unittest 54 | 55 | class Test(unittest.TestCase): 56 | def test_sort_stack(self): 57 | self.assertEqual(str(sort_stack(Stack())), "None,None") 58 | stack = Stack() 59 | stack.push(10) 60 | stack.push(30) 61 | stack.push(70) 62 | stack.push(40) 63 | stack.push(80) 64 | stack.push(20) 65 | stack.push(90) 66 | stack.push(50) 67 | stack.push(60) 68 | self.assertEqual(str(stack), "60,50,90,20,80,40,70,30,10,None") 69 | self.assertEqual(str(sort_stack(stack)), "10,20,30,40,50,60,70,80,90,None") 70 | 71 | if __name__ == "__main__": 72 | unittest.main() 73 | 74 | -------------------------------------------------------------------------------- /ch-03-stacks-and-queues/06-animal-shelter.py: -------------------------------------------------------------------------------- 1 | # Implement a cat and dog queue for an animal shelter. 2 | 3 | class AnimalShelter(): 4 | def __init__(self): 5 | self.cats, self.dogs = [], [] 6 | 7 | def enqueue(self, animal): 8 | if animal.__class__ == Cat: self.cats.append(animal) 9 | else: self.dogs.append(animal) 10 | 11 | def dequeueAny(self): 12 | if len(self.cats): return self.dequeueCat() 13 | return self.dequeueDog() 14 | 15 | def dequeueCat(self): 16 | if len(self.cats) == 0: return None 17 | cat = self.cats[0] 18 | self.cats = self.cats[1:] 19 | return cat 20 | 21 | def dequeueDog(self): 22 | if len(self.dogs) == 0: return None 23 | dog = self.dogs[0] 24 | self.dogs = self.dogs[1:] 25 | return dog 26 | 27 | class Animal(): 28 | def __init__(self, name): 29 | self.name = name 30 | 31 | def __str__(self): 32 | return self.name 33 | 34 | class Cat(Animal): pass 35 | class Dog(Animal): pass 36 | 37 | import unittest 38 | 39 | class Test(unittest.TestCase): 40 | def test_animal_shelter(self): 41 | shelter = AnimalShelter() 42 | shelter.enqueue(Cat("Hanzack")) 43 | shelter.enqueue(Dog("Pluto")) 44 | shelter.enqueue(Cat("Garfield")) 45 | shelter.enqueue(Cat("Tony")) 46 | shelter.enqueue(Dog("Clifford")) 47 | shelter.enqueue(Dog("Blue")) 48 | self.assertEqual(str(shelter.dequeueAny()), "Hanzack") 49 | self.assertEqual(str(shelter.dequeueAny()), "Garfield") 50 | self.assertEqual(str(shelter.dequeueDog()), "Pluto") 51 | self.assertEqual(str(shelter.dequeueDog()), "Clifford") 52 | self.assertEqual(str(shelter.dequeueCat()), "Tony") 53 | self.assertEqual(str(shelter.dequeueCat()), "None") 54 | self.assertEqual(str(shelter.dequeueAny()), "Blue") 55 | self.assertEqual(str(shelter.dequeueAny()), "None") 56 | 57 | if __name__ == "__main__": 58 | unittest.main() 59 | 60 | -------------------------------------------------------------------------------- /ch-04-trees-and-graphs/01-route-between-nodes.py: -------------------------------------------------------------------------------- 1 | # Find a route from the first node to the second node in a directed graph. 2 | 3 | def find_route(node1, node2): 4 | found_path = None 5 | queue = Queue() 6 | node = node1 7 | node.shortest_path = [node] 8 | all_visited_nodes = [node] 9 | while node: 10 | for adjacent in node.adjacency_list: 11 | if not adjacent.shortest_path: 12 | adjacent.shortest_path = node.shortest_path + [adjacent] 13 | if adjacent == node2: 14 | found_path = node.shortest_path + [adjacent] 15 | break 16 | queue.add(adjacent) 17 | all_visited_nodes.append(adjacent) 18 | node = queue.remove() 19 | for visited in all_visited_nodes: 20 | visited.shortest_path = None 21 | return found_path 22 | 23 | class Node(): 24 | def __init__(self, data, adjacency_list=None): 25 | self.data = data 26 | self.adjacency_list = adjacency_list or [] 27 | self.shortest_path = None 28 | 29 | def add_edge_to(self, node): 30 | self.adjacency_list += [node] 31 | 32 | def __str__(self): 33 | return self.data 34 | 35 | class Queue(): 36 | def __init__(self): 37 | self.array = [] 38 | 39 | def add(self, item): 40 | self.array.append(item) 41 | 42 | def remove(self): 43 | if not len(self.array): 44 | return None 45 | item = self.array[0] 46 | del self.array[0] 47 | return item 48 | 49 | import unittest 50 | 51 | def str_for(path): 52 | if not path: return str(path) 53 | return ''.join([str(n) for n in path]) 54 | 55 | class Test(unittest.TestCase): 56 | def test_find_route(self): 57 | node_j = Node('J') 58 | node_i = Node('I') 59 | node_h = Node('H') 60 | node_d = Node('D') 61 | node_f = Node('F', [node_i]) 62 | node_b = Node('B', [node_j]) 63 | node_g = Node('G', [node_d, node_h]) 64 | node_c = Node('C', [node_g]) 65 | node_a = Node('A', [node_b, node_c, node_d]) 66 | node_e = Node('E', [node_f, node_a]) 67 | node_d.add_edge_to(node_a) 68 | self.assertEqual(str_for(find_route(node_a, node_i)), 'None') 69 | self.assertEqual(str_for(find_route(node_a, node_j)), 'ABJ') 70 | node_h.add_edge_to(node_i) 71 | self.assertEqual(str_for(find_route(node_a, node_i)), 'ACGHI') 72 | 73 | if __name__ == "__main__": 74 | unittest.main() 75 | 76 | -------------------------------------------------------------------------------- /ch-04-trees-and-graphs/02-minimal-tree.py: -------------------------------------------------------------------------------- 1 | # Create a minimal height binary search tree with the elements of a given 2 | # sorted array. 3 | 4 | def minimal_height_bst(sorted_array): 5 | if len(sorted_array) == 0: 6 | return None 7 | middle = len(sorted_array) / 2 8 | left = minimal_height_bst(sorted_array[:middle]) 9 | right = minimal_height_bst(sorted_array[(middle+1):]) 10 | return BSTNode(sorted_array[middle], left, right) 11 | 12 | class BSTNode(): 13 | def __init__(self, data=None, left=None, right=None): 14 | self.data, self.left, self.right = data, left, right 15 | 16 | def __str__(self): 17 | string = "(" + str(self.data) 18 | if self.left: string += str(self.left) 19 | else: string += "." 20 | if self.right: string += str(self.right) 21 | else: string += "." 22 | return string + ")" 23 | 24 | import unittest 25 | 26 | class Test(unittest.TestCase): 27 | def test_minimal_height_bst(self): 28 | sorted_array = [1, 2, 3, 4, 5, 6, 7, 8, 9] 29 | bst = minimal_height_bst(sorted_array) 30 | self.assertEqual(str(bst), "(5(3(2(1..).)(4..))(8(7(6..).)(9..)))") 31 | 32 | if __name__ == "__main__": 33 | unittest.main() 34 | 35 | -------------------------------------------------------------------------------- /ch-04-trees-and-graphs/03-list-of-depths.py: -------------------------------------------------------------------------------- 1 | # Return an array of linked lists containing all elements on each depth 2 | # of a binary tree. 3 | 4 | def list_of_depths(binary_tree): 5 | if not binary_tree: 6 | return [] 7 | lists = [] 8 | queue = Queue() 9 | current_depth = -1 10 | current_tail = None 11 | node = binary_tree 12 | node.depth = 0 13 | while node: 14 | if node.depth == current_depth: 15 | current_tail.next = ListNode(node.data) 16 | current_tail = current_tail.next 17 | else: 18 | current_depth = node.depth 19 | current_tail = ListNode(node.data) 20 | lists.append(current_tail) 21 | for child in [node.left, node.right]: 22 | if child: 23 | child.depth = node.depth + 1 24 | queue.add(child) 25 | node = queue.remove() 26 | return lists 27 | 28 | class TreeNode(): 29 | def __init__(self, data=None, left=None, right=None): 30 | self.data, self.left, self.right = data, left, right 31 | self.depth = None 32 | 33 | class ListNode(): 34 | def __init__(self, data=None, next=None): 35 | self.data, self.next = data, next 36 | 37 | def __str__(self): 38 | return str(self.data) + ',' + str(self.next) 39 | 40 | class Queue(): 41 | def __init__(self): 42 | self.head, self.tail = None, None 43 | 44 | def add(self, item): 45 | if self.head: 46 | self.tail.next = ListNode(item) 47 | self.tail = self.tail.next 48 | else: 49 | self.head = self.tail = ListNode(item) 50 | 51 | def remove(self): 52 | if not self.head: 53 | return None 54 | item = self.head.data 55 | self.head = self.head.next 56 | return item 57 | 58 | import unittest 59 | 60 | class Test(unittest.TestCase): 61 | def test_list_of_depths(self): 62 | node_h = TreeNode('H') 63 | node_g = TreeNode('G') 64 | node_f = TreeNode('F') 65 | node_e = TreeNode('E', node_g) 66 | node_d = TreeNode('D', node_h) 67 | node_c = TreeNode('C', None, node_f) 68 | node_b = TreeNode('B', node_d, node_e) 69 | node_a = TreeNode('A', node_b, node_c) 70 | lists = list_of_depths(node_a) 71 | self.assertEqual(str(lists[0]), "A,None") 72 | self.assertEqual(str(lists[1]), "B,C,None") 73 | self.assertEqual(str(lists[2]), "D,E,F,None") 74 | self.assertEqual(str(lists[3]), "H,G,None") 75 | self.assertEqual(len(lists), 4) 76 | 77 | if __name__ == "__main__": 78 | unittest.main() 79 | 80 | -------------------------------------------------------------------------------- /ch-04-trees-and-graphs/04-check-balanced.py: -------------------------------------------------------------------------------- 1 | # Tell whether or not a binary tree is balanced. 2 | 3 | def is_balanced(binary_tree): 4 | if not binary_tree: 5 | return (True, 0) 6 | (left_balanced, left_depth) = is_balanced(binary_tree.left) 7 | if not left_balanced: 8 | return (False, None) 9 | (right_balanced, right_depth) = is_balanced(binary_tree.right) 10 | if (not right_balanced) or (abs(left_depth - right_depth) > 1): 11 | return (False, None) 12 | depth = max(left_depth, right_depth) + 1 13 | return (True, depth) 14 | 15 | class Node(): 16 | def __init__(self, left=None, right=None): 17 | self.left, self.right = left, right 18 | 19 | import unittest 20 | 21 | class Test(unittest.TestCase): 22 | def test_is_balanced(self): 23 | self.assertEqual(is_balanced(Node(Node(),Node())), (True, 2)) 24 | self.assertEqual(is_balanced(Node(Node(),Node(Node()))), (True, 3)) 25 | self.assertEqual(is_balanced(Node(Node(),Node(Node(Node())))), 26 | (False, None)) 27 | self.assertEqual(is_balanced(Node(Node(Node()),Node(Node(Node())))), 28 | (False,None)) 29 | self.assertEqual(is_balanced(Node(Node(Node()), 30 | Node(Node(Node()),Node()))), (True, 4)) 31 | 32 | if __name__ == "__main__": 33 | unittest.main() 34 | -------------------------------------------------------------------------------- /ch-04-trees-and-graphs/05-validate-bst.py: -------------------------------------------------------------------------------- 1 | # Validate that a binary tree is a binary search tree. 2 | 3 | def validate_tree(binary_tree): 4 | return validate_tree_node(binary_tree, -float('inf'), float('inf')) 5 | 6 | def validate_tree_node(node, left_bound, right_bound): 7 | if not node: 8 | return True 9 | return node.data >= left_bound and node.data <= right_bound and \ 10 | validate_tree_node(node.left, left_bound, node.data) and \ 11 | validate_tree_node(node.right, node.data, right_bound) 12 | 13 | class Node(): 14 | def __init__(self, data, left=None, right=None): 15 | self.data, self.left, self.right = data, left, right 16 | 17 | import unittest 18 | 19 | class Test(unittest.TestCase): 20 | def test_validate_tree(self): 21 | self.assertEqual(validate_tree(Node(3,Node(1),Node(8))), True) 22 | tree1 = Node(5,Node(3,Node(1),Node(4)),Node(7,Node(6),Node(8,None,Node(9)))) 23 | self.assertEqual(validate_tree(tree1), True) 24 | tree2 = Node(7,Node(3,Node(1),Node(8)),Node(9,Node(8),Node(11))) 25 | self.assertEqual(validate_tree(tree2), False) 26 | 27 | if __name__ == "__main__": 28 | unittest.main() 29 | 30 | -------------------------------------------------------------------------------- /ch-04-trees-and-graphs/06-successor.py: -------------------------------------------------------------------------------- 1 | # Return the successor of a node in a binary search tree. 2 | 3 | def successor(node): 4 | if not node: 5 | return None 6 | child = node.right 7 | if child: 8 | while child.left: 9 | child = child.left 10 | if child: 11 | return child 12 | if node.parent and node.parent.data > node.data: 13 | return node.parent 14 | return None 15 | 16 | class Node(): 17 | def __init__(self, data, left=None, right=None): 18 | self.data, self.left, self.right = data, left, right 19 | self.parent = None 20 | if self.left: self.left.parent = self 21 | if self.right: self.right.parent = self 22 | 23 | import unittest 24 | 25 | class Test(unittest.TestCase): 26 | def test_successor(self): 27 | self.assertEqual(successor(Node(22, Node(11))), None) 28 | self.assertEqual(successor(Node(22, Node(11), Node(33))).data, 33) 29 | self.assertEqual(successor(Node(22, Node(11), Node(33, Node(28)))).data, 28) 30 | self.assertEqual(successor(Node(22, Node(11), Node(33)).left).data, 22) 31 | self.assertEqual(successor(Node(22, Node(11), Node(33)).right), None) 32 | 33 | if __name__ == "__main__": 34 | unittest.main() 35 | 36 | -------------------------------------------------------------------------------- /ch-04-trees-and-graphs/07-build-order.py: -------------------------------------------------------------------------------- 1 | # Determine a build order given a list of projects and their dependencies. 2 | 3 | def build_order(projects, dependencies): 4 | nodes = {} 5 | for project in projects: 6 | nodes[project] = GraphNode(project) 7 | for dependency in dependencies: 8 | nodes[dependency[0]].add_edge(nodes[dependency[1]]) 9 | queue = Queue() 10 | for project in projects: 11 | node = nodes[project] 12 | if not node.dependencies_left: 13 | queue.add(node) 14 | build_order = [] 15 | while queue.is_not_empty(): 16 | node = queue.remove() 17 | build_order.append(node.data) 18 | for dependent in node.edges: 19 | dependent.dependencies_left -= 1 20 | if not dependent.dependencies_left: 21 | queue.add(dependent) 22 | if len(build_order) < len(projects): 23 | return Exception("Cycle detected") 24 | return build_order 25 | 26 | class GraphNode(): 27 | def __init__(self, data): 28 | self.data = data 29 | self.edges = [] 30 | self.dependencies_left = 0 31 | 32 | def add_edge(self, node): 33 | self.edges.append(node) 34 | node.dependencies_left += 1 35 | 36 | class Queue(): 37 | def __init__(self): self.array = [] 38 | def add(self, item): self.array.append(item) 39 | def remove(self): return self.array.pop(0) 40 | def is_not_empty(self): return len(self.array) > 0 41 | 42 | import unittest 43 | 44 | class Test(unittest.TestCase): 45 | def test_build_order(self): 46 | projects = ["A", "B", "C", "D", "E", "F", "G"] 47 | dependencies1 = [("C", "A"), ("B", "A"), ("F", "A"), ("F", "B"), ("F", "C"), 48 | ("A", "E"), ("B", "E"), ("D", "G")] 49 | self.assertEqual(build_order(projects, dependencies1), 50 | ["D", "F", "G", "B", "C", "A", "E"]) 51 | dependencies2 = [("A", "B"), ("B", "C"), ("C", "D"), ("D", "A")] 52 | self.assertEqual(build_order(projects, dependencies2).__class__, Exception) 53 | dependencies3 = [("A", "B"), ("A", "C"), ("E", "A"), ("E", "B"), ("A", "F"), 54 | ("B", "F"), ("C", "F"), ("G", "D")] 55 | self.assertEqual(build_order(projects, dependencies3), 56 | ["E", "G", "A", "D", "B", "C", "F"]) 57 | 58 | if __name__ == "__main__": 59 | unittest.main() 60 | 61 | -------------------------------------------------------------------------------- /ch-04-trees-and-graphs/08-first-ancestor.py: -------------------------------------------------------------------------------- 1 | # Find the first common ancestor of two nodes in a tree. 2 | 3 | def first_common_ancestor(node1, node2): 4 | search1, search2 = node1, node2 5 | ancestors1, ancestors2 = {}, {} 6 | while search1 or search2: 7 | if search1: 8 | if search1 in ancestors2: 9 | return search1 10 | ancestors1[search1] = True 11 | search1 = search1.parent 12 | if search2: 13 | if search2 in ancestors1: 14 | return search2 15 | ancestors2[search2] = True 16 | search2 = search2.parent 17 | return None 18 | 19 | class Node(): 20 | def __init__(self, data=None, left=None, right=None): 21 | self.data, self.left, self.right = data, left, right 22 | self.parent = None 23 | if self.left: 24 | self.left.parent = self 25 | if self.right: 26 | self.right.parent = self 27 | 28 | import unittest 29 | 30 | class Test(unittest.TestCase): 31 | def test_first_common_ancestor(self): 32 | node1 = Node(11, Node(55), Node(77, Node(44))) 33 | node2 = Node(22, Node(99)) 34 | self.assertEqual(first_common_ancestor(node1, node2), None) 35 | node3 = Node(33, node1, Node(88, Node(123, None, node2))) 36 | node4 = Node(44, node3, Node(66)) 37 | self.assertEqual(first_common_ancestor(node1, node2), node3) 38 | 39 | if __name__ == "__main__": 40 | unittest.main() 41 | -------------------------------------------------------------------------------- /ch-04-trees-and-graphs/09-bst-sequences.py: -------------------------------------------------------------------------------- 1 | # Enumerate all inserrtion sequences that could have led to the given BST. 2 | 3 | def bst_sequences(bst): 4 | return bst_sequences_partial([], [bst]) 5 | 6 | def bst_sequences_partial(partial, subtrees): 7 | if not len(subtrees): 8 | return [partial] 9 | sequences = [] 10 | for index, subtree in enumerate(subtrees): 11 | next_partial = partial + [subtree.data] 12 | next_subtrees = subtrees[:index] + subtrees[index+1:] 13 | if subtree.left: 14 | next_subtrees.append(subtree.left) 15 | if subtree.right: 16 | next_subtrees.append(subtree.right) 17 | sequences += bst_sequences_partial(next_partial, next_subtrees) 18 | return sequences 19 | 20 | class Node(): 21 | def __init__(self, data=None, left=None, right=None): 22 | self.data, self.left, self.right = data, left, right 23 | 24 | import unittest 25 | 26 | class Test(unittest.TestCase): 27 | def test_bst_sequences(self): 28 | self.assertEqual(bst_sequences(Node(7,Node(4,Node(5)),Node(9))), [ 29 | [7, 4, 9, 5], 30 | [7, 4, 5, 9], 31 | [7, 9, 4, 5]]) 32 | self.assertEqual(bst_sequences(Node(7,Node(4,Node(5),Node(6)),Node(9))), [ 33 | [7, 4, 9, 5, 6], 34 | [7, 4, 9, 6, 5], 35 | [7, 4, 5, 9, 6], 36 | [7, 4, 5, 6, 9], 37 | [7, 4, 6, 9, 5], 38 | [7, 4, 6, 5, 9], 39 | [7, 9, 4, 5, 6], 40 | [7, 9, 4, 6, 5]]) 41 | 42 | if __name__ == "__main__": 43 | unittest.main() 44 | 45 | -------------------------------------------------------------------------------- /ch-04-trees-and-graphs/10-is-subtree.py: -------------------------------------------------------------------------------- 1 | # Determine whether one binary tree is a subtree of another. 2 | 3 | def is_subtree(bt1, bt2): 4 | for node in tree_generator(bt1): 5 | if equivalent_trees(node, bt2): 6 | return True 7 | return False 8 | 9 | def equivalent_trees(bt1, bt2): 10 | if not bt1: 11 | return not bt2 12 | if not bt2: 13 | return False 14 | if bt1.data != bt2.data: 15 | return False 16 | return equivalent_trees(bt1.left, bt2.left) and \ 17 | equivalent_trees(bt1.right, bt2.right) 18 | 19 | class Node(): 20 | def __init__(self, data=None, left=None, right=None): 21 | self.data, self.left, self.right = data, left, right 22 | 23 | def tree_generator(node): 24 | if not node: return 25 | yield node 26 | for child in tree_generator(node.left): yield child 27 | for child in tree_generator(node.right): yield child 28 | 29 | import unittest 30 | 31 | class Test(unittest.TestCase): 32 | def test_is_subtree(self): 33 | tree1 = Node(5,Node(3,Node(2),Node(4)),Node(8,Node(7,Node(9)),Node(1))) 34 | tree2 = Node(8,Node(7),Node(1)) 35 | self.assertEqual(is_subtree(tree1, tree2), False) 36 | tree3 = Node(8,Node(7,Node(9)),Node(1)) 37 | self.assertEqual(is_subtree(tree1, tree3), True) 38 | 39 | if __name__ == "__main__": 40 | unittest.main() 41 | 42 | -------------------------------------------------------------------------------- /ch-04-trees-and-graphs/11-random-node.py: -------------------------------------------------------------------------------- 1 | # Design a binary tree class that can return a random node. 2 | 3 | class Node(): 4 | def __init__(self, data=None, left=None, right=None): 5 | self.data, self.left, self.right = data, left, right 6 | self.count = 1 7 | if self.left: 8 | self.count += self.left.count 9 | if self.right: 10 | self.count += self.right.count 11 | 12 | def get_random_node(self): 13 | return self.get_numbered_node(randint(0, self.count - 1)) 14 | 15 | def get_numbered_node(self, number): 16 | if number == 0: 17 | return self 18 | if self.left: 19 | if number - 1 < self.left.count: 20 | return self.left.get_numbered_node(number - 1) 21 | elif self.right: 22 | return self.right.get_numbered_node(number - 1 - self.left.count) 23 | if self.right: 24 | return self.right.get_numbered_node(number - 1) 25 | return None 26 | 27 | import random 28 | import unittest 29 | 30 | mock_random_value = False 31 | 32 | def randint(lower_bound, upper_bound): 33 | if not mock_random_value is False: 34 | return mock_random_value 35 | return random.randint(lower_bound, upper_bound) 36 | 37 | class Test(unittest.TestCase): 38 | def test_mock_randint(self): 39 | global mock_random_value 40 | mock_random_value = 12 41 | self.assertEqual(randint(0, 2000), 12) 42 | 43 | def test_get_random_value(self): 44 | global mock_random_value 45 | tree = Node(11,Node(21,Node(31),Node(32,Node(41),Node(42,None,Node(51)))), 46 | Node(22,Node(33),Node(34))) 47 | mock_random_value = 0 48 | self.assertEqual(tree.get_random_node().data, 11) 49 | mock_random_value = 4 50 | self.assertEqual(tree.get_random_node().data, 41) 51 | mock_random_value = 8 52 | self.assertEqual(tree.get_random_node().data, 33) 53 | 54 | if __name__ == "__main__": 55 | unittest.main() 56 | 57 | -------------------------------------------------------------------------------- /ch-04-trees-and-graphs/12-paths-with-sum.py: -------------------------------------------------------------------------------- 1 | # Return all downward paths through a tree whose nodes sum to a target value. 2 | 3 | def paths_with_sum(binary_tree, target_sum): 4 | partial_paths = ListDict({target_sum: [[]]}) 5 | return paths_with_partial_sum(binary_tree, target_sum, partial_paths) 6 | 7 | def paths_with_partial_sum(node, target_sum, partial_paths): 8 | if not node: 9 | return [] 10 | next_partial_paths = ListDict({target_sum: [[]]}) 11 | for path_sum, paths in partial_paths.items(): 12 | for path in paths: 13 | next_partial_paths[path_sum - node.value] += [path + [node.name]] 14 | paths = next_partial_paths[0] 15 | for child in [node.left, node.right]: 16 | paths += paths_with_partial_sum(child, target_sum, next_partial_paths) 17 | return paths 18 | 19 | class Node(): 20 | def __init__(self, name, value, left=None, right=None): 21 | self.name, self.value, self.left, self.right = name, value, left, right 22 | 23 | class ListDict(dict): 24 | def __missing__(self, key): 25 | return [] 26 | 27 | import unittest 28 | 29 | class Test(unittest.TestCase): 30 | def test_paths_with_sum(self): 31 | bt=Node("A",4,Node("B",-2,Node("D",7),Node("E", 4)), 32 | Node("C", 7,Node("F",-1,Node("H",-1),Node("I",2,Node("K",1))), 33 | Node("G", 0,None, Node("J", -2)))) 34 | self.assertEqual(paths_with_sum(bt, 12), [["A", "C", "F", "I"]]) 35 | self.assertEqual(paths_with_sum(bt, 2), [["A", "B"], ["B", "E"], ["I"], 36 | ["F", "I", "K"]]) 37 | self.assertEqual(paths_with_sum(bt, 9), [["A","B","D"], ["A","C","F","H"], 38 | ["C","F","I","K"], ["A","C","G","J"]]) 39 | 40 | if __name__ == "__main__": 41 | unittest.main() 42 | 43 | -------------------------------------------------------------------------------- /ch-05-bit-manipulation/01-insertion.py: -------------------------------------------------------------------------------- 1 | # Insert `m` into `n` between `i` and `j`. 2 | 3 | def insertion(n, m, i, j): 4 | cleared_n = n & ~((1 << (j+1)) - (1 << i)) 5 | shifted_m = m << i 6 | return cleared_n | shifted_m 7 | 8 | import unittest 9 | 10 | class Test(unittest.TestCase): 11 | def test_insertion(self): 12 | self.assertEqual(insertion(0b11111111, 0b10, 2, 5), 0b11001011) 13 | self.assertEqual(insertion(0b00000000, 0b1010, 4, 7), 0b10100000) 14 | 15 | if __name__ == "__main__": 16 | unittest.main() 17 | 18 | -------------------------------------------------------------------------------- /ch-05-bit-manipulation/02-binary-to-string.py: -------------------------------------------------------------------------------- 1 | # Return a binary string representation of the given floating point number 2 | # between 0 and 1 as long as it accurately fits in 32 bits. 3 | 4 | import ctypes 5 | 6 | def binary_to_string(number): 7 | if number == 1: 8 | return "1.0" 9 | if number == 0: 10 | return "0.0" 11 | if number < 0 or number > 1: 12 | return Exception("Out of bounds") 13 | normalized = bits_for(number + 1.0) 14 | bits = normalized & ((1 << 52) - 1) 15 | if normalized & ((1 << 22) - 1): 16 | return Exception("Insufficient precision") 17 | digits = ["0."] 18 | bit = (1 << 51) 19 | while bits: 20 | if bits & bit: 21 | digits.append("1") 22 | else: 23 | digits.append("0") 24 | bits &= ~bit 25 | bit >>= 1 26 | return "".join(digits) 27 | 28 | def bits_for(number): 29 | return ctypes.c_longlong.from_buffer(ctypes.c_double(number)).value 30 | 31 | import unittest 32 | 33 | class Test(unittest.TestCase): 34 | def test_binary_to_string(self): 35 | self.assertEqual(binary_to_string(0.75), "0.11") 36 | self.assertEqual(binary_to_string(0.625), "0.101") 37 | self.assertEqual(binary_to_string(0.3).message, "Insufficient precision") 38 | 39 | if __name__ == "__main__": 40 | unittest.main() 41 | 42 | -------------------------------------------------------------------------------- /ch-05-bit-manipulation/03-flip-bit-to-win.py: -------------------------------------------------------------------------------- 1 | # Determine the longest sequence of 1s attainable by flipping up to one digit. 2 | 3 | def longest_sequence_after_flip(bits): 4 | bit = 1 << 63 5 | longest = 0 6 | current_without_flip = 0 7 | current_with_flip = 0 8 | while bit: 9 | if bits & bit: 10 | current_without_flip += 1 11 | current_with_flip += 1 12 | else: 13 | current_with_flip = current_without_flip + 1 14 | current_without_flip = 0 15 | if current_with_flip > longest: 16 | longest = current_with_flip 17 | bit >>= 1 18 | return longest 19 | 20 | import unittest 21 | 22 | class Test(unittest.TestCase): 23 | def test_longest_sequence_after_flip(self): 24 | self.assertEqual(longest_sequence_after_flip(0b1111100), 6) 25 | self.assertEqual(longest_sequence_after_flip(0b0111111), 7) 26 | self.assertEqual(longest_sequence_after_flip(-1), 64) 27 | self.assertEqual(longest_sequence_after_flip(0b1011110111001111110), 8) 28 | 29 | if __name__ == "__main__": 30 | unittest.main() 31 | 32 | -------------------------------------------------------------------------------- /ch-05-bit-manipulation/04-next-number.py: -------------------------------------------------------------------------------- 1 | # Return the next larger and next smaller numbers that use the same number of 2 | # 1's in their binary representation as the given positive integer. 3 | 4 | def next_numbers(number): 5 | if number % 2: 6 | # Larger X011...1 --> X101...1 7 | negated = ~number 8 | lsz = negated & -negated 9 | larger = number ^ (lsz | (lsz >> 1)) 10 | # Smaller X100...01...1 --> X011...10...0 11 | end_ones = lsz - 1 12 | cleared = number ^ end_ones 13 | if cleared: 14 | next_bit = cleared & -cleared 15 | flip_bits1 = next_bit | (next_bit >> 1) 16 | extra_zeros = ((next_bit >> 1) - 1) ^ end_ones 17 | flip_bits2 = extra_zeros | (extra_zeros >> end_ones.bit_length()) 18 | smaller = number ^ flip_bits1 ^ flip_bits2 19 | else: 20 | smaller = None 21 | else: 22 | # Smaller X100...0 --> X010...0 23 | lsb = number & -number 24 | smaller = number ^ (lsb | (lsb >> 1)) 25 | # Larger X011...10...0 --> X100...01...1 26 | end_zeros = lsb - 1 27 | cleared_negated = ~number ^ end_zeros 28 | next_zero = cleared_negated & -cleared_negated 29 | flip_bits1 = next_zero | (next_zero >> 1) 30 | extra_ones = ((next_zero >> 1) - 1) ^ end_zeros 31 | flip_bits2 = extra_ones | (extra_ones >> end_zeros.bit_length()) 32 | larger = number ^ flip_bits1 ^ flip_bits2 33 | return (smaller, larger) 34 | 35 | import unittest 36 | 37 | class Test(unittest.TestCase): 38 | def test_next_numbers(self): 39 | self.assertEqual(next_numbers(8), (4, 16)) 40 | self.assertEqual(next_numbers(12), (10, 17)) 41 | self.assertEqual(next_numbers(15), (None, 23)) 42 | self.assertEqual(next_numbers(143), (124, 151)) 43 | self.assertEqual(next_numbers(159), (126, 175)) 44 | 45 | if __name__ == "__main__": 46 | unittest.main() 47 | 48 | -------------------------------------------------------------------------------- /ch-05-bit-manipulation/05-debugger.py: -------------------------------------------------------------------------------- 1 | # Determine what the following function does. 2 | 3 | def is_power_of_two_or_zero(n): 4 | return (n & (n-1)) == 0 5 | 6 | import unittest 7 | 8 | class Test(unittest.TestCase): 9 | def test_is_power_of_two(self): 10 | self.assertTrue( is_power_of_two_or_zero(0)) 11 | self.assertTrue( is_power_of_two_or_zero(1)) 12 | self.assertTrue( is_power_of_two_or_zero(2)) 13 | self.assertFalse(is_power_of_two_or_zero(3)) 14 | self.assertTrue( is_power_of_two_or_zero(4)) 15 | self.assertFalse(is_power_of_two_or_zero(5)) 16 | self.assertFalse(is_power_of_two_or_zero(6)) 17 | self.assertFalse(is_power_of_two_or_zero(1000)) 18 | self.assertTrue( is_power_of_two_or_zero(1024)) 19 | 20 | if __name__ == "__main__": 21 | unittest.main() 22 | -------------------------------------------------------------------------------- /ch-05-bit-manipulation/06-conversion.py: -------------------------------------------------------------------------------- 1 | # Tell how many bits must be flipped in order to transform one number into 2 | # the other. 3 | 4 | EVEN = 0x5555555555555555 5 | ODD = 0xAAAAAAAAAAAAAAAA 6 | 7 | # This is better for numbers that are nearly the same. 8 | def bits_different_1(a, b): 9 | different = a ^ b 10 | count = 0 11 | while different: 12 | different ^= different & -different 13 | count += 1 14 | return count 15 | 16 | # This is better for numbers that differ by a lot. 17 | def bits_different_2(a, b): 18 | different = a ^ b 19 | counts = (different & EVEN) + ((different & ODD) >> 1) 20 | counts = (counts & 0x3333333333333333) + ((counts & 0xCCCCCCCCCCCCCCCC) >> 2) 21 | counts = (counts & 0x0F0F0F0F0F0F0F0F) + ((counts & 0xF0F0F0F0F0F0F0F0) >> 4) 22 | counts = (counts & 0x00FF00FF00FF00FF) + ((counts & 0xFF00FF00FF00FF00) >> 8) 23 | counts = (counts & 0x0000FFFF0000FFFF) + ((counts & 0xFFFF0000FFFF0000) >> 16) 24 | counts = (counts & 0x00000000FFFFFFFF) + (counts >> 32) 25 | return counts 26 | 27 | import unittest 28 | 29 | class Test(unittest.TestCase): 30 | def test_bits_different_1(self): 31 | self.assertEqual(bits_different_1(16, 2), 2) 32 | self.assertEqual(bits_different_1(17, 34), 4) 33 | self.assertEqual(bits_different_1(15, 97), 5) 34 | 35 | def test_bits_different_2(self): 36 | self.assertEqual(bits_different_2(16, 2), 2) 37 | self.assertEqual(bits_different_2(17, 34), 4) 38 | self.assertEqual(bits_different_2(15, 97), 5) 39 | 40 | if __name__ == "__main__": 41 | unittest.main() 42 | 43 | -------------------------------------------------------------------------------- /ch-05-bit-manipulation/07-pairwise-swap.py: -------------------------------------------------------------------------------- 1 | # Swap the odd and even bits in a number. 2 | 3 | EVEN = 0x5555555555555555 4 | ODD = 0xAAAAAAAAAAAAAAAA 5 | 6 | def swap_odd_even_bits(n): 7 | return ((n & ODD) >> 1) | ((n & EVEN) << 1) 8 | 9 | import unittest 10 | 11 | class Test(unittest.TestCase): 12 | def test_swap_odd_even_bits(self): 13 | self.assertEqual(swap_odd_even_bits(42), 21) 14 | self.assertEqual(swap_odd_even_bits(21), 42) 15 | self.assertEqual(swap_odd_even_bits(43), 23) 16 | self.assertEqual(swap_odd_even_bits(EVEN), ODD) 17 | self.assertEqual(swap_odd_even_bits(511), 767) 18 | self.assertEqual(swap_odd_even_bits(1023), 1023) 19 | 20 | if __name__ == "__main__": 21 | unittest.main() 22 | -------------------------------------------------------------------------------- /ch-05-bit-manipulation/08-draw-line.py: -------------------------------------------------------------------------------- 1 | # Draw a horizontal line from (x_1, y) to (x_2, y). 2 | 3 | def draw_line(screen, width, x1, x2, y): 4 | byte_width = width / 8 5 | height = len(screen) / byte_width 6 | if x1 < x2: 7 | x_start, x_end = x1, x2 8 | else: 9 | x_start, x_end = x2, x1 10 | if x_start < 0 or x_end > width or y > height: 11 | return None 12 | byte = y * byte_width + x_start / 8 13 | byte_end = y * byte_width + x_end / 8 14 | screen[byte] = (1 << (x_start % 8)) - 1 15 | byte += 1 16 | while byte < byte_end: 17 | screen[byte] = 255 18 | byte += 1 19 | screen[byte] = 255 ^ ((1 << (x_end % 8)) - 1) 20 | 21 | import unittest 22 | 23 | class Test(unittest.TestCase): 24 | def test_draw_line(self): 25 | screen = [0, 0, 0, 0, 0, 0, 0, 0, 26 | 0, 0, 0, 0, 0, 0, 0, 0, 27 | 0, 0, 0, 0, 0, 0, 0, 0] 28 | draw_line(screen, 64, 20, 42, 1) 29 | self.assertEqual(screen, [0]*8 + [0, 0, 15, 255, 255, 252, 0, 0] + [0]*8) 30 | 31 | if __name__ == "__main__": 32 | unittest.main() 33 | 34 | -------------------------------------------------------------------------------- /ch-06-math-and-logic-puzzles/01-pill-bottles.py: -------------------------------------------------------------------------------- 1 | # Determine which pill bottle contains heavy pills. 2 | 3 | def pill_bottle(bottles): 4 | pills = [] 5 | for i, bottle in enumerate(bottles): 6 | pills += [bottle.pill()] * i 7 | weight = use_scale(pills) 8 | index = (weight - 190) * 10 9 | return int(index + 0.1) 10 | 11 | def use_scale(pills): 12 | return sum(pills) 13 | 14 | class Bottle(): 15 | def __init__(self, pill_weight=1.0): 16 | self.pill_weight = pill_weight 17 | 18 | def pill(self): 19 | return self.pill_weight 20 | 21 | import unittest 22 | 23 | class Test(unittest.TestCase): 24 | def test_pill_bottle(self): 25 | bottles = [Bottle(), Bottle(), Bottle(), Bottle(), Bottle(), 26 | Bottle(), Bottle(), Bottle(), Bottle(), Bottle(), 27 | Bottle(), Bottle(), Bottle(), Bottle(), Bottle(1.1), 28 | Bottle(), Bottle(), Bottle(), Bottle(), Bottle()] 29 | self.assertEqual(pill_bottle(bottles), 14) 30 | 31 | if __name__ == "__main__": 32 | unittest.main() 33 | 34 | -------------------------------------------------------------------------------- /ch-06-math-and-logic-puzzles/02-basketball.py: -------------------------------------------------------------------------------- 1 | # Choose to try to shoot one of one or two of three given a shooting accuracy. 2 | 3 | def basketball(accuracy): 4 | if accuracy < 0.5: 5 | return "single shot" 6 | else: 7 | return "three shots" 8 | 9 | import unittest 10 | 11 | class Test(unittest.TestCase): 12 | def test_basketball(self): 13 | accuracy = 0 14 | while accuracy <= 1: 15 | if accuracy >= accuracy ** 3 + 3 * accuracy ** 2 * (1 - accuracy): 16 | self.assertEqual(basketball(accuracy), "single shot") 17 | else: 18 | self.assertEqual(basketball(accuracy), "three shots") 19 | accuracy += 0.01 20 | 21 | if __name__ == "__main__": 22 | unittest.main() 23 | 24 | -------------------------------------------------------------------------------- /ch-06-math-and-logic-puzzles/03-dominoes.py: -------------------------------------------------------------------------------- 1 | # Can 31 dominoes cover all but two opposite corners of a chess board? 2 | 3 | def dominoes(): 4 | # Each dominoe must cover one black and one white square. 5 | # But there are different numbers of black and white squares. 6 | # Read up on such parity arguments here: 7 | # http://ihxrelation.blogspot.com/2015/10/tiling-problems.html 8 | return False 9 | 10 | -------------------------------------------------------------------------------- /ch-06-math-and-logic-puzzles/04-ants-on-a-triangle.py: -------------------------------------------------------------------------------- 1 | # How likely is it that ants on a polygon will run into each other if they 2 | # walk at constant speed but start in a random direction. 3 | 4 | def ants_on_a_polygon(n): 5 | return 1 - 0.5 ** (n - 1) 6 | 7 | import unittest 8 | 9 | class Test(unittest.TestCase): 10 | def test_ants_on_a_polygon(self): 11 | self.assertEqual(ants_on_a_polygon(3), 0.75) 12 | self.assertEqual(ants_on_a_polygon(4), 0.875) 13 | 14 | if __name__ == "__main__": 15 | unittest.main() 16 | 17 | -------------------------------------------------------------------------------- /ch-06-math-and-logic-puzzles/05-jugs-of-water.py: -------------------------------------------------------------------------------- 1 | # Measure 4 liters with a 5 liter jug and a 3 liter jug. 2 | 3 | def measure_four(): 4 | jug3 = Jug(3) 5 | jug5 = Jug(5) 6 | jug3.fill() 7 | jug3.pour(jug5) 8 | jug3.fill() 9 | jug3.pour(jug5) 10 | jug5.dump() 11 | jug3.pour(jug5) 12 | jug3.fill() 13 | jug3.pour(jug5) 14 | return jug5.water 15 | 16 | class Jug(): 17 | def __init__(self, capacity): 18 | self.capacity = capacity 19 | self.water = 0 20 | 21 | def fill(self): 22 | self.water = self.capacity 23 | 24 | def pour(self, jug): 25 | total = self.water + jug.water 26 | jug.water = min(jug.capacity, total) 27 | self.water = total - jug.water 28 | 29 | def dump(self): 30 | self.water = 0 31 | 32 | import unittest 33 | 34 | class Test(unittest.TestCase): 35 | def test_measure_four(self): 36 | self.assertEqual(measure_four(), 4) 37 | 38 | if __name__ == "__main__": 39 | unittest.main() 40 | 41 | -------------------------------------------------------------------------------- /ch-06-math-and-logic-puzzles/06-blue-eyed-island.py: -------------------------------------------------------------------------------- 1 | # How many days will it take for all of the islanders to leave? 2 | 3 | def days_to_leave(n): 4 | if n == 1: 5 | return 0 6 | return n 7 | 8 | -------------------------------------------------------------------------------- /ch-06-math-and-logic-puzzles/07-the-apocalypse.py: -------------------------------------------------------------------------------- 1 | # What will the gender ratio be after every family stops having children after 2 | # after they have a girl and not until then. 3 | 4 | def birth_ratio(): 5 | # Everytime a child is born, there is a 0.5 chance of the baby being male 6 | # and 0.5 chance of the baby being a girl. So the ratio is 1:1. 7 | return 1 8 | 9 | -------------------------------------------------------------------------------- /ch-06-math-and-logic-puzzles/08-the-egg-drop-problem.py: -------------------------------------------------------------------------------- 1 | # What is the best floor to drop an egg from in an n-story building if you 2 | # have k eggs left if you want to determine the highest floor from which an 3 | # egg can fall without breaking while minimizing the number of drops you 4 | # must perform. Assume a uniform distribution of breaking floors. 5 | # Note that this differs slightly from the book's problem in that we want to 6 | # minimize *average* drops rather than the worst case scenario. 7 | 8 | def egg_drop_floor(n, k): 9 | if k == 0: 10 | return (None, None) 11 | if k == 1: 12 | return (1, float(n+1)/2.0) 13 | # Allocate a table to store the average drops given a number of floors and a 14 | # number of eggs. 15 | table = [[0.0 for i in xrange(k+1)] for j in xrange(n+1)] 16 | # If we only have one egg left, we need drop from one level higher each time. 17 | for floor in xrange(1, n+1): 18 | table[floor][1] = float(floor+1)/2.0 19 | # Given a number of floors and a number of eggs, consider what happens 20 | # if the egg breaks and if it does not break. 21 | best_floor = n + 1 22 | for eggs_left in xrange(2, k+1): 23 | table[1][eggs_left] = 1.0 24 | for floor in xrange(2, n+1): 25 | minimum_average_drops = n + 1 26 | for drop_floor in xrange(1, floor+1): 27 | egg_breaks = float(drop_floor)/float(floor) 28 | average_drops = 1.0 29 | average_drops += egg_breaks*table[drop_floor][eggs_left - 1] 30 | average_drops += (1 - egg_breaks)*table[floor - drop_floor][eggs_left] 31 | if average_drops < minimum_average_drops: 32 | minimum_average_drops = average_drops 33 | best_floor = drop_floor 34 | table[floor][eggs_left] = minimum_average_drops 35 | return (best_floor, table[n][k]) 36 | 37 | import unittest 38 | 39 | class Test(unittest.TestCase): 40 | def test_egg_drop_floor(self): 41 | (start_floor, best_average_drops) = egg_drop_floor(100, 1) 42 | self.assertEqual(start_floor, 1) 43 | self.assertEqual(best_average_drops, 50.5) 44 | (start_floor, best_average_drops) = egg_drop_floor(128, 8) 45 | self.assertEqual(start_floor, 64) 46 | self.assertAlmostEqual(best_average_drops, 8.0, 1) 47 | # The assertions below were not verified as correct. 48 | (start_floor, best_average_drops) = egg_drop_floor(100, 2) 49 | self.assertEqual(start_floor, 13) 50 | self.assertAlmostEqual(best_average_drops, 10.4, 1) 51 | (start_floor, best_average_drops) = egg_drop_floor(100, 3) 52 | self.assertEqual(start_floor, 28) 53 | self.assertAlmostEqual(best_average_drops, 7.8, 1) 54 | 55 | if __name__ == "__main__": 56 | unittest.main() 57 | 58 | -------------------------------------------------------------------------------- /ch-06-math-and-logic-puzzles/09-lockers.py: -------------------------------------------------------------------------------- 1 | # How many lockers are left open if each locker is toggles once for each of 2 | # its divisors. 3 | 4 | import math 5 | 6 | # Lemma 1: If p^{q} is prime power, then p^{2q} has an odd number of divisors. 7 | # Proof: All of the divisors of p^{2q} are {1, p, p^2, ..., p^{2q}} which 8 | # has odd cardinality. 9 | 10 | # Lemma 2: Let n^2 be a square number. Then n has an odd number of divisors. 11 | # Proof: For a base case, notice that 1 has an odd number of divisors. 12 | # Now, suppose that m^2 has an odd number of divisors. Then the 13 | # number of divisors of (m * p^q)^2 is the product of tow odd numbers 14 | # for any power of primes p^q. This completes the proof by induction 15 | # on the prime power factors of n^2. 16 | 17 | # Theorem: The number n has an odd number of divisors if and only if n is a 18 | # square number. 19 | # Proof: Suppose n is not a square number. Then n= p^q * m where q is odd 20 | # and m is not divisible by p. Then n has an even number of divisors. 21 | 22 | def lockers_left_open(n): 23 | return int(math.sqrt(n-1)) 24 | 25 | import unittest 26 | 27 | class Test(unittest.TestCase): 28 | def test_lockers_left_open(self): 29 | self.assertEqual(lockers_left_open(100), 9) 30 | self.assertEqual(lockers_left_open(200), 14) 31 | 32 | if __name__ == "__main__": 33 | unittest.main() 34 | 35 | -------------------------------------------------------------------------------- /ch-06-math-and-logic-puzzles/10-poison.py: -------------------------------------------------------------------------------- 1 | # Detect which of 1000 bottles contains poison using 10 detection strips. 2 | 3 | def detect_poison(bottles, strips): 4 | for b, bottle in enumerate(bottles): 5 | for s, strip in enumerate(strips): 6 | if b & (1 << s): 7 | strip.add_drop_from(bottle) 8 | index = 0 9 | for s, strip in enumerate(strips): 10 | strip.wait() 11 | if strip.detected_poison: 12 | index |= (1 << s) 13 | return index 14 | 15 | class Bottle(): 16 | def __init__(self, poison): 17 | self.poison = poison 18 | 19 | class Strip(): 20 | def __init__(self): 21 | self.detected_poison = False 22 | self.drops_from = [] 23 | 24 | def add_drop_from(self, bottle): 25 | self.drops_from.append(bottle) 26 | 27 | def wait(self): 28 | for drop in self.drops_from: 29 | if drop.poison: 30 | self.detected_poison = True 31 | return 32 | 33 | import unittest 34 | 35 | class Test(unittest.TestCase): 36 | def test_detect_posion(self): 37 | bottles = [Bottle(ix == 367) for ix in xrange(1000)] 38 | strips = [Strip() for i in xrange(10)] 39 | self.assertEqual(detect_poison(bottles, strips), 367) 40 | 41 | if __name__ == "__main__": 42 | unittest.main() 43 | 44 | -------------------------------------------------------------------------------- /ch-07-object-oriented-design/01-deck-of-cards.py: -------------------------------------------------------------------------------- 1 | # Design a deck of cards. 2 | 3 | class CardDeck(): 4 | def __init__(self, cards): 5 | if cards: 6 | self.cards = cards 7 | else: 8 | self.cards = [] 9 | 10 | def shuffle(self): 11 | for i in xrange(len(self.cards)): 12 | o = random.randint(i) 13 | self.cards[i], self.cards[o] = self.cards[o], self.cards[i] 14 | 15 | def draw_card(self): 16 | return self.cards.pop() 17 | 18 | class BlackjackHand(CardDeck): 19 | def value(self): 20 | value, aces = 0, 0 21 | for card in self.cards: 22 | value += min(card.number, 10) 23 | aces += 1 24 | while value <= 11: 25 | if aces: 26 | value += 10 27 | aces -= 1 28 | return value 29 | 30 | class Card(): 31 | def __init__(self, number, suit): 32 | self.number, self.suit = number, suit 33 | 34 | import unittest 35 | 36 | class Test(unittest.TestCase): 37 | def test_card_deck(self): 38 | deck = CardDeck([Card(2, "Hearts"), Card(4, "Clubs")]) 39 | self.assertEqual(deck.draw_card().suit, "Clubs") 40 | 41 | def test_blackjack_hand(self): 42 | hand = BlackjackHand([Card(5,"Diamonds"), Card(7,"Clubs")]) 43 | self.assertEqual(hand.value(), 12) 44 | hand = BlackjackHand([Card(5,"Diamonds"), Card(1,"Clubs")]) 45 | self.assertEqual(hand.value(), 16) 46 | hand = BlackjackHand([Card(12,"Diamonds"),Card(1,"Clubs")]) 47 | self.assertEqual(hand.value(), 21) 48 | hand = BlackjackHand([Card(12,"Diamonds"),Card(1,"Clubs"),Card(1,"Hearts")]) 49 | self.assertEqual(hand.value(), 12) 50 | 51 | if __name__ == "__main__": 52 | unittest.main() 53 | 54 | -------------------------------------------------------------------------------- /ch-07-object-oriented-design/02-call-center.py: -------------------------------------------------------------------------------- 1 | # Design a call center. 2 | 3 | class CallCenter(): 4 | def __init__(self, respondents, managers, director): 5 | self.respondents = respondents 6 | self.managers = managers 7 | self.director = director 8 | self.respondent_queue = [] 9 | self.call_queue = [] 10 | for respondent in respondents: 11 | respondent.callcenter = self 12 | if not respondent.call: 13 | self.respondent_queue.append(respondent) 14 | 15 | def route_respondent(self, respondent): 16 | if len(self.call_queue): 17 | respondent.take_call(self.call_queue.pop(0)) 18 | else: 19 | self.respondent_queue.append(respondent) 20 | 21 | def route_call(self, call): 22 | if len(self.respondent_queue): 23 | self.respondent_queue.pop(0).take_call(call) 24 | else: 25 | self.call_queue.append(call) 26 | 27 | class Call(): 28 | def __init__(self, issue): 29 | self.issue = issue 30 | self.employee = None 31 | 32 | def resolve(self, handled): 33 | if handled: 34 | self.issue = None 35 | self.employee.finish_call(handled) 36 | 37 | def apologize_and_hangup(self): 38 | # "Try calling our competitor." 39 | self.employee = None 40 | 41 | class Employee(object): 42 | def __init__(self, name, manager): 43 | self.name, self.manager = name, manager 44 | self.call = None 45 | 46 | def take_call(self, call): 47 | if self.call: 48 | self.escalate(call) 49 | else: 50 | self.call = call 51 | self.call.employee = self 52 | 53 | def escalate(self, call): 54 | if self.manager: 55 | self.manager.take_call(call) 56 | else: 57 | call.apologize_and_hangup() 58 | 59 | def finish_call(self, handled=True): 60 | if not handled: 61 | if self.manager: 62 | self.manager.take_call(self.call) 63 | else: 64 | call.apologize_and_hangup() 65 | self.call = None 66 | 67 | class Respondent(Employee): 68 | def finish_call(self, handled=True): 69 | super(Respondent, self).finish_call(handled) 70 | self.callcenter.route_respondent(self) 71 | 72 | class Manager(Employee): 73 | pass 74 | 75 | class Director(Employee): 76 | def __init__(self, name): 77 | super(Director, self).__init__(name, None) 78 | 79 | import unittest 80 | 81 | class Test(unittest.TestCase): 82 | def test_call_center(self): 83 | lashaun = Director("Lashaun") 84 | juan = Manager("Juan", lashaun) 85 | sally = Manager("Sally", lashaun) 86 | boris = Respondent("Boris", juan) 87 | sam = Respondent("Sam", juan) 88 | jordan = Respondent("Jordan", sally) 89 | casey = Respondent("Casey", sally) 90 | center = CallCenter([boris, sam, jordan, casey], [juan, lashaun], sally) 91 | call1 = Call("The toilet") 92 | call2 = Call("The webpage") 93 | call3 = Call("The email") 94 | call4 = Call("The lizard") 95 | call5 = Call("The cloudy weather") 96 | call6 = Call("The noise") 97 | self.assertEqual(len(center.respondent_queue), 4) 98 | center.route_call(call1) 99 | center.route_call(call2) 100 | self.assertEqual(len(center.respondent_queue), 2) 101 | center.route_call(call3) 102 | center.route_call(call4) 103 | center.route_call(call5) 104 | center.route_call(call6) 105 | self.assertEqual(center.call_queue, [call5, call6]) 106 | call1.resolve(True) 107 | self.assertEqual(call1.issue, None) 108 | self.assertEqual(center.call_queue, [call6]) 109 | self.assertEqual(sally.call, None) 110 | self.assertEqual(lashaun.call, None) 111 | call4.resolve(False) 112 | self.assertEqual(sally.call, call4) 113 | call4.resolve(False) 114 | self.assertEqual(sally.call, None) 115 | self.assertEqual(lashaun.call, call4) 116 | call4.resolve(True) 117 | self.assertEqual(lashaun.call, None) 118 | call6.resolve(True) 119 | self.assertEqual(center.respondent_queue, [casey]) 120 | 121 | if __name__ == "__main__": 122 | unittest.main() 123 | 124 | -------------------------------------------------------------------------------- /ch-07-object-oriented-design/03-jukebox.py: -------------------------------------------------------------------------------- 1 | # Design a jukebox. 2 | 3 | class Jukebox(object): 4 | def __init__(self, songs): 5 | self.songs = {} 6 | for song in songs: 7 | self.songs[song.title] = song 8 | self.playing = None 9 | 10 | def play_song(self, title): 11 | if self.playing: 12 | self.stop_song() 13 | self.playing = self.songs[title] 14 | self.playing.play() 15 | 16 | def stop_song(self): 17 | if self.playing: 18 | self.playing.stop() 19 | 20 | class Song(object): 21 | def __init__(self, title, data): 22 | self.title, self.data = title, data 23 | self.play_count = 0 24 | 25 | def play(self): 26 | self.is_playing = True 27 | self.play_count += 1 28 | 29 | def stop(self): 30 | self.is_playing = False 31 | 32 | import unittest 33 | 34 | class Test(unittest.TestCase): 35 | def test_jukebox(self): 36 | song1 = Song("Just Dance", "8598742398723498") 37 | song2 = Song("When You Wish Upon a Beard", "9879834759827209384") 38 | jukebox = Jukebox([song1, song2]) 39 | jukebox.play_song("When You Wish Upon a Beard") 40 | self.assertEqual(song2.play_count, 1) 41 | 42 | if __name__ == "__main__": 43 | unittest.main() 44 | 45 | -------------------------------------------------------------------------------- /ch-07-object-oriented-design/04-parking-lot.py: -------------------------------------------------------------------------------- 1 | # Design a parking lot. 2 | 3 | class ParkingLot(object): 4 | def __init__(self): 5 | self.cars = {} 6 | 7 | def park_car(self, car): 8 | self.cars[car] = True 9 | 10 | def unpark_car(self, car): 11 | del self.cars[car] 12 | 13 | class Car(object): pass 14 | 15 | import unittest 16 | 17 | class Test(unittest.TestCase): 18 | def test_parking_lot(self): 19 | lot = ParkingLot() 20 | car1 = Car() 21 | car2 = Car() 22 | lot.park_car(car1) 23 | lot.park_car(car2) 24 | lot.unpark_car(car1) 25 | self.assertEqual(len(lot.cars), 1) 26 | 27 | if __name__ == "__main__": 28 | unittest.main() 29 | 30 | -------------------------------------------------------------------------------- /ch-07-object-oriented-design/05-online-book-reader.py: -------------------------------------------------------------------------------- 1 | # Design an online book reader. 2 | 3 | class BookReader(object): 4 | def __init__(self, books): 5 | self.books = books 6 | 7 | def read_next_book(self): 8 | if len(self.books): 9 | return self.books.pop(0).text 10 | else: 11 | return None 12 | 13 | class Book(object): 14 | def __init__(self, text): 15 | self.text = text 16 | 17 | import unittest 18 | 19 | class Test(unittest.TestCase): 20 | def test_book_reader(self): 21 | reader = BookReader([Book("The start."), Book("The end.")]) 22 | self.assertEqual(reader.read_next_book(), "The start.") 23 | self.assertEqual(reader.read_next_book(), "The end.") 24 | self.assertEqual(reader.read_next_book(), None) 25 | 26 | if __name__ == "__main__": 27 | unittest.main() 28 | 29 | -------------------------------------------------------------------------------- /ch-07-object-oriented-design/06-jigsaw.py: -------------------------------------------------------------------------------- 1 | # Design a jigsaw puzzle. 2 | 3 | import random 4 | 5 | class Puzzle(object): 6 | def __init__(self, n): 7 | pieces = [[Piece() for r in xrange(n)] for c in xrange(n)] 8 | for r in xrange(n): 9 | for c in xrange(n): 10 | if r: 11 | pieces[r][c].fit_with(pieces[r-1][c]) 12 | if c: 13 | pieces[r][c].fit_with(pieces[r][c-1]) 14 | self.pieces = sum(pieces, []) 15 | for i, _ in enumerate(self.pieces): 16 | j = random.randint(0, len(pieces)-1) 17 | self.pieces[i], self.pieces[j] = self.pieces[j], self.pieces[i] 18 | 19 | def is_solved(self): 20 | for piece in self.pieces: 21 | if piece.connected != piece.fits: 22 | return False 23 | return True 24 | 25 | def solve(puzzle): 26 | for piece in puzzle.pieces: 27 | for other in puzzle.pieces: 28 | if other in piece.fits: 29 | piece.connect(other) 30 | 31 | class Piece(object): 32 | def __init__(self): 33 | self.fits = set() 34 | self.connected = set() 35 | 36 | def fit_with(self, other): 37 | self.fits.add(other) 38 | other.fits.add(self) 39 | 40 | def connect(self, other): 41 | self.connected.add(other) 42 | other.connected.add(self) 43 | 44 | import unittest 45 | 46 | class Test(unittest.TestCase): 47 | def test_puzzle(self): 48 | puzzle = Puzzle(20) 49 | self.assertEqual(puzzle.is_solved(), False) 50 | solve(puzzle) 51 | self.assertEqual(puzzle.is_solved(), True) 52 | 53 | if __name__ == "__main__": 54 | unittest.main() 55 | 56 | -------------------------------------------------------------------------------- /ch-07-object-oriented-design/07-chat-server.py: -------------------------------------------------------------------------------- 1 | # Design a chat server. 2 | 3 | class ChatServer(object): 4 | def __init__(self): 5 | self.participants = set() 6 | self.messages = [] 7 | 8 | def join(self, participant): 9 | self.participants.add(participant) 10 | for message in self.messages[-8:]: 11 | participant.send(message) 12 | 13 | def leave(self, participant): 14 | if participant in self.participants: 15 | self.participants.remove(participant) 16 | participant.clear_history() 17 | 18 | def send_all(self, participant, text): 19 | message = (participant.name, text) 20 | self.messages.append(message) 21 | for p in self.participants: 22 | p.send(message) 23 | 24 | class Participant(object): 25 | def __init__(self, name): 26 | self.name = name 27 | self.history = [] 28 | 29 | def send(self, message): 30 | self.history.append(message) 31 | 32 | def clear_history(self): 33 | self.history = [] 34 | 35 | import unittest 36 | 37 | class Test(unittest.TestCase): 38 | def test_chat_server(self): 39 | server = ChatServer() 40 | albert = Participant("Albert") 41 | jordi = Participant("Jordi") 42 | martha = Participant("Martha") 43 | kat = Participant("Kat") 44 | self.assertEqual(server.participants, set()) 45 | server.join(albert) 46 | for i in xrange(12): 47 | server.send_all(albert, i) 48 | server.join(jordi) 49 | server.leave(jordi) 50 | server.join(martha) 51 | self.assertEqual(server.participants, {albert, martha}) 52 | self.assertEqual(albert.history, [('Albert', i) for i in xrange(12)]) 53 | self.assertEqual(martha.history, [('Albert', i) for i in xrange(4, 12)]) 54 | self.assertEqual(jordi.history, []) 55 | server.send_all(martha, "AlphaGo's victory was surprising!") 56 | server.join(kat) 57 | server.send_all(kat, "It's too bad Arimaa didn't outlast Go.") 58 | self.assertEqual(kat.history[-1], albert.history[-1]) 59 | 60 | if __name__ == "__main__": 61 | unittest.main() 62 | 63 | -------------------------------------------------------------------------------- /ch-07-object-oriented-design/08-othello.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Design an Othello game. 4 | 5 | import random 6 | 7 | MOVE_DOTS = True 8 | WHITE_SEARCH_DEPTH = 6 # None for human 9 | BLACK_SEARCH_DEPTH = None # None for human 10 | 11 | DIRECTIONS = [(1, 0xFEFEFEFEFEFEFEFE), (-1, 0x7F7F7F7F7F7F7F7F), 12 | (8, 0xFFFFFFFFFFFFFFFF), (-8, 0xFFFFFFFFFFFFFFFF), 13 | (7, 0x7F7F7F7F7F7F7F7F), (-7, 0xFEFEFEFEFEFEFEFE), 14 | (9, 0xFEFEFEFEFEFEFEFE), (-9, 0x7F7F7F7F7F7F7F7F)] 15 | 16 | EDGE = 0x7E8181818181817E 17 | CORNER = 0x8100000000000081 18 | NOT_CN = -0x42c300000000c343 19 | BLACK_START = (1<<27) | (1<<36) 20 | WHITE_START = (1<<28) | (1<<35) 21 | 22 | class Othello(object): 23 | def __init__(self, side=0, black=BLACK_START, white=WHITE_START): 24 | self.side = side 25 | self.bits = [black, white] 26 | 27 | def end_score(self): 28 | score = popcount(self.bits[self.side]) - popcount(self.bits[self.side^1]) 29 | return score * 1024 30 | 31 | def evaluate(self): 32 | friendly, enemy = self.bits[self.side], self.bits[self.side^1] 33 | empty_n = neighbors(~(friendly | enemy)) 34 | value = float(popcount(friendly & NOT_CN) - popcount(enemy & NOT_CN)) 35 | value += 0.875 * float(count(friendly & EDGE) - count(enemy & EDGE)) 36 | value += 8 * float(count(friendly & CORNER) - count(enemy & CORNER)) 37 | value -= 1.5 * (popcount(friendly & empty_n) - popcount(enemy & empty_n)) 38 | return value + random.random() 39 | 40 | def legal_moves(self): 41 | moves = [] 42 | friendly, enemy = self.bits[self.side], self.bits[self.side^1] 43 | placement = self.bits[0] | self.bits[1] 44 | potential = neighbors(enemy) & ~placement 45 | while potential: 46 | move = potential & -potential 47 | potential ^= move 48 | for step, mask in DIRECTIONS: 49 | bit = shift(move, step) & mask & enemy 50 | if bit == 0: 51 | continue 52 | bit = shift(bit, step) & mask 53 | while bit & enemy: 54 | bit = shift(bit, step) & mask 55 | if bit & friendly: 56 | moves.append(move) 57 | break 58 | return moves 59 | 60 | def winner(self): 61 | if not len(self.legal_moves()): 62 | black_count = popcount(self.bits[0]) 63 | white_count = popcount(self.bits[1]) 64 | if black_count == white_count: 65 | return "Tie" 66 | elif black_count > white_count: 67 | return "Black" 68 | else: 69 | return "White" 70 | return None 71 | 72 | def make_move(self, row, col): 73 | if row < 0 or row > 7 or col < 0 or col > 7: 74 | raise Exception('Invalid location.') 75 | bit = 1 << (row * 8 + col) 76 | if not bit in self.legal_moves(): 77 | raise Exception('Illegal move.') 78 | self.make_bit_move(bit) 79 | 80 | def make_bit_move(self, move): 81 | self.bits[self.side] |= move 82 | friendly, enemy = self.bits[self.side], self.bits[self.side^1] 83 | for step, mask in DIRECTIONS: 84 | bit = shift(move, step) & mask & enemy 85 | if bit == 0: 86 | continue 87 | bit = shift(bit, step) & mask 88 | while bit & enemy: 89 | bit = shift(bit, step) & mask 90 | if bit & friendly == 0: 91 | continue 92 | bit = shift(bit, -step) 93 | while bit & enemy: 94 | self.bits[0] ^= bit 95 | self.bits[1] ^= bit 96 | bit = shift(bit, -step) 97 | self.side ^= 1 98 | 99 | def negamax(self, depth): 100 | moves = self.legal_moves() 101 | if not depth: 102 | return moves[random.randint(0, len(moves) - 1)] 103 | best_move = moves[0] 104 | best_rating = float('-inf') 105 | solid_best_move = moves[0] 106 | solid_best_rating = float('-inf') 107 | for move in moves: 108 | rating = -self.ab_rate_move(move, depth, float('-inf'), -best_rating) 109 | if rating > best_rating: 110 | best_rating = rating 111 | best_move = move 112 | return best_move 113 | 114 | def ab_rate_move(self, move, depth, lower, upper): 115 | board = Othello(self.side, self.bits[0], self.bits[1]) 116 | board.make_bit_move(move) 117 | if depth == 1: 118 | return board.evaluate() 119 | moves = board.legal_moves() 120 | if len(moves) == 0: 121 | return board.end_score() 122 | best_rating = float('-inf') 123 | new_lower = lower 124 | for m in moves: 125 | rating = -board.ab_rate_move(m, depth-1, -upper, -new_lower) 126 | if rating > best_rating: 127 | best_rating = rating 128 | if best_rating > new_lower: 129 | new_lower = best_rating 130 | if new_lower > upper: 131 | break 132 | return best_rating 133 | 134 | def __str__(self): 135 | moves = self.legal_moves() 136 | parts = ['\n +-----------------+\n'] 137 | for r in xrange(8): 138 | parts.append(' ' + str(8 - r) + '| ') 139 | for c in xrange(8): 140 | bit = 1 << (r * 8 + c) 141 | if bit & self.bits[0]: parts.append('○ ') 142 | elif bit & self.bits[1]: parts.append('● ') 143 | elif MOVE_DOTS and (bit in moves): parts.append('. ') 144 | else: parts.append(' ') 145 | parts.append('|\n') 146 | parts.append(' +-----------------+\n') 147 | parts.append(' a b c d e f g h\n\n') 148 | return "".join(parts) 149 | 150 | def popcount(bits): 151 | counts = (bits & 0x5555555555555555) + ((bits & 0xAAAAAAAAAAAAAAAA) >> 1) 152 | counts = (counts & 0x3333333333333333) + ((counts & 0xCCCCCCCCCCCCCCCC) >> 2) 153 | counts = (counts & 0x0F0F0F0F0F0F0F0F) + ((counts & 0xF0F0F0F0F0F0F0F0) >> 4) 154 | counts = (counts & 0x00FF00FF00FF00FF) + ((counts & 0xFF00FF00FF00FF00) >> 8) 155 | counts = (counts & 0x0000FFFF0000FFFF) + ((counts & 0xFFFF0000FFFF0000) >> 16) 156 | counts = (counts & 0x00000000FFFFFFFF) + (counts >> 32) 157 | return counts 158 | 159 | def count(bits): 160 | count = 0 161 | while bits: 162 | bits &= bits - 1 163 | count += 1 164 | return count 165 | 166 | def neighbors(bits): 167 | nbits = (bits << 1) & 0xFEFEFEFEFEFEFEFE 168 | nbits |= (bits >> 1) & 0x7F7F7F7F7F7F7F7F 169 | nbits |= (bits << 9) & 0xFEFEFEFEFEFEFEFE 170 | nbits |= (bits << 7) & 0x7F7F7F7F7F7F7F7F 171 | nbits |= (bits >> 7) & 0xFEFEFEFEFEFEFEFE 172 | nbits |= (bits >> 9) & 0x7F7F7F7F7F7F7F7F 173 | nbits |= (bits << 8) & 0xFFFFFFFFFFFFFFFF 174 | return nbits | (bits >> 8) 175 | 176 | def shift(bits, step): 177 | if step > 0: 178 | return bits << step 179 | return bits >> -step 180 | 181 | def play_game(): 182 | board = Othello() 183 | while len(board.legal_moves()): 184 | if board.side: 185 | if not WHITE_SEARCH_DEPTH is None: 186 | print(str(board) + 'The computer is making a move for ●.') 187 | board.make_bit_move(board.negamax(WHITE_SEARCH_DEPTH)) 188 | continue 189 | elif not BLACK_SEARCH_DEPTH is None: 190 | print(str(board) + 'The computer is making a move for ○.') 191 | board.make_bit_move(board.negamax(BLACK_SEARCH_DEPTH)) 192 | continue 193 | if board.side: 194 | prompt = 'Choose where to place a ●. (ie "d3")\n> ' 195 | else: 196 | prompt = 'Choose where to place a ○. (ie "d3")\n> ' 197 | line = raw_input(str(board) + prompt) 198 | try: 199 | row = 8 - int(line[1]) 200 | col = ord(line[0]) - 97 201 | board.make_move(row, col) 202 | except Exception as e: 203 | print(e) 204 | print(str(board)) 205 | winner = board.winner() 206 | if winner == "Tie": 207 | print("The game is a tie!") 208 | elif winner == "Black": 209 | print("○ wins!") 210 | else: 211 | print("● wins!") 212 | return winner 213 | 214 | if __name__ == "__main__": 215 | import sys 216 | if sys.argv[-1] == 'play': 217 | play_game() 218 | else: 219 | import unittest 220 | class Test(unittest.TestCase): 221 | def test_othello(self): 222 | board = Othello() 223 | self.assertEqual(board.side, 0) 224 | self.assertEqual(board.end_score(), 0) 225 | self.assertEqual(len(board.legal_moves()), 4) 226 | board.make_bit_move(board.legal_moves()[0]) 227 | self.assertEqual(board.side, 1) 228 | self.assertEqual(len(board.legal_moves()), 3) 229 | board = Othello(1, 0x2040c08000000, 0x181010000000) 230 | self.assertEqual(board.negamax(1), (1 << 56)) 231 | self.assertEqual(board.negamax(2), (1 << 56)) 232 | self.assertEqual(board.negamax(3), (1 << 56)) 233 | unittest.main() 234 | 235 | -------------------------------------------------------------------------------- /ch-07-object-oriented-design/09-circular-array.py: -------------------------------------------------------------------------------- 1 | # Design an efficient circular array. 2 | 3 | class CircularArray(object): 4 | def __init__(self, array): 5 | self.array = array 6 | self.start = 0 7 | 8 | def rotate(self, i): 9 | self.start = (self.start + i) % len(self.array) 10 | 11 | def __iter__(self): 12 | for i in xrange(self.start, len(self.array)): 13 | yield self.array[i] 14 | for i in xrange(0, self.start): 15 | yield self.array[i] 16 | 17 | def __getitem__(self, i): 18 | return self.array[(self.start + i) % len(self.array)] 19 | 20 | def __setitem__(self, i, item): 21 | self.array[(self.start + i) % len(self.array)] = item 22 | 23 | def __delitem__(self, i): 24 | index = (self.start + i) % len(self.array) 25 | del self.array[index] 26 | if index < self.start: 27 | self.start -= 1 28 | 29 | import unittest 30 | 31 | class Test(unittest.TestCase): 32 | def test_circular_array(self): 33 | ca = CircularArray([11, 22, 33, 44, 55, 66, 77]) 34 | ca.rotate(5) 35 | self.assertEqual(ca[0], 66) 36 | array = [] 37 | for item in ca: 38 | array.append(item) 39 | self.assertEqual(array, [66, 77, 11, 22, 33, 44, 55]) 40 | ca[3] = 999 41 | del ca[4] 42 | array = [] 43 | for item in ca: 44 | array.append(item) 45 | self.assertEqual(array, [66, 77, 11, 999, 44, 55]) 46 | ca.rotate(2) 47 | array = [] 48 | for item in ca: 49 | array.append(item) 50 | self.assertEqual(array, [11, 999, 44, 55, 66, 77]) 51 | 52 | if __name__ == "__main__": 53 | unittest.main() 54 | 55 | -------------------------------------------------------------------------------- /ch-07-object-oriented-design/10-minesweeper.py: -------------------------------------------------------------------------------- 1 | # Design Minesweeper. 2 | 3 | class Minesweeper(object): 4 | def __init__(self, rows, cols, bombs_count=None): 5 | self.rows, self.cols = rows, cols 6 | self.game_over = False 7 | self.game_won = False 8 | self.bombs_found = 0 9 | self.bombs_count = bombs_count 10 | if not self.bombs_count: 11 | self.bombs_count = (rows * cols + 10) / 10 12 | self.bombs = [[0 for r in xrange(rows+2)] for c in xrange(cols+2)] 13 | self.counts = [[0 for r in xrange(rows+2)] for c in xrange(cols+2)] 14 | self.revealed = [[0 for r in xrange(rows+2)] for c in xrange(cols+2)] 15 | for i in xrange(self.bombs_count): 16 | bomb_row = random.randint(1, rows) 17 | bomb_col = random.randint(1, cols) 18 | if not self.bombs[bomb_row][bomb_col]: 19 | self.bombs[bomb_row][bomb_col] = 1 20 | for row in [bomb_row-1, bomb_row, bomb_row+1]: 21 | for col in [bomb_col-1, bomb_col, bomb_col+1]: 22 | self.counts[row][col] += 1 23 | 24 | def __str__(self): 25 | line1, line2, line3 = " ", " ", " " 26 | for c in xrange(1, self.cols+1): 27 | label = str(c) 28 | while len(label) < 3: 29 | label = " " + label 30 | line1 += " " + label[0] 31 | line2 += " " + label[1] 32 | line3 += " " + label[2] 33 | parts = [line1, '\n', line2, '\n', line3, '\n'] 34 | parts.append((' +' + '-' * (2*self.cols+1)) + '+\n') 35 | for r in xrange(1, self.rows+1): 36 | label = str(r) 37 | while len(label) < 3: 38 | label = " " + label 39 | parts += label + '| ' 40 | for c in xrange(1, self.cols+1): 41 | if self.revealed[r][c]: 42 | if self.bombs[r][c]: 43 | parts.append("B ") 44 | elif self.counts[r][c]: 45 | parts.append(str(self.counts[r][c]) + " ") 46 | else: 47 | parts.append(" ") 48 | else: 49 | parts.append(". ") 50 | parts += '|\n' 51 | parts += [' +' + ('-' * (2*self.cols+1)) + '+\n'] 52 | return "".join(parts) 53 | 54 | def mark_clear(self, row, col): 55 | if row < 1 or row > self.rows or col < 1 or col > self.cols: 56 | return 57 | if self.revealed[row][col]: 58 | return 59 | self.revealed[row][col] = True 60 | if self.bombs[row][col]: 61 | self.game_over = True 62 | elif self.counts[row][col] == 0: 63 | for r in xrange(row - 1, row + 2): 64 | for c in xrange(col - 1, col + 2): 65 | self.mark_clear(r, c) 66 | 67 | 68 | def mark_bomb(self, row, col): 69 | self.revealed[row][col] = True 70 | if not self.bombs[row][col]: 71 | self.game_over = True 72 | else: 73 | self.bombs_found += 1 74 | if self.bombs_found == self.bombs_count: 75 | self.game_won = True 76 | self.game_over = True 77 | 78 | prompt = "Enter row,col to clear and bomb,row,col to label a bomb.\n> " 79 | 80 | def play_game(): 81 | rows = int(sys.argv[-2]) 82 | cols = int(sys.argv[-1]) 83 | minesweeper = Minesweeper(rows, cols) 84 | while not minesweeper.game_over: 85 | line = raw_input(str(minesweeper) + prompt) 86 | try: 87 | coords = line.split(",") 88 | if coords[0].strip().lower() == "bomb": 89 | row, col = int(coords[1]), int(coords[2]) 90 | minesweeper.mark_bomb(row, col) 91 | else: 92 | row, col = int(coords[0]), int(coords[1]) 93 | minesweeper.mark_clear(row, col) 94 | except Exception as e: 95 | print(e) 96 | print(str(minesweeper)) 97 | if minesweeper.game_won: 98 | print("You win!") 99 | else: 100 | print("You lose.") 101 | 102 | 103 | class MockRandom(object): 104 | def __init__(self): 105 | self.values = [1, 3, 1, 1] 106 | 107 | def randint(self, lower, upper): 108 | return self.values.pop(0) 109 | 110 | if __name__ == "__main__": 111 | import sys 112 | if len(sys.argv) > 2 and sys.argv[-3] == "play": 113 | import random 114 | play_game() 115 | else: 116 | import unittest 117 | print("Use the option --play {rows} {cols} to play Minesweeper.") 118 | global random 119 | random = MockRandom() 120 | 121 | class Test(unittest.TestCase): 122 | def test_minesweeper(self): 123 | m = Minesweeper(4, 4) 124 | self.assertEqual(m.bombs, [[0, 0, 0, 0, 0, 0], 125 | [0, 1, 0, 1, 0, 0], 126 | [0, 0, 0, 0, 0, 0], 127 | [0, 0, 0, 0, 0, 0], 128 | [0, 0, 0, 0, 0, 0], 129 | [0, 0, 0, 0, 0, 0]]) 130 | unittest.main() 131 | 132 | -------------------------------------------------------------------------------- /ch-07-object-oriented-design/11-file-system.py: -------------------------------------------------------------------------------- 1 | # Design an in-memory file system. 2 | 3 | class Directory(object): 4 | def __init__(self, name): 5 | self.name = name 6 | self.parent = None 7 | self.files = {} 8 | self.subdirectories = {} 9 | 10 | def addDirectory(self, directory): 11 | if directory.parent: 12 | del directory.parent.subdirectories[directory.name] 13 | directory.parent = self 14 | self.subdirectories[directory.name] = directory 15 | 16 | def addFile(self, file): 17 | self.files[file.name] = file 18 | 19 | def get(self, path): 20 | parts = path.split("/") 21 | directory = self 22 | for part in parts: 23 | if part == '..': 24 | directory = directory.parent 25 | if not directory: 26 | return None 27 | elif part in directory.subdirectories: 28 | directory = directory.subdirectories[part] 29 | elif part in directory.files: 30 | return directory.files[part] 31 | else: 32 | return None 33 | return directory 34 | 35 | class File(object): 36 | def __init__(self, name, content): 37 | self.name, self.content = name, content 38 | 39 | class NoneHash(dict): 40 | def __missing__(self, item): 41 | return None 42 | 43 | import unittest 44 | 45 | class Test(unittest.TestCase): 46 | def test_directory(self): 47 | directory0 = Directory("root") 48 | directory1 = Directory("food") 49 | directory2 = Directory("vegetables") 50 | file1 = File("arugula.png", "345235434565") 51 | directory0.addDirectory(directory1) 52 | directory1.addDirectory(directory2) 53 | directory2.addFile(file1) 54 | self.assertEqual(directory0.get("food/vegetables/rutabaga.png"), None) 55 | self.assertEqual(directory0.get("food/vegetables/arugula.png"), file1) 56 | self.assertEqual(directory0.get(".."), None) 57 | self.assertEqual(directory2.get(".."), directory1) 58 | 59 | if __name__ == "__main__": 60 | unittest.main() 61 | 62 | -------------------------------------------------------------------------------- /ch-07-object-oriented-design/12-hash-table.py: -------------------------------------------------------------------------------- 1 | # Implement a hash table. 2 | 3 | class HashTable(object): 4 | def __init__(self, array_size=64): 5 | self.array = [None for _ in xrange(array_size)] 6 | self.count = 0 7 | self.capacity = 0.75 * array_size 8 | 9 | def add(self, item, value): 10 | if self.count >= self.capacity: 11 | self.expand() 12 | self.count += 1 13 | new_node = Node(item, value) 14 | index = hash(item) % len(self.array) 15 | node = self.array[index] 16 | if node: 17 | while node.next: 18 | node = node.next 19 | node.next = new_node 20 | else: 21 | self.array[index] = new_node 22 | 23 | def lookup(self, item): 24 | node = self.array[hash(item) % len(self.array)] 25 | while node: 26 | if node.item == item: 27 | return node.value 28 | node = node.next 29 | return None 30 | 31 | def delete(self, item): 32 | index = hash(item) % len(self.array) 33 | node = self.array[index] 34 | prev = None 35 | while node: 36 | if node.item == item: 37 | self.count -= 1 38 | if prev: 39 | prev.next = node.next 40 | else: 41 | self.array[index] = node.next 42 | prev = node 43 | node = node.next 44 | 45 | def expand(self): 46 | new_array = [None for _ in xrange(2 * len(self.array))] 47 | self.count = 0 48 | self.capacity *= 2 49 | self.array, prev_array = new_array, self.array 50 | for node in prev_array: 51 | while node: 52 | self.add(node.item, node.value) 53 | node = node.next 54 | 55 | def __len__(self): 56 | return self.count 57 | 58 | class Node(object): 59 | def __init__(self, item, value, next=None): 60 | self.item, self.value, self.next = item, value, next 61 | 62 | import unittest 63 | 64 | class Test(unittest.TestCase): 65 | def test_hash_table(self): 66 | ht = HashTable(8) 67 | self.assertEqual(ht.capacity, 6) 68 | self.assertEqual(len(ht), 0) 69 | self.assertEqual(len(ht.array), 8) 70 | ht.add(15, "fifteen") 71 | ht.add(7, "seven") 72 | self.assertEqual(len(ht), 2) 73 | node_seven = ht.array[7] 74 | self.assertEqual(ht.array, [None,None,None,None,None,None,None,node_seven]) 75 | self.assertEqual(node_seven.item, 15) 76 | self.assertEqual(node_seven.value, "fifteen") 77 | self.assertEqual(node_seven.next.item, 7) 78 | self.assertEqual(node_seven.next.value, "seven") 79 | self.assertEqual(node_seven.next.next, None) 80 | self.assertEqual(ht.lookup(15), "fifteen") 81 | self.assertEqual(ht.lookup(7), "seven") 82 | self.assertEqual(ht.lookup(24), None) 83 | ht.delete(24) 84 | ht.add(23, "twenty three") 85 | ht.delete(7) 86 | self.assertEqual(ht.lookup(7), None) 87 | ht.delete(15) 88 | self.assertEqual(ht.lookup(23), "twenty three") 89 | self.assertEqual(ht.lookup(15), None) 90 | self.assertEqual(len(ht), 1) 91 | ht.add(31, "thirty one") 92 | ht.add(15, "FIFTEEN") 93 | for i in xrange(70, 90): 94 | ht.add(i, "number " + str(i)) 95 | self.assertEqual(len(ht), 23) 96 | self.assertEqual(len(ht.array), 32) 97 | self.assertEqual(ht.capacity, 24) 98 | node_fifteen = ht.array[15] 99 | self.assertEqual(node_fifteen.value, "FIFTEEN") 100 | self.assertEqual(node_fifteen.next.value, "number 79") 101 | self.assertEqual(ht.lookup(81), "number 81") 102 | 103 | if __name__ == "__main__": 104 | unittest.main() 105 | 106 | -------------------------------------------------------------------------------- /ch-08-recursion-and-dynamic-programming/01-triple-step.py: -------------------------------------------------------------------------------- 1 | # Give the number of ways to climb n steps 1, 2, or 3 steps at a time. 2 | 3 | def triple_step(n): 4 | counts = [1, 1, 2] 5 | if n < 3: 6 | return counts[n] 7 | i = 2 8 | while i < n: 9 | i += 1 10 | counts[i % 3] = sum(counts) 11 | return counts[i % 3] 12 | 13 | import unittest 14 | 15 | class Test(unittest.TestCase): 16 | def test_triple_step(self): 17 | self.assertEqual(triple_step(3), 4) 18 | self.assertEqual(triple_step(7), 44) 19 | 20 | if __name__ == "__main__": 21 | unittest.main() 22 | 23 | -------------------------------------------------------------------------------- /ch-08-recursion-and-dynamic-programming/02-robot-in-a-grid.py: -------------------------------------------------------------------------------- 1 | # Guide a robot with "right" and "down" steps from the upper left corner of a grid 2 | # to the lower right corner. 3 | 4 | def path_through_grid(grid): 5 | if len(grid) == 0: 6 | return [] 7 | search = [] 8 | for r, row in enumerate(grid): 9 | search.append([]) 10 | for c, blocked in enumerate(row): 11 | if r == 0 and c == 0: 12 | search[r].append("start") 13 | elif blocked: 14 | search[r].append(None) 15 | elif r > 0 and search[r-1][c]: 16 | search[r].append("down") 17 | elif c > 0 and search[r][c-1]: 18 | search[r].append("right") 19 | else: 20 | search[r].append(None) 21 | path = ["end"] 22 | r, c = len(grid) - 1, len(grid[0]) - 1 23 | if not search[r][c]: 24 | return None 25 | while c > 0 or r > 0: 26 | path.append(search[r][c]) 27 | if search[r][c] == "down": 28 | r -= 1 29 | else: 30 | c -= 1 31 | path.append("start") 32 | path.reverse() 33 | return path 34 | 35 | import unittest 36 | 37 | class Test(unittest.TestCase): 38 | def test_path_through_grid(self): 39 | grid = [[0, 0, 0, 0, 0, 0, 1], 40 | [0, 1, 1, 0, 1, 1, 0], 41 | [0, 0, 1, 0, 0, 0, 0], 42 | [1, 1, 0, 0, 0, 1, 0]] 43 | self.assertEqual(path_through_grid(grid), ["start", "right", "right", 44 | "right", "down", "down", "right", "right", "right", "down", "end"]) 45 | grid = [[0, 0, 0, 0, 0, 0, 1], 46 | [0, 1, 0, 1, 1, 1, 0], 47 | [0, 0, 0, 1, 0, 0, 0], 48 | [1, 1, 0, 0, 0, 1, 0]] 49 | self.assertEqual(path_through_grid(grid), None) 50 | 51 | if __name__ == "__main__": 52 | unittest.main() 53 | 54 | -------------------------------------------------------------------------------- /ch-08-recursion-and-dynamic-programming/03-magic-index.py: -------------------------------------------------------------------------------- 1 | # Find a magic index in a sorted array. 2 | 3 | def magic_index_distinct(array): 4 | if len(array) == 0 or array[0] > 0 or array[-1] < len(array) - 1: 5 | return None 6 | return magic_index_distinct_bounds(array, 0, len(array)) 7 | 8 | def magic_index_distinct_bounds(array, lower, upper): 9 | if lower == upper: 10 | return None 11 | middle = (lower + upper) / 2 12 | if array[middle] == middle: 13 | return middle 14 | elif array[middle] > middle: 15 | return magic_index_distinct_bounds(array, lower, middle) 16 | else: 17 | return magic_index_distinct_bounds(array, middle+1, upper) 18 | 19 | def magic_index(array): 20 | index = 0 21 | while index < len(array): 22 | if index == array[index]: 23 | return index 24 | index = max(array[index], index + 1) 25 | return None 26 | 27 | import unittest 28 | 29 | class Test(unittest.TestCase): 30 | def test_magic_index_distinct(self): 31 | self.assertEqual(magic_index_distinct([3,4,5]), None) 32 | self.assertEqual(magic_index_distinct([-2,-1,0,2]), None) 33 | self.assertEqual(magic_index_distinct([-20,0,1,2,3,4,5,6,20]), None) 34 | self.assertEqual(magic_index_distinct([-20,0,1,2,3,4,5,7,20]), 7) 35 | self.assertEqual(magic_index_distinct([-20,1,2,3,4,5,6,20]), 4) 36 | 37 | def test_magic_index(self): 38 | self.assertEqual(magic_index([3,4,5]), None) 39 | self.assertEqual(magic_index([-2,-1,0,2]), None) 40 | self.assertEqual(magic_index([-20,0,1,2,3,4,5,6,20]), None) 41 | self.assertEqual(magic_index([-20,0,1,2,3,4,5,7,20]), 7) 42 | self.assertEqual(magic_index([-20,1,2,3,4,5,6,20]), 1) 43 | self.assertEqual(magic_index([-20,5,5,5,5,5,6,20]), 5) 44 | 45 | if __name__ == "__main__": 46 | unittest.main() 47 | 48 | -------------------------------------------------------------------------------- /ch-08-recursion-and-dynamic-programming/04-power-set.py: -------------------------------------------------------------------------------- 1 | # Compute the power set of a set. 2 | 3 | def power_set(set0): 4 | ps = {frozenset()} 5 | for element in set0: 6 | additions = set() 7 | for subset in ps: 8 | additions.add(subset.union(element)) 9 | ps = ps.union(additions) 10 | return ps 11 | 12 | import unittest 13 | 14 | class Test(unittest.TestCase): 15 | def test_power_set(self): 16 | s = {'a', 'b', 'c', 'd'} 17 | ps = power_set(s) 18 | self.assertEqual(len(ps), 16) 19 | subsets = [set(), {'a'}, {'b'}, {'c'}, {'d'}, 20 | {'a', 'b'}, {'a', 'c'}, {'a', 'd'}, {'b', 'c'}, {'b', 'd'}, {'c', 'd'}, 21 | {'a', 'b', 'c'}, {'a', 'b', 'd'}, {'a', 'c', 'd'}, {'b', 'c', 'd'}, s] 22 | self.assertEqual(ps, set([frozenset(s) for s in subsets])) 23 | 24 | if __name__ == "__main__": 25 | unittest.main() 26 | 27 | -------------------------------------------------------------------------------- /ch-08-recursion-and-dynamic-programming/05-recursive-multiply.py: -------------------------------------------------------------------------------- 1 | # Multiply two positive integers without *. 2 | 3 | # O(lg(min(a, b))) 4 | 5 | def multiply(a, b): 6 | if a > b: 7 | n, m = a, b 8 | else: 9 | n, m = b, a 10 | product = 0 11 | while m: 12 | bit = m & -m 13 | m ^= bit 14 | exponent = bit.bit_length() - 1 15 | product += n << exponent 16 | return product 17 | 18 | import unittest 19 | 20 | class Test(unittest.TestCase): 21 | def test_multiply(self): 22 | self.assertEqual(multiply(2, 2), 4) 23 | self.assertEqual(multiply(1, 125), 125) 24 | self.assertEqual(multiply(7, 11), 77) 25 | self.assertEqual(multiply(10000000010, 21), 210000000210) 26 | 27 | if __name__ == "__main__": 28 | unittest.main() 29 | 30 | -------------------------------------------------------------------------------- /ch-08-recursion-and-dynamic-programming/06-towers-of-hanoi.py: -------------------------------------------------------------------------------- 1 | # Move the blocks on tower1 to tower3. 2 | 3 | def towers_of_hanoi(tower1, tower2, tower3, n=None): 4 | if n is None: 5 | n = len(tower1.discs) 6 | if n == 0: 7 | return 8 | towers_of_hanoi(tower1, tower3, tower2, n - 1) 9 | disc = tower1.discs.pop() 10 | #print("Moving disc {} from {} to {}.".format(disc, tower1, tower3)) 11 | tower3.discs.append(disc) 12 | towers_of_hanoi(tower2, tower1, tower3, n - 1) 13 | 14 | class Tower(object): 15 | def __init__(self, name, discs=None): 16 | self.name = name 17 | if discs: 18 | self.discs = discs 19 | else: 20 | self.discs = [] 21 | 22 | def __str__(self): 23 | return self.name 24 | 25 | import unittest 26 | 27 | class Test(unittest.TestCase): 28 | def test_towers_of_hanoi(self): 29 | tower1 = Tower("Tower1", ["6", "5", "4", "3", "2", "1"]) 30 | tower2 = Tower("Tower2") 31 | tower3 = Tower("Tower3") 32 | towers_of_hanoi(tower1, tower2, tower3) 33 | self.assertEqual(tower1.discs, []) 34 | self.assertEqual(tower2.discs, []) 35 | self.assertEqual(tower3.discs, ["6", "5", "4", "3", "2", "1"]) 36 | 37 | if __name__ == "__main__": 38 | unittest.main() 39 | 40 | -------------------------------------------------------------------------------- /ch-08-recursion-and-dynamic-programming/07-permutations-without-dups.py: -------------------------------------------------------------------------------- 1 | # List all permutations of a string that contains no duplicate letters. 2 | 3 | def permutations(string): 4 | return partial_permutations("", string) 5 | 6 | def partial_permutations(partial, letters_left): 7 | if len(letters_left) == 0: 8 | return [partial] 9 | permutations = [] 10 | for i, letter in enumerate(letters_left): 11 | next_letters_left = letters_left[:i] + letters_left[(i+1):] 12 | permutations += partial_permutations(partial + letter, next_letters_left) 13 | return permutations 14 | 15 | import unittest 16 | 17 | class Test(unittest.TestCase): 18 | def test_permutations(self): 19 | self.assertEqual(permutations("ABCD"), ["ABCD", "ABDC", "ACBD", "ACDB", 20 | "ADBC", "ADCB", "BACD", "BADC", "BCAD", "BCDA", "BDAC", "BDCA", 21 | "CABD", "CADB", "CBAD", "CBDA", "CDAB", "CDBA", "DABC", "DACB", 22 | "DBAC", "DBCA", "DCAB", "DCBA"]) 23 | 24 | if __name__ == "__main__": 25 | unittest.main() 26 | 27 | -------------------------------------------------------------------------------- /ch-08-recursion-and-dynamic-programming/08-permutations-with-dups.py: -------------------------------------------------------------------------------- 1 | # List all permutation of the letters in the given string. 2 | 3 | def permutations(string): 4 | return partial_permutations("", sorted(string)) 5 | 6 | def partial_permutations(partial, letters): 7 | if len(letters) == 0: 8 | return [partial] 9 | permutations = [] 10 | previous_letter = None 11 | for i, letter in enumerate(letters): 12 | if letter == previous_letter: 13 | continue 14 | next_partial = partial + letter 15 | next_letters = letters[:i] + letters[(i+1):] 16 | permutations += partial_permutations(next_partial, next_letters) 17 | previous_letter = letter 18 | return permutations 19 | 20 | import unittest 21 | 22 | class Test(unittest.TestCase): 23 | def test_permutations(self): 24 | self.assertEqual(permutations("abca"), ["aabc", "aacb", "abac", "abca", 25 | "acab", "acba", "baac", "baca", "bcaa", "caab", "caba", "cbaa"]) 26 | self.assertEqual(permutations("ABCD"), ["ABCD", "ABDC", "ACBD", "ACDB", 27 | "ADBC", "ADCB", "BACD", "BADC", "BCAD", "BCDA", "BDAC", "BDCA", 28 | "CABD", "CADB", "CBAD", "CBDA", "CDAB", "CDBA", "DABC", "DACB", 29 | "DBAC", "DBCA", "DCAB", "DCBA"]) 30 | 31 | if __name__ == "__main__": 32 | unittest.main() 33 | 34 | -------------------------------------------------------------------------------- /ch-08-recursion-and-dynamic-programming/09-parens.py: -------------------------------------------------------------------------------- 1 | # List all valid strings containing n opening and n closing parenthesis. 2 | 3 | # Note that parens1 happens to be faster and more space efficient than parens2, 4 | # which is faster than parens3. The slowest is parens4 only because it is not 5 | # memoized. 6 | 7 | def parens1(n): 8 | parens_of_length = [[""]] 9 | if n == 0: 10 | return parens_of_length[0] 11 | for length in xrange(1, n + 1): 12 | parens_of_length.append([]) 13 | for i in xrange(length): 14 | for inside in parens_of_length[i]: 15 | for outside in parens_of_length[length - i - 1]: 16 | parens_of_length[length].append("(" + inside + ")" + outside) 17 | return parens_of_length[n] 18 | 19 | def parens2(n, open_count=0, close_count=0, memo=None): 20 | if open_count + close_count == 2 * n: 21 | return [""] 22 | key = (n - open_count - close_count, open_count) 23 | if memo is None: 24 | memo = {} 25 | elif key in memo: 26 | return memo[key] 27 | parens = [] 28 | if open_count < n: 29 | parens += ["(" + end for end in parens2(n, open_count+1, close_count, memo)] 30 | if close_count < open_count: 31 | parens += [")" + end for end in parens2(n, open_count, close_count+1, memo)] 32 | memo[key] = parens 33 | return parens 34 | 35 | def parens3(n): 36 | return parens_memo3(n, 0, 0, {}) 37 | 38 | def parens_memo3(n, open_count, close_count, memo): 39 | if open_count + close_count == 2 * n: 40 | return [""] 41 | key = (n - open_count - close_count, open_count) 42 | if key in memo: 43 | return memo[key] 44 | parens = [] 45 | if open_count < n: 46 | for end in parens_memo3(n, open_count + 1, close_count, memo): 47 | parens.append("(" + end) 48 | if close_count < open_count: 49 | for end in parens_memo3(n, open_count, close_count + 1, memo): 50 | parens.append(")" + end) 51 | memo[key] = parens 52 | return parens 53 | 54 | def parens4(n, partial="", open_count=0, close_count=0): 55 | if open_count + close_count == 2 * n: 56 | return [partial] 57 | parens = [] 58 | if open_count < n: 59 | parens += parens4(n, partial + "(", open_count + 1, close_count) 60 | if close_count < open_count: 61 | parens += parens4(n, partial + ")", open_count, close_count + 1) 62 | return parens 63 | 64 | import unittest 65 | 66 | class Test(unittest.TestCase): 67 | def test_parens1(self): 68 | self.assertEqual(parens1(1), ["()"]) 69 | self.assertEqual(parens1(2), ["()()", "(())"]) 70 | self.assertEqual(parens1(3), ["()()()", "()(())", "(())()", "(()())", 71 | "((()))"]) 72 | 73 | def test_parens2(self): 74 | self.assertEqual(parens2(1), ["()"]) 75 | self.assertEqual(parens2(2), ["(())", "()()"]) 76 | self.assertEqual(parens2(3), ["((()))", "(()())", "(())()", "()(())", 77 | "()()()"]) 78 | self.assertEqual(set(parens1(7)), set(parens2(7))) 79 | 80 | def test_parens3(self): 81 | self.assertEqual(parens3(1), ["()"]) 82 | self.assertEqual(parens3(2), ["(())", "()()"]) 83 | self.assertEqual(parens3(3), ["((()))", "(()())", "(())()", "()(())", 84 | "()()()"]) 85 | self.assertEqual(set(parens1(7)), set(parens3(7))) 86 | 87 | def test_parens4(self): 88 | self.assertEqual(parens4(1), ["()"]) 89 | self.assertEqual(parens4(2), ["(())", "()()"]) 90 | self.assertEqual(parens4(3), ["((()))", "(()())", "(())()", "()(())", 91 | "()()()"]) 92 | self.assertEqual(set(parens1(7)), set(parens4(7))) 93 | 94 | if __name__ == "__main__": 95 | unittest.main() 96 | 97 | -------------------------------------------------------------------------------- /ch-08-recursion-and-dynamic-programming/10-paint-fill.py: -------------------------------------------------------------------------------- 1 | # Fill in the region containing the point with the given color. 2 | 3 | def paint_fill(image, x, y, color): 4 | if x < 0 or y < 0 or len(image) <= y or len(image[y]) <= x: 5 | return 6 | old_color = image[y][x] 7 | if old_color == color: 8 | return 9 | paint_fill_color(image, x, y, color, old_color) 10 | 11 | def paint_fill_color(image, x, y, new_color, old_color): 12 | if image[y][x] != old_color: 13 | return 14 | image[y][x] = new_color 15 | if y > 0: 16 | paint_fill_color(image, x, y - 1, new_color, old_color) 17 | if y < len(image) - 1: 18 | paint_fill_color(image, x, y + 1, new_color, old_color) 19 | if x > 0: 20 | paint_fill_color(image, x - 1, y, new_color, old_color) 21 | if x < len(image[y]) - 1: 22 | paint_fill_color(image, x + 1, y, new_color, old_color) 23 | 24 | import unittest 25 | 26 | class Test(unittest.TestCase): 27 | def test_paint_fill(self): 28 | image1 = [[10, 10, 10, 10, 10, 10, 10, 40], 29 | [30, 20, 20, 10, 10, 40, 40, 40], 30 | [10, 10, 20, 20, 10, 10, 10, 10], 31 | [10, 10, 30, 20, 20, 20, 20, 10], 32 | [40, 40, 10, 10, 10, 10, 10, 10]] 33 | image2 = [[30, 30, 30, 30, 30, 30, 30, 40], 34 | [30, 20, 20, 30, 30, 40, 40, 40], 35 | [10, 10, 20, 20, 30, 30, 30, 30], 36 | [10, 10, 30, 20, 20, 20, 20, 30], 37 | [40, 40, 30, 30, 30, 30, 30, 30]] 38 | image3 = [[30, 30, 30, 30, 30, 30, 30, 40], 39 | [30, 20, 20, 30, 30, 40, 40, 40], 40 | [30, 30, 20, 20, 30, 30, 30, 30], 41 | [30, 30, 30, 20, 20, 20, 20, 30], 42 | [40, 40, 30, 30, 30, 30, 30, 30]] 43 | paint_fill(image1, 3, 1, 30) 44 | self.assertEqual(image1, image2) 45 | paint_fill(image1, 3, 1, 10) 46 | paint_fill(image1, 3, 1, 30) 47 | self.assertEqual(image1, image3) 48 | 49 | if __name__ == "__main__": 50 | unittest.main() 51 | 52 | -------------------------------------------------------------------------------- /ch-08-recursion-and-dynamic-programming/11-coins.py: -------------------------------------------------------------------------------- 1 | # Count the number of ways to make change for a given number of cents. 2 | 3 | # Why settle for a slower algorithm when a constant time algorithm exists? 4 | 5 | def coins1(cents): 6 | count = 0 7 | for c in xrange(cents, -1, -25): 8 | count += coins1_pnd(c) 9 | return count 10 | 11 | def coins1_pnd(cents): 12 | count = 0 13 | for c in xrange(cents, -1, -10): 14 | count += (c / 5) + 1 15 | return count 16 | 17 | def coins2(cents): 18 | n = cents / 5 19 | return int(n*n*n/60.0 + n*n*9/40.0 + n*53/60.0 + 301/240.0) 20 | 21 | import unittest 22 | 23 | class Test(unittest.TestCase): 24 | def test_coins1(self): 25 | self.assertEqual(coins1(0), 1) 26 | self.assertEqual(coins1(1), 1) 27 | self.assertEqual(coins1(4), 1) 28 | self.assertEqual(coins1(5), 2) 29 | self.assertEqual(coins1(15), 6) 30 | self.assertEqual(coins1(17), 6) 31 | self.assertEqual(coins1(20), 9) 32 | self.assertEqual(coins1(25), 13) 33 | self.assertEqual(coins1(52), 49) 34 | 35 | def test_coins2(self): 36 | for c in xrange(1000): 37 | self.assertEqual(coins1(c), coins2(c)) 38 | self.assertEqual(coins2(1000000), 133342333510001) 39 | 40 | if __name__ == "__main__": 41 | unittest.main() 42 | 43 | -------------------------------------------------------------------------------- /ch-08-recursion-and-dynamic-programming/12-eight-queens.py: -------------------------------------------------------------------------------- 1 | # Find all arrangements of eight queens on a chess board that cannot attack 2 | # each other. 3 | 4 | STEPS = [ 5 | lambda b: (b << 1) & 0xFEFEFEFEFEFEFEFE, 6 | lambda b: (b << 7) & 0x7F7F7F7F7F7F7F7F, 7 | lambda b: (b << 9) & 0xFEFEFEFEFEFEFEFE, 8 | lambda b: (b << 8) & 0xFFFFFFFFFFFFFFFF, 9 | lambda b: (b >> 1) & 0x7F7F7F7F7F7F7F7F, 10 | lambda b: (b >> 7) & 0xFEFEFEFEFEFEFEFE, 11 | lambda b: (b >> 9) & 0x7F7F7F7F7F7F7F7F, 12 | lambda b: (b >> 8)] 13 | 14 | def eight_queens(): 15 | return eight_queens_partial(0, -1, 0xFF00000000000000) 16 | 17 | def eight_queens_partial(queens, available, row): 18 | if row == 0: 19 | return [queens] 20 | placements = [] 21 | possibility = available & row 22 | next_row = row >> 8 23 | while possibility: 24 | queen = possibility & -possibility 25 | possibility ^= queen 26 | next_queens = queens | queen 27 | next_available = available & ~queen_reach(queen) 28 | placements += eight_queens_partial(next_queens, next_available, next_row) 29 | return placements 30 | 31 | def queen_reach(bit): 32 | reach = bit 33 | for step in STEPS: 34 | move = bit 35 | while move: 36 | reach |= move 37 | move = step(move) 38 | return reach 39 | 40 | def show(placement): 41 | parts = ["\n+-----------------+\n"] 42 | for row in xrange(8): 43 | parts.append('| ') 44 | for col in xrange(8): 45 | bit = 1 << (row * 8 + col) 46 | if bit & placement: 47 | parts.append('Q ') 48 | else: 49 | parts.append(' ') 50 | parts.append('|\n') 51 | parts.append("+-----------------+\n") 52 | return "".join(parts) 53 | 54 | import unittest 55 | 56 | class Test(unittest.TestCase): 57 | def test_eight_queens(self): 58 | placements = eight_queens() 59 | self.assertEqual(len(placements), 92) 60 | self.assertEqual(show(placements[0]), """ 61 | +-----------------+ 62 | | Q | 63 | | Q | 64 | | Q | 65 | | Q | 66 | | Q | 67 | | Q | 68 | | Q | 69 | | Q | 70 | +-----------------+ 71 | """) 72 | 73 | if __name__ == "__main__": 74 | unittest.main() 75 | 76 | -------------------------------------------------------------------------------- /ch-08-recursion-and-dynamic-programming/13-stacks-of-boxes.py: -------------------------------------------------------------------------------- 1 | # Stack boxes as high as possible. 2 | 3 | def stack_boxes(boxes): 4 | sorted_boxes = sorted(boxes, lambda a, b: a.height > b.height) 5 | return stack_more_boxes(sorted_boxes, None, 0) 6 | 7 | def stack_more_boxes(boxes, base, index): 8 | if index >= len(boxes): 9 | return 0 10 | without_box_height = stack_more_boxes(boxes, base, index + 1) 11 | box = boxes[index] 12 | if (not base) or box.fits_on(base): 13 | with_box_height = box.height + stack_more_boxes(boxes, box, index + 1) 14 | if with_box_height > without_box_height: 15 | return with_box_height 16 | return without_box_height 17 | 18 | class Box(object): 19 | def __init__(self, height, width, depth): 20 | self.height, self.width, self.depth = height, width, depth 21 | 22 | def fits_on(self, base): 23 | return base.height > self.height and \ 24 | base.width > self.width and \ 25 | base.depth > self.depth 26 | 27 | import unittest 28 | 29 | class Test(unittest.TestCase): 30 | def test_stack_boxes(self): 31 | boxes = [Box(100, 100, 100)] 32 | self.assertEqual(stack_boxes(boxes), 100) 33 | boxes.append(Box(25, 25, 25)) 34 | self.assertEqual(stack_boxes(boxes), 125) 35 | boxes.append(Box(20, 5, 30)) 36 | boxes.append(Box(17, 4, 28)) 37 | self.assertEqual(stack_boxes(boxes), 137) 38 | 39 | if __name__ == "__main__": 40 | unittest.main() 41 | 42 | -------------------------------------------------------------------------------- /ch-08-recursion-and-dynamic-programming/14-boolean-evaluation.py: -------------------------------------------------------------------------------- 1 | # Return the number of ways that an expression can be parenthesized and 2 | # achieve a given truth value 3 | 4 | def count_eval(expr, value, memo=None): 5 | if len(expr) % 2 == 0: 6 | return Exception("Malformed expression.") 7 | if len(expr) == 1: 8 | return int((expr == "0") ^ value) 9 | if memo is None: 10 | memo = {} 11 | elif expr in memo: 12 | counts = memo[expr] 13 | return counts[int(value)] 14 | true_count = 0 15 | for opix in xrange(1, len(expr) - 1, 2): 16 | left, op, right = expr[:opix], expr[opix], expr[(opix+1):] 17 | if op == '&': 18 | true_count += count_eval(left,True, memo) * count_eval(right,True, memo) 19 | elif op == '|': 20 | true_count += count_eval(left,True, memo) * count_eval(right,True, memo) 21 | true_count += count_eval(left,False,memo) * count_eval(right,True, memo) 22 | true_count += count_eval(left,True, memo) * count_eval(right,False,memo) 23 | elif op == '^': 24 | true_count += count_eval(left,True, memo) * count_eval(right,False,memo) 25 | true_count += count_eval(left,False,memo) * count_eval(right,True, memo) 26 | else: 27 | return Exception('Unknown operation.') 28 | total_count = catalan_number((len(expr) - 1) / 2) 29 | false_count = total_count - true_count 30 | counts = (false_count, true_count) 31 | memo[expr] = counts 32 | return counts[int(value)] 33 | 34 | def catalan_number(n): 35 | number = 1 36 | for i in xrange(n + 1, 2*n + 1): 37 | number *= i 38 | for i in xrange(1, n + 2): 39 | number /= i 40 | return number 41 | 42 | import unittest 43 | 44 | class Test(unittest.TestCase): 45 | def test_count_eval(self): 46 | self.assertEqual(count_eval("1", True), 1) 47 | self.assertEqual(count_eval("0", True), 0) 48 | self.assertEqual(count_eval("0", False), 1) 49 | self.assertEqual(count_eval("1&1", True), 1) 50 | self.assertEqual(count_eval("1|0", False), 0) 51 | self.assertEqual(count_eval("1^0", True), 1) 52 | self.assertEqual(count_eval("1&0&1", True), 0) 53 | self.assertEqual(count_eval("1|1^0", True), 2) 54 | self.assertEqual(count_eval("1^0|0|1", False), 2) 55 | self.assertEqual(count_eval("1^0|0|1", True), 3) 56 | self.assertEqual(count_eval("0&0&0&1^1|0", True), 10) 57 | 58 | def test_catalan_number(self): 59 | self.assertEqual(catalan_number(0), 1) 60 | self.assertEqual(catalan_number(1), 1) 61 | self.assertEqual(catalan_number(2), 2) 62 | self.assertEqual(catalan_number(3), 5) 63 | self.assertEqual(catalan_number(4), 14) 64 | self.assertEqual(catalan_number(5), 42) 65 | 66 | if __name__ == "__main__": 67 | unittest.main() 68 | 69 | -------------------------------------------------------------------------------- /ch-09-system-design-and-scalability/.gitignore: -------------------------------------------------------------------------------- 1 | /env 2 | -------------------------------------------------------------------------------- /ch-09-system-design-and-scalability/01-stock-data.py: -------------------------------------------------------------------------------- 1 | # Design a system to deliver stock data. 2 | 3 | from wsgiref.simple_server import make_server 4 | from pyramid.config import Configurator 5 | from pyramid.view import view_config 6 | 7 | class Database(object): 8 | def __init__(self): 9 | self.stock_quotes = { 10 | "GOOG": {"open": 908.1, "close": 921.0, "low": 905.3, "high": 921.7}, 11 | "AMZN": {"open": 812.7, "close": 817.1, "low": 812.5, "high": 818.5}, 12 | "BIDU": {"open": 173.7, "close": 180.2, "low": 173.5, "high": 181.2}, 13 | "INTC": {"open": 38.3, "close": 41.1, "low": 38.0, "high": 41.2}, 14 | "NVDA": {"open": 112.8, "close": 116.8, "low": 111.9, "high": 117.0}, 15 | "AAPL": {"open": 144.5, "close": 146.8, "low": 144.0, "high": 147.0}, 16 | "SNAP": {"open": 24.9, "close": 27.0, "low": 24.0, "high": 25.2}, 17 | "TWTR": {"open": 16.6, "close": 17.7, "low": 16.4, "high": 17.8}, 18 | "IBM": {"open": 168.6, "close": 171.1, "low": 168.4, "high": 172.1}, 19 | "FB": {"open": 140.2, "close": 148.3, "low": 140.2, "high": 150.3}, 20 | "GE": {"open": 32.2, "close": 31.4, "low": 32.0, "high": 32.8}} 21 | 22 | db = Database() 23 | 24 | @view_config(route_name='stock_quotes', renderer='json') 25 | def stock_quotes(request): 26 | if 'tickers' in request.params: 27 | tickers = request.params['tickers'].split(',') 28 | quotes = {} 29 | for ticker in tickers: 30 | quotes[ticker] = db.stock_quotes[ticker] 31 | return quotes 32 | else: 33 | return db.stock_quotes 34 | 35 | if __name__ == '__main__': 36 | config = Configurator() 37 | config.add_route('stock_quotes', '/stock_quotes') 38 | config.scan() 39 | app = config.make_wsgi_app() 40 | server = make_server('0.0.0.0', 8080, app) 41 | server.serve_forever() 42 | 43 | -------------------------------------------------------------------------------- /ch-10-sorting-and-searching/01-sorted-merge.py: -------------------------------------------------------------------------------- 1 | # Merge array b into array a given that array a contains len(b) extra space at 2 | # the end. 3 | 4 | def sorted_merge(a, b): 5 | bix = len(b) - 1 6 | aix = len(a) - len(b) - 1 7 | while aix >= 0 and bix >= 0: 8 | if a[aix] > b[bix]: 9 | a[aix + bix + 1] = a[aix] 10 | aix -= 1 11 | else: 12 | a[aix + bix + 1] = b[bix] 13 | bix -= 1 14 | while bix >= 0: 15 | a[bix] = b[bix] 16 | bix -= 1 17 | 18 | import unittest 19 | 20 | class Test(unittest.TestCase): 21 | def test_sorted_merge(self): 22 | a = [33, 44, 55, 66, 88, 99, None, None, None] 23 | b = [11, 22, 77] 24 | sorted_merge(a, b) 25 | self.assertEqual(a, [11, 22, 33, 44, 55, 66, 77, 88, 99]) 26 | a = [11, 22, 55, 66, 88, None, None, None] 27 | b = [33, 44, 99] 28 | sorted_merge(a, b) 29 | self.assertEqual(a, [11, 22, 33, 44, 55, 66, 88, 99]) 30 | 31 | if __name__ == "__main__": 32 | unittest.main() 33 | 34 | -------------------------------------------------------------------------------- /ch-10-sorting-and-searching/02-group-anagrams.py: -------------------------------------------------------------------------------- 1 | # Group string anagrams together. 2 | 3 | def group_anagrams(strings): 4 | pairs = [(s, sorted(s)) for s in strings] 5 | pairs.sort(key=lambda p: p[1]) 6 | return [p[0] for p in pairs] 7 | 8 | import unittest 9 | 10 | class Test(unittest.TestCase): 11 | def test_group_anagrams(self): 12 | strings = ["cat", "bat", "rat", "arts", "tab", "tar", "car", "star"] 13 | self.assertEqual(group_anagrams(strings), 14 | ["bat", "tab", "car", "cat", "arts", "star", "rat", "tar"]) 15 | 16 | if __name__ == "__main__": 17 | unittest.main() 18 | 19 | -------------------------------------------------------------------------------- /ch-10-sorting-and-searching/03-search-in-rotated-array.py: -------------------------------------------------------------------------------- 1 | # Search for an item in a rotated array. 2 | 3 | def rotated_search(array, item, leftix=0, rightix=None): 4 | if rightix is None: 5 | rightix = len(array) 6 | if rightix <= leftix: 7 | return None 8 | middleix = (rightix + leftix) / 2 9 | left, middle = array[leftix], array[middleix] 10 | if item == middle: 11 | return middleix 12 | if item == left: 13 | return leftix 14 | if left > middle: 15 | if middle < item and item < left: 16 | return rotated_search(array, item, middleix+1, rightix) 17 | else: 18 | return rotated_search(array, item, leftix+1, middleix) 19 | elif left < item and item < middle: 20 | return rotated_search(array, item, leftix+1, middleix) 21 | else: 22 | return rotated_search(array, item, middleix+1, rightix) 23 | 24 | import unittest 25 | 26 | class Test(unittest.TestCase): 27 | def test_rotated_search(self): 28 | array = [55, 60, 65, 70, 75, 80, 85, 90, 95, 15, 20, 25, 30, 35, 40, 45] 29 | self.assertEqual(rotated_search(array, 55), 0) 30 | self.assertEqual(rotated_search(array, 60), 1) 31 | self.assertEqual(rotated_search(array, 90), 7) 32 | self.assertEqual(rotated_search(array, 95), 8) 33 | self.assertEqual(rotated_search(array, 15), 9) 34 | self.assertEqual(rotated_search(array, 30), 12) 35 | self.assertEqual(rotated_search(array, 45), 15) 36 | 37 | if __name__ == "__main__": 38 | unittest.main() 39 | 40 | -------------------------------------------------------------------------------- /ch-10-sorting-and-searching/04-sorted-search-no-size.py: -------------------------------------------------------------------------------- 1 | # Find an item in a sorted list-like object without knowing its length. 2 | 3 | def search_listy(listy, item, leftix=0, rightix=None): 4 | if rightix is None: 5 | rightix = 4 6 | right = listy[rightix] 7 | while right < item and right != -1: 8 | rightix *= 2 9 | right = listy[rightix] 10 | if right == item: 11 | return rightix 12 | if leftix == rightix: 13 | return None 14 | middleix = (leftix + rightix) / 2 15 | middle = listy[middleix] 16 | if middle == item: 17 | return middleix 18 | if middle == -1 or middle > item: 19 | return search_listy(listy, item, leftix, middleix) 20 | else: 21 | return search_listy(listy, item, middleix+1, rightix) 22 | 23 | class Listy(object): 24 | def __init__(self, array): 25 | self.array = array 26 | 27 | def __getitem__(self, ix): 28 | if ix < len(self.array): 29 | return self.array[ix] 30 | else: 31 | return -1 32 | 33 | import unittest 34 | 35 | class Test(unittest.TestCase): 36 | def test_search_listy(self): 37 | listy = Listy([-22, -11, 11, 22, 33, 44, 55, 66, 77, 88, 99]) 38 | self.assertEqual(search_listy(listy, 25), None) 39 | self.assertEqual(search_listy(listy, -22), 0) 40 | self.assertEqual(search_listy(listy, 22), 3) 41 | self.assertEqual(search_listy(listy, 66), 7) 42 | self.assertEqual(search_listy(listy, 77), 8) 43 | self.assertEqual(search_listy(listy, 99), 10) 44 | self.assertEqual(search_listy(listy, 100), None) 45 | 46 | if __name__ == "__main__": 47 | unittest.main() 48 | 49 | -------------------------------------------------------------------------------- /ch-10-sorting-and-searching/05-sparse-search.py: -------------------------------------------------------------------------------- 1 | # Search a sorted sparse array of positive integers. 2 | 3 | def sparse_search(array, item): 4 | if item < 1: 5 | return None 6 | return sparse_search_bounds(array, item, 0, len(array)) 7 | 8 | def sparse_search_bounds(array, item, left_ix, right_ix): 9 | if left_ix == right_ix: 10 | return None 11 | middle_ix = (left_ix + right_ix) / 2 12 | next_ix = middle_ix 13 | next = array[next_ix] 14 | while not next: 15 | next_ix += 1 16 | if next_ix == len(array): 17 | return sparse_search_bounds(array, item, left_ix, middle_ix) 18 | next = array[next_ix] 19 | if next == item: 20 | return next_ix 21 | if next > item: 22 | return sparse_search_bounds(array, item, left_ix, middle_ix) 23 | else: 24 | return sparse_search_bounds(array, item, next_ix+1, right_ix) 25 | 26 | import unittest 27 | 28 | class Test(unittest.TestCase): 29 | def test_sparse_search(self): 30 | array = [0, 0, 7, 0, 0, 0, 0, 0, 19, 0, 0, 0, 0, 37, 40, 0, 0, 0] 31 | self.assertEqual(sparse_search(array, 0), None) 32 | self.assertEqual(sparse_search(array, 7), 2) 33 | self.assertEqual(sparse_search(array, 19), 8) 34 | self.assertEqual(sparse_search(array, 37), 13) 35 | self.assertEqual(sparse_search(array, 40), 14) 36 | array = [0, 12, 0, 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] 37 | self.assertEqual(sparse_search(array, 12), 1) 38 | self.assertEqual(sparse_search(array, 18), 3) 39 | 40 | if __name__ == "__main__": 41 | unittest.main() 42 | 43 | -------------------------------------------------------------------------------- /ch-10-sorting-and-searching/06-sort-big-file.py: -------------------------------------------------------------------------------- 1 | # Sort a big file. 2 | 3 | import os 4 | 5 | ONE_GIGABYTE = 64 6 | 7 | def sort_big_file(input_filename, output_filename): 8 | in_file = open(input_filename, "r") 9 | temp_files = [open("temp-0.txt", "w+")] 10 | index = 0 11 | next_line = in_file.readline() 12 | temp_size = 0 13 | while next_line: 14 | if temp_size + len(next_line) > ONE_GIGABYTE: 15 | temp_name = "temp-" + str(len(temp_files) + 1) + ".txt" 16 | temp_files.append(open(temp_name, "w+")) 17 | temp_size = 0 18 | temp_size += len(next_line) 19 | temp_files[-1].write(next_line) 20 | next_line = in_file.readline() 21 | min_heap = MinHeap() 22 | sorted_files = [] 23 | for f in temp_files: 24 | f.close() 25 | sort_in_memory(f.name) 26 | sorted_file = open(f.name, "r") 27 | min_heap.add((sorted_file.readline(), sorted_file)) 28 | out_file = open(output_filename, "w") 29 | next = min_heap.remove() 30 | while next: 31 | (next_line, f) = next 32 | out_file.write(next_line) 33 | line = f.readline() 34 | if line: 35 | min_heap.add((line, f)) 36 | next = min_heap.remove() 37 | for f in temp_files: 38 | f.close() 39 | os.remove(f.name) 40 | 41 | def sort_in_memory(small_filename): 42 | small_file = open(small_filename, "r") 43 | lines = small_file.readlines() 44 | small_file.close() 45 | small_file = open(small_filename, "w") 46 | lines.sort() 47 | small_file.writelines(lines) 48 | small_file.close() 49 | 50 | class MinHeap(object): 51 | def __init__(self): 52 | self.data = [None] 53 | 54 | def add(self, item): 55 | self.data.append(item) 56 | ix = len(self.data) - 1 57 | while ix and (item < self.data[ix/2]): 58 | self.data[ix], self.data[ix/2] = self.data[ix/2], item 59 | ix /= 2 60 | 61 | def remove(self): 62 | if len(self.data) == 1: 63 | return None 64 | if len(self.data) == 2: 65 | return self.data.pop() 66 | minimum = self.data[1] 67 | self.data[1] = self.data.pop() 68 | ix = 1 69 | while 2*ix < len(self.data): 70 | child_ix = 2*ix 71 | if 2*ix+1 < len(self.data) and self.data[child_ix] > self.data[2*ix+1]: 72 | child_ix = 2*ix + 1 73 | if self.data[child_ix] >= self.data[ix]: 74 | break 75 | self.data[child_ix], self.data[ix] = self.data[ix], self.data[child_ix] 76 | ix = child_ix 77 | return minimum 78 | 79 | import unittest 80 | 81 | class Test(unittest.TestCase): 82 | def test_min_heap(self): 83 | mh = MinHeap() 84 | mh.add(27) 85 | mh.add(22) 86 | mh.add(20) 87 | mh.add(31) 88 | mh.add(35) 89 | mh.add(19) 90 | mh.add(30) 91 | mh.add(18) 92 | self.assertEqual(mh.remove(), 18) 93 | self.assertEqual(mh.remove(), 19) 94 | self.assertEqual(mh.remove(), 20) 95 | self.assertEqual(mh.remove(), 22) 96 | self.assertEqual(mh.remove(), 27) 97 | self.assertEqual(mh.remove(), 30) 98 | self.assertEqual(mh.remove(), 31) 99 | self.assertEqual(mh.remove(), 35) 100 | 101 | def test_sort_big_file(self): 102 | big_file = open("big-input-file.txt", "w") 103 | big_file.write("""Haskell 104 | Golang 105 | Erlang 106 | Ruby 107 | Chess 108 | Arimaa 109 | AlphaGo 110 | Self-driving cars 111 | Convolutional neural networks 112 | TensorFlow 113 | Fourier transform 114 | Coq 115 | CFDG 116 | Processing 117 | Art festival 118 | Scrabble 119 | Bananagrams 120 | Frisbee 121 | Basketball 122 | Soccer 123 | Tennis 124 | Quantum computers 125 | Encryption 126 | Topology 127 | Homotopy 128 | Combinatorics 129 | Generating functions 130 | Matrix completion 131 | """) 132 | ordered = """AlphaGo 133 | Arimaa 134 | Art festival 135 | Bananagrams 136 | Basketball 137 | CFDG 138 | Chess 139 | Combinatorics 140 | Convolutional neural networks 141 | Coq 142 | Encryption 143 | Erlang 144 | Fourier transform 145 | Frisbee 146 | Generating functions 147 | Golang 148 | Haskell 149 | Homotopy 150 | Matrix completion 151 | Processing 152 | Quantum computers 153 | Ruby 154 | Scrabble 155 | Self-driving cars 156 | Soccer 157 | Tennis 158 | TensorFlow 159 | Topology 160 | """ 161 | big_file.close() 162 | sort_big_file("big-input-file.txt", "big-output-file.txt") 163 | output_file = open("big-output-file.txt", "r") 164 | self.assertEqual("".join(output_file.readlines()), ordered) 165 | output_file.close() 166 | os.remove("big-output-file.txt") 167 | os.remove("big-input-file.txt") 168 | 169 | if __name__ == "__main__": 170 | unittest.main() 171 | 172 | -------------------------------------------------------------------------------- /ch-10-sorting-and-searching/07-missing-int.py: -------------------------------------------------------------------------------- 1 | # Find an integer that is not contained in a list of 4 billion integers. 2 | 3 | import random 4 | 5 | def missing_int(integer_list): 6 | guess = random.randint(0, (1 << 64) - 1) 7 | for integer in integer_list: 8 | if integer == guess: 9 | return missing_int(integer_list) 10 | return guess 11 | 12 | import unittest 13 | 14 | class Test(unittest.TestCase): 15 | def test_missing_int(self): 16 | integer_list = [400, 438, 998098093, 171, 10003] 17 | integer = missing_int(integer_list) 18 | self.assertFalse( integer in integer_list) 19 | 20 | if __name__ == "__main__": 21 | unittest.main() 22 | 23 | -------------------------------------------------------------------------------- /ch-10-sorting-and-searching/08-find-duplicates.py: -------------------------------------------------------------------------------- 1 | # Find the duplicates in an array that contains numbers between 0 and 32000. 2 | 3 | def find_duplicates(array): 4 | bv = [0] * 512 5 | duplicates = [] 6 | for n in array: 7 | bit = 1 << (n % 64) 8 | if bv[n / 64] & bit: 9 | duplicates.append(n) 10 | else: 11 | bv[n / 64] |= bit 12 | return duplicates 13 | 14 | import unittest 15 | 16 | class Test(unittest.TestCase): 17 | def test_find_duplicates(self): 18 | array = [1, 2, 3, 4, 55, 20000, 20001, 20002, 20003, 17, 18, 19, 20, 22, 23, 19 | 7, 2, 3, 3, 55, 20002, 20004, 20005, 20006, 16, 18, 22, 24, 25, 26] 20 | self.assertEqual(find_duplicates(array), [2, 3, 3, 55, 20002, 18, 22]) 21 | 22 | if __name__ == "__main__": 23 | unittest.main() 24 | 25 | -------------------------------------------------------------------------------- /ch-10-sorting-and-searching/09-sorted-matrix-search.py: -------------------------------------------------------------------------------- 1 | # Search a sorted martix. 2 | 3 | def sorted_matrix_search(mat, item): 4 | if len(mat) == 0: 5 | return None 6 | return sorted_matrix_search_bounds(mat, item, 0, len(mat[0]), 0, len(mat)) 7 | 8 | def sorted_matrix_search_bounds(mat, item, x1, x2, y1, y2): 9 | if x1 == x2 or y1 == y2: 10 | return None 11 | row, col = (y1 + y2)/2, (x1 + x2)/2 12 | middle = mat[row][col] 13 | if middle == item: 14 | return (row, col) 15 | if middle > item: 16 | found = sorted_matrix_search_bounds(mat, item, x1, col, y1, row) or \ 17 | sorted_matrix_search_bounds(mat, item, col, col+1, y1, row) or \ 18 | sorted_matrix_search_bounds(mat, item, x1, col, row, row+1) 19 | if found: 20 | return found 21 | else: 22 | found = sorted_matrix_search_bounds(mat, item, col+1, x2, row+1, y2) or \ 23 | sorted_matrix_search_bounds(mat, item, col, col+1, row+1, y2) or \ 24 | sorted_matrix_search_bounds(mat, item, col+1, x2, row, row+1) 25 | if found: 26 | return found 27 | return sorted_matrix_search_bounds(mat, item, x1, col, row+1, y2) or \ 28 | sorted_matrix_search_bounds(mat, item, col+1, x2, y1, row) 29 | 30 | import unittest 31 | 32 | class Test(unittest.TestCase): 33 | def test_sorted_matrix_search(self): 34 | mat = [[1, 2, 3, 4, 5, 6, 7, 8, 9], 35 | [5, 10, 15, 20, 25, 30, 35, 40, 45], 36 | [10, 20, 30, 40, 50, 60, 70, 80, 90], 37 | [13, 23, 33, 43, 53, 63, 73, 83, 93], 38 | [14, 24, 34, 44, 54, 64, 74, 84, 94], 39 | [15, 25, 35, 45, 55, 65, 75, 85, 95], 40 | [16, 26, 36, 46, 56, 66, 77, 88, 99]] 41 | self.assertEqual(sorted_matrix_search(mat, 10), (1,1)) 42 | self.assertEqual(sorted_matrix_search(mat, 13), (3,0)) 43 | self.assertEqual(sorted_matrix_search(mat, 14), (4,0)) 44 | self.assertEqual(sorted_matrix_search(mat, 16), (6,0)) 45 | self.assertEqual(sorted_matrix_search(mat, 56), (6,4)) 46 | self.assertEqual(sorted_matrix_search(mat, 65), (5,5)) 47 | self.assertEqual(sorted_matrix_search(mat, 74), (4,6)) 48 | self.assertEqual(sorted_matrix_search(mat, 99), (6,8)) 49 | 50 | if __name__ == "__main__": 51 | unittest.main() 52 | 53 | -------------------------------------------------------------------------------- /ch-10-sorting-and-searching/10-rank-from-stream.py: -------------------------------------------------------------------------------- 1 | # Record the number of elements lower than any given elements in a stream. 2 | 3 | class RankNode(object): 4 | def __init__(self, data=None): 5 | self.data = data 6 | self.left, self.right = None, None 7 | if self.data is None: 8 | self.count = 0 9 | else: 10 | self.count = 1 11 | self.left_count = 0 12 | 13 | def track(self, item): 14 | if self.data is None: 15 | self.data = item 16 | self.count = 1 17 | elif self.data == item: 18 | self.count += 1 19 | elif self.data > item: 20 | if self.left: 21 | self.left.track(item) 22 | else: 23 | self.left = RankNode(item) 24 | self.left_count += 1 25 | else: 26 | if self.right: 27 | self.right.track(item) 28 | else: 29 | self.right = RankNode(item) 30 | 31 | def get_rank(self, item): 32 | if self.data is None: 33 | return 0 34 | elif self.data < item: 35 | if self.right: 36 | return self.count + self.left_count + self.right.get_rank(item) 37 | else: 38 | return self.count + self.left_count 39 | elif self.data > item: 40 | if self.left: 41 | return self.left.get_rank(item) 42 | else: 43 | return 0 44 | else: 45 | return self.left_count 46 | 47 | import unittest 48 | 49 | class Test(unittest.TestCase): 50 | def test_rank_tree(self): 51 | rt = RankNode() 52 | self.assertEqual(rt.get_rank(20), 0) 53 | rt.track(11) 54 | self.assertEqual(rt.get_rank(20), 1) 55 | self.assertEqual(rt.get_rank(10), 0) 56 | rt.track(30) 57 | rt.track(7) 58 | rt.track(7) 59 | rt.track(10) 60 | rt.track(15) 61 | rt.track(7) 62 | rt.track(3) 63 | rt.track(22) 64 | self.assertEqual(rt.get_rank(20), 7) 65 | self.assertEqual(rt.get_rank(7), 1) 66 | self.assertEqual(rt.get_rank(8), 4) 67 | self.assertEqual(rt.get_rank(14), 6) 68 | 69 | if __name__ == "__main__": 70 | unittest.main() 71 | 72 | -------------------------------------------------------------------------------- /ch-10-sorting-and-searching/11-peaks-and-valleys.py: -------------------------------------------------------------------------------- 1 | # Reorder an array into peaks and valleys. 2 | 3 | def peaks_and_valleys(array): 4 | if len(array) < 3: 5 | return 6 | for ix in xrange(len(array) - 1): 7 | # If this is a peak: 8 | if ix % 2: 9 | if array[ix] < array[ix + 1]: 10 | array[ix], array[ix + 1] = array[ix + 1], array[ix] 11 | # Otherwise, if this is a valley: 12 | else: 13 | if array[ix] > array[ix + 1]: 14 | array[ix], array[ix + 1] = array[ix + 1], array[ix] 15 | 16 | 17 | import unittest 18 | 19 | class Test(unittest.TestCase): 20 | def test_peaks_and_valleys(self): 21 | a = [12, 6, 3, 1, 0, 14, 13, 20, 22, 10] 22 | peaks_and_valleys(a) 23 | self.assertEqual(a, [6, 12, 1, 3, 0, 14, 13, 22, 10, 20]) 24 | b = [34, 55, 60, 65, 70, 75, 85, 10, 5, 16] 25 | peaks_and_valleys(b) 26 | self.assertEqual(b, [34, 60, 55, 70, 65, 85, 10, 75, 5, 16]) 27 | 28 | if __name__ == "__main__": 29 | unittest.main() 30 | 31 | -------------------------------------------------------------------------------- /ch-11-testing/01-mistake.py: -------------------------------------------------------------------------------- 1 | # What was the mistake? 2 | 3 | def following_code(): 4 | i = 100 5 | while i >= 0: 6 | print("{}".format(i)) 7 | i -= 1 8 | 9 | if __name__ == "__main__": 10 | following_code() 11 | 12 | -------------------------------------------------------------------------------- /ch-11-testing/02-random-crashes.py: -------------------------------------------------------------------------------- 1 | # A program never crashes in the same place, but it is supposed to crash 2 | # uniformly randomly. Test that a sequence behaves randomly. 3 | 4 | # Note that the randomness test implemented here is very weak. 5 | 6 | # TODO Implement the algorithm in this paper instead: 7 | # ftp://ftp.inf.ethz.ch/pub/crypto/publications/Maurer92a.pdf 8 | 9 | import math 10 | 11 | def how_random(sequence): 12 | bit_counts = [0] * 128 13 | for element in sequence: 14 | for bit_ix in xrange(64): 15 | bit = 1 << bit_ix 16 | if element & bit: 17 | bit_counts[bit_ix] += 1 18 | else: 19 | bit_counts[bit_ix + 64] += 1 20 | total = sum(bit_counts) 21 | average = total / 128.0 22 | square_divergence = sum([abs(c - average) for c in bit_counts]) 23 | if not square_divergence: 24 | return 1 25 | return 1 / math.sqrt(square_divergence) 26 | 27 | import random 28 | import unittest 29 | 30 | random.seed(0) 31 | 32 | class Test(unittest.TestCase): 33 | def test_randomness(self): 34 | sequence0 = [i % 2 for i in xrange(10000)] 35 | randomness0 = how_random(sequence0) 36 | self.assertTrue(randomness0 > 0.0 and randomness0 < 0.1) 37 | 38 | sequence1 = [i % 1000 for i in xrange(10000)] 39 | randomness1 = how_random(sequence1) 40 | self.assertTrue(randomness0 < randomness1) 41 | 42 | sequence2 = [(i * 6007) % 50000 for i in xrange(10000)] 43 | randomness2 = how_random(sequence2) 44 | self.assertTrue(randomness1 < randomness2) 45 | 46 | sequence3 = [random.randint(1, (1 << 64) - 1) for _ in xrange(10000)] 47 | randomness3 = how_random(sequence3) 48 | self.assertTrue(randomness2 < randomness3) 49 | 50 | if __name__ == "__main__": 51 | unittest.main() 52 | 53 | -------------------------------------------------------------------------------- /ch-11-testing/03-chess-test.py: -------------------------------------------------------------------------------- 1 | # Test chess movement. 2 | 3 | CARDINAL = [ 4 | lambda b: (b << 1) & 0xFEFEFEFEFEFEFEFE, 5 | lambda b: (b >> 1) & 0x7F7F7F7F7F7F7F7F, 6 | lambda b: (b << 8) & 0xFFFFFFFFFFFFFFFF, 7 | lambda b: (b >> 8)] 8 | DIAGONAL = [ 9 | lambda b: (b << 7) & 0x7F7F7F7F7F7F7F7F, 10 | lambda b: (b >> 7) & 0xFEFEFEFEFEFEFEFE, 11 | lambda b: (b << 9) & 0xFEFEFEFEFEFEFEFE, 12 | lambda b: (b >> 9) & 0x7F7F7F7F7F7F7F7F] 13 | KNIGHTAL = [ 14 | lambda b: (b << 10) & 0xFCFCFCFCFCFCFC00, 15 | lambda b: (b >> 10) & 0x3F3F3F3F3F3F3F, 16 | lambda b: (b << 15) & 0x7F7F7F7F7F7F0000, 17 | lambda b: (b >> 15) & 0xFEFEFEFEFEFE, 18 | lambda b: (b << 17) & 0xFEFEFEFEFEFE0000, 19 | lambda b: (b >> 17) & 0x7F7F7F7F7F7F, 20 | lambda b: (b << 6) & 0x3F3F3F3F3F3F3F00, 21 | lambda b: (b >> 6) & 0xFCFCFCFCFCFCFC] 22 | 23 | EMPTY = 0 24 | W_PAWN = 1 25 | W_KNIGHT = 2 26 | W_BISHOP = 3 27 | W_ROOK = 4 28 | W_QUEEN = 5 29 | W_KING = 6 30 | B_PAWN = 7 31 | B_KNIGHT = 8 32 | B_BISHOP = 9 33 | B_ROOK = 10 34 | B_QUEEN = 11 35 | B_KING = 12 36 | 37 | PIECE_CHARS = " pnbrqkPNBRQK" 38 | 39 | def neighbors(bit): 40 | neighbors = 0 41 | for step in CARDINAL: 42 | neighbors |= step(bit) 43 | for step in DIAGONAL: 44 | neighbors |= step(bit) 45 | return neighbors 46 | 47 | class ChessBoard(object): 48 | def __init__(self, string=None, side=0, bits=None): 49 | if string is None: 50 | self.side = side 51 | if bits is None: 52 | self.bits = [0xFFFFFFFF0000, 53 | 0xFF000000000000, 0x4200000000000000, 0x2400000000000000, 54 | 0x8100000000000000, 0x800000000000000, 0x1000000000000000, 55 | 0xFF00, 0x42, 0x24, 0x81, 0x8, 0x10] 56 | else: 57 | self.bits = bits 58 | else: 59 | string = string.strip() 60 | self.side = int(string[0] == 'B') 61 | self.bits = [0xFFFFFFFFFFFFFFFF, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] 62 | lines = string.split("\n") 63 | for row, line in enumerate(lines[2:10]): 64 | line = line.strip() 65 | for col in xrange(0, 8): 66 | bit = 1 << (row * 8 + col) 67 | piece_char = line[3 + col * 2] 68 | piece = PIECE_CHARS.index(piece_char) 69 | if piece == -1: 70 | raise Exception("Bad piece character {}.".format(piece_char)) 71 | self.bits[EMPTY] ^= bit 72 | self.bits[piece] ^= bit 73 | 74 | def legal_moves(self): 75 | moves = [] 76 | offset = self.side * 6 77 | # Pawns 78 | friendly_pawn = W_PAWN + offset 79 | pawns = self.bits[friendly_pawn] 80 | if self.side: 81 | pawn_step = CARDINAL[2] 82 | pawn_start_row = 0xFF00 83 | else: 84 | pawn_step = CARDINAL[3] 85 | pawn_start_row = 0xFF000000000000 86 | # TODO En passant. 87 | while pawns: 88 | pawn = pawns & -pawns 89 | pawns ^= pawn 90 | front = pawn_step(pawn) 91 | if front & self.bits[EMPTY]: 92 | self.try_move(moves, friendly_pawn, pawn, front) 93 | if pawn & pawn_start_row: 94 | double = pawn_step(front) 95 | if double & self.bits[EMPTY]: 96 | self.try_move(moves, friendly_pawn, pawn, double) 97 | right = CARDINAL[0](front) 98 | if right & ~self.bits[EMPTY]: 99 | self.try_move(moves, friendly_pawn, pawn, right) 100 | left = CARDINAL[1](front) 101 | if left & ~self.bits[EMPTY]: 102 | self.try_move(moves, friendly_pawn, pawn, left) 103 | # Knights 104 | friendly_knight = W_KNIGHT + offset 105 | knights = self.bits[friendly_knight] 106 | while knights: 107 | knight = knights & -knights 108 | knights ^= knight 109 | for knight_move in KNIGHTAL: 110 | self.try_move(moves, friendly_knight, knight, knight_move(knight)) 111 | # Bishops 112 | friendly_bishop = W_BISHOP + offset 113 | bishops = self.bits[friendly_bishop] 114 | while bishops: 115 | bishop = bishops & -bishops 116 | bishops ^= bishop 117 | for diagonal_step in DIAGONAL: 118 | move = diagonal_step(bishop) 119 | while move & self.bits[EMPTY]: 120 | self.try_move(moves, friendly_bishop, bishop, move) 121 | move = diagonal_step(move) 122 | self.try_move(moves, friendly_bishop, bishop, move) 123 | # Rooks 124 | friendly_rook = W_ROOK + offset 125 | rooks = self.bits[friendly_rook] 126 | while rooks: 127 | rook = rooks & -rooks 128 | rooks ^= rook 129 | for cardinal_step in CARDINAL: 130 | move = cardinal_step(rook) 131 | while move & self.bits[EMPTY]: 132 | self.try_move(moves, friendly_rook, rook, move) 133 | move = cardinal_step(move) 134 | self.try_move(moves, friendly_rook, rook, move) 135 | # Queens 136 | friendly_queen = W_QUEEN + offset 137 | queens = self.bits[friendly_queen] 138 | while queens: 139 | queen = queens & -queens 140 | queens ^= queen 141 | for diagonal_step in DIAGONAL: 142 | move = diagonal_step(queen) 143 | while move & self.bits[EMPTY]: 144 | self.try_move(moves, friendly_queen, queen, move) 145 | move = diagonal_step(move) 146 | self.try_move(moves, friendly_queen, queen, move) 147 | for cardinal_step in CARDINAL: 148 | move = cardinal_step(queen) 149 | while move & self.bits[EMPTY]: 150 | self.try_move(moves, friendly_queen, queen, move) 151 | move = cardinal_step(move) 152 | self.try_move(moves, friendly_queen, queen, move) 153 | # King 154 | friendly_king = W_KING + offset 155 | king = self.bits[friendly_king] 156 | # TODO Castling 157 | for diagonal_step in DIAGONAL: 158 | self.try_move(moves, friendly_king, king, diagonal_step(king)) 159 | for cardinal_step in CARDINAL: 160 | self.try_move(moves, friendly_king, king, cardinal_step(king)) 161 | return moves 162 | 163 | def try_move(self, moves, piece, from_bit, to_bit): 164 | # Make sure that a friendly piece is not being stepped on. 165 | to_bit_piece = EMPTY 166 | enemy = (self.side ^ 1) * 6 167 | if not (to_bit & self.bits[EMPTY]): 168 | friendly_pawn = W_PAWN + self.side * 6 169 | if to_bit & self.bits[friendly_pawn]: 170 | return 171 | enemy_pawn = W_PAWN + enemy 172 | for enemy_piece in xrange(enemy_pawn, enemy_pawn + 5): 173 | if to_bit & self.bits[enemy_piece]: 174 | to_bit_piece = enemy_piece 175 | break 176 | if to_bit_piece == EMPTY: 177 | return 178 | # Try making the move. 179 | # TODO Handle promotions. 180 | self.bits[piece] ^= from_bit | to_bit 181 | self.bits[EMPTY] ^= from_bit 182 | self.bits[to_bit_piece] ^= to_bit 183 | # Check for check. 184 | legal = not self.in_check() 185 | # Undo the move. 186 | self.bits[piece] ^= from_bit | to_bit 187 | self.bits[EMPTY] ^= from_bit 188 | self.bits[to_bit_piece] ^= to_bit 189 | if legal: 190 | moves.append((piece, from_bit, to_bit, to_bit_piece)) 191 | 192 | def in_check(self): 193 | enemy = (self.side ^ 1) * 6 194 | king = self.bits[W_KING + self.side * 6] 195 | for diagonal_step in DIAGONAL: 196 | move = diagonal_step(king) 197 | while move & self.bits[EMPTY]: 198 | move = diagonal_step(move) 199 | if move and move & (self.bits[W_BISHOP+enemy] | self.bits[W_QUEEN+enemy]): 200 | return True 201 | for cardinal_step in CARDINAL: 202 | move = cardinal_step(king) 203 | while move & self.bits[EMPTY]: 204 | move = cardinal_step(move) 205 | if move and move & (self.bits[W_ROOK+enemy] | self.bits[W_QUEEN+enemy]): 206 | return True 207 | for knight_move in KNIGHTAL: 208 | if knight_move(king) & self.bits[W_KNIGHT+enemy]: 209 | return True 210 | if self.side: 211 | if (DIAGONAL[0](king) | DIAGONAL[2](king)) & self.bits[W_PAWN+enemy]: 212 | return True 213 | else: 214 | if (DIAGONAL[1](king) | DIAGONAL[3](king)) & self.bits[W_PAWN+enemy]: 215 | return True 216 | return bool(neighbors(king) & self.bits[W_KING+enemy]) 217 | 218 | def make_move(self, from_row, from_col, to_row, to_col): 219 | from_bit = 1 << (from_row * 8 + from_col) 220 | to_bit = 1 << (to_row * 8 + to_col) 221 | friendly = self.side * 6 222 | piece = EMPTY 223 | if from_bit & self.bits[EMPTY]: 224 | raise Exception("Cannot move from an empty square.") 225 | for p in xrange(W_PAWN + friendly, W_KING + friendly + 1): 226 | if from_bit & self.bits[p]: 227 | piece = p 228 | break 229 | if piece == EMPTY: 230 | raise Exception("Must move a friendly piece.") 231 | to_bit_piece = EMPTY 232 | if not (to_bit & self.bits[EMPTY]): 233 | enemy = (self.side ^ 1) * 6 234 | for p in xrange(W_PAWN + enemy, W_KING + enemy): 235 | if to_bit & self.bits[p]: 236 | to_bit_piece = p 237 | break 238 | move = (piece, from_bit, to_bit, to_bit_piece) 239 | if not move in self.legal_moves(): 240 | raise Exception("Illegal move.") 241 | self.make_bit_move(piece, from_bit, to_bit, to_bit_piece) 242 | 243 | def make_bit_move(self, piece, from_bit, to_bit, to_bit_piece): 244 | self.bits[piece] ^= from_bit | to_bit 245 | self.bits[EMPTY] ^= from_bit 246 | self.bits[to_bit_piece] ^= to_bit 247 | self.side ^= 1 248 | # TODO Castling and en passant and promotion and repetition. 249 | 250 | def __eq__(self, other): 251 | return self.side == other.side and self.bits == other.bits 252 | # TODO Add additional castling and en passant data. 253 | 254 | def __str__(self): 255 | parts = [] 256 | if self.side: 257 | parts.append("\nB\n") 258 | else: 259 | parts.append("\nw\n") 260 | parts.append(' +-----------------+\n') 261 | for r in xrange(8): 262 | parts.append(' ' + str(8 - r) + '| ') 263 | for c in xrange(8): 264 | bit = 1 << (r * 8 + c) 265 | if bit & self.bits[0]: parts.append(' ') 266 | elif bit & self.bits[1]: parts.append('p ') 267 | elif bit & self.bits[2]: parts.append('n ') 268 | elif bit & self.bits[3]: parts.append('b ') 269 | elif bit & self.bits[4]: parts.append('r ') 270 | elif bit & self.bits[5]: parts.append('q ') 271 | elif bit & self.bits[6]: parts.append('k ') 272 | elif bit & self.bits[7]: parts.append('P ') 273 | elif bit & self.bits[8]: parts.append('N ') 274 | elif bit & self.bits[9]: parts.append('B ') 275 | elif bit & self.bits[10]: parts.append('R ') 276 | elif bit & self.bits[11]: parts.append('Q ') 277 | elif bit & self.bits[12]: parts.append('K ') 278 | else: parts.append('? ') 279 | parts.append('|\n') 280 | parts.append(' +-----------------+\n') 281 | parts.append(' a b c d e f g h\n\n') 282 | return "".join(parts) 283 | 284 | def play(): 285 | board = ChessBoard() 286 | legal_moves = board.legal_moves() 287 | while len(legal_moves): 288 | prompt = 'Enter a move. (ie "c2c4")\n> ' 289 | line = raw_input(str(board) + prompt) 290 | try: 291 | from_row = 8 - int(line[1]) 292 | from_col = ord(line[0]) - 97 293 | to_row = 8 - int(line[3]) 294 | to_col = ord(line[2]) - 97 295 | board.make_move(from_row, from_col, to_row, to_col) 296 | except Exception as e: 297 | print(e) 298 | legal_moves = board.legal_moves() 299 | 300 | if __name__ == "__main__": 301 | import sys 302 | if sys.argv[-1] == "play": 303 | play() 304 | else: 305 | import unittest 306 | class Test(unittest.TestCase): 307 | def test_chess_board_init(self): 308 | board = ChessBoard() 309 | self.assertEqual(board, ChessBoard(side=board.side, bits=board.bits[:])) 310 | self.assertEqual(board, ChessBoard(string=str(board))) 311 | 312 | def test_chess_board_initial_moves(self): 313 | board = ChessBoard() 314 | self.assertEqual(len(board.legal_moves()), 20) 315 | 316 | def test_chess_board_knight_moves(self): 317 | board = ChessBoard("""w 318 | +-----------------+ 319 | 8| K | 320 | 7| P | 321 | 6| p | 322 | 5| k | 323 | 4| n | 324 | 3| | 325 | 2| | 326 | 1| | 327 | +-----------------+ 328 | a b c d e f g h""") 329 | moves = board.legal_moves() 330 | self.assertEqual(len(moves), 14) 331 | knight_moves = 0 332 | for move in moves: 333 | if move[0] == W_KNIGHT: 334 | knight_moves |= move[2] 335 | self.assertEqual(knight_moves, 0x14220022140000) 336 | 337 | def test_chess_board_queen_moves(self): 338 | board = ChessBoard("""w 339 | +-----------------+ 340 | 8| K | 341 | 7| k P | 342 | 6| p | 343 | 5| | 344 | 4| q | 345 | 3| | 346 | 2| | 347 | 1| | 348 | +-----------------+ 349 | a b c d e f g h""") 350 | moves = board.legal_moves() 351 | self.assertEqual(len(moves), 29) 352 | queen_moves = 0 353 | for move in moves: 354 | if move[0] == W_QUEEN: 355 | queen_moves |= move[2] 356 | self.assertEqual(queen_moves, 0x492a1cf71c2a0908) 357 | 358 | def test_chess_board_in_check_moves(self): 359 | board = ChessBoard("""w 360 | +-----------------+ 361 | 8| n K Q | 362 | 7| P | 363 | 6| R k | 364 | 5| b | 365 | 4| r r | 366 | 3| B | 367 | 2| | 368 | 1| | 369 | +-----------------+ 370 | a b c d e f g h""") 371 | moves = board.legal_moves() 372 | self.assertEqual(len(moves), 6) 373 | 374 | def test_chess_board_legal_moves(self): 375 | board = ChessBoard("""w 376 | +-----------------+ 377 | 8| R N B K B R | 378 | 7| P P P Q P P P | 379 | 6| P | 380 | 5| N n | 381 | 4| q p p P | 382 | 3| p | 383 | 2| p p p p p | 384 | 1| r n b k b r | 385 | +-----------------+ 386 | a b c d e f g h""") 387 | self.assertEqual(len(board.legal_moves()), 37) 388 | board.side = 1 389 | self.assertEqual(len(board.legal_moves()), 32) 390 | unittest.main() 391 | 392 | -------------------------------------------------------------------------------- /ch-11-testing/04-no-test-tools.py: -------------------------------------------------------------------------------- 1 | # Load test a webpage without using testing tools. 2 | 3 | # All programming tools are testing tools when they are used for testing. 4 | 5 | # Post a link to the webpage on a popular thread on Reddit or on Slashdot. 6 | 7 | -------------------------------------------------------------------------------- /ch-11-testing/05-test-a-pen.py: -------------------------------------------------------------------------------- 1 | # Test a pen. 2 | 3 | # If the use case for the pen is signing a loan request form from a bank in 4 | # order to open a low-sugar bakery, then scribbling on a scrap of paper will 5 | # suffice. 6 | 7 | # If the use case is writing a ten-page letter to a spouse that has been living 8 | # in a different country for the past 10 months, then opening the pen up to 9 | # visually inspect the amount of ink left in the cartridge is advisable. 10 | 11 | # If the use case is ensuring that this line of pens will not leak ink into a 12 | # customer's pocket during an accidental bike ride in the rain, then taking a 13 | # statistically significant sample of pens and simulating the regrettable 14 | # conditions should work. 15 | 16 | -------------------------------------------------------------------------------- /ch-11-testing/06-test-an-atm.py: -------------------------------------------------------------------------------- 1 | # Test at-the-money options trading in a distributed banking system. 2 | 3 | # In a distributed banking system, an option is at-the-money if the strike 4 | # price is equal to the market value of the underlying security. Automatic 5 | # test methods for this type of option should include several different 6 | # scenarios including edge cases. If the system includes he ability to 7 | # autonomously trade such options, then the decision making algorithm should 8 | # be back-tested against historic data and evaluated in terms of risk in 9 | # addition to expected fiscal gains. 10 | 11 | -------------------------------------------------------------------------------- /ch-12-c-and-c-plus-plus/.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.so 3 | *.txt 4 | -------------------------------------------------------------------------------- /ch-12-c-and-c-plus-plus/01-last-k-lines.py: -------------------------------------------------------------------------------- 1 | # Read the last k lines of a file with C or C++. 2 | 3 | import ctypes 4 | import os 5 | if not os.path.isfile("./lastklines.so"): 6 | print("Run `make`.") 7 | lib = ctypes.cdll.LoadLibrary('./lastklines.so') 8 | 9 | def read_last_lines(filename, k): 10 | lib.lastklines.restype = ctypes.c_char_p 11 | return lib.lastklines(ctypes.c_char_p(filename), ctypes.c_int(k)) 12 | 13 | import unittest 14 | 15 | class Test(unittest.TestCase): 16 | def test_read_last_lines(self): 17 | file0 = open('test-file.txt', 'w') 18 | file0.write("""Gattaca 19 | Big Fish 20 | The Princess Bride 21 | Hidden Figures 22 | Fantastic Mr. Fox 23 | Get Out 24 | Her 25 | Big Hero 6 26 | Zootopia 27 | WALL-E 28 | Yes Man 29 | Stand and Deliver 30 | Garden State 31 | The Sound of Music 32 | Dead Poets Society 33 | Captain Fantastic 34 | I Origins 35 | Life of Pi 36 | Interstellar 37 | Ex Machina""") 38 | file0.close() 39 | result = read_last_lines("test-file.txt", 5) 40 | self.assertEqual(result, 41 | "Captain Fantastic\nI Origins\nLife of Pi\nInterstellar\nEx Machina\n") 42 | os.remove(file0.name) 43 | 44 | if __name__ == "__main__": 45 | unittest.main() 46 | 47 | -------------------------------------------------------------------------------- /ch-12-c-and-c-plus-plus/Makefile: -------------------------------------------------------------------------------- 1 | all: lastklines.so 2 | 3 | lastklines.so: lastklines.o 4 | g++ -shared -Wl,-soname,lastklines.so -o lastklines.so lastklines.o 5 | 6 | lastklines.o: lastklines.cpp 7 | g++ -Wall -fPIC -O2 -c lastklines.cpp -o lastklines.o 8 | 9 | clean: 10 | rm -f *.o *.so 11 | 12 | -------------------------------------------------------------------------------- /ch-12-c-and-c-plus-plus/lastklines.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | using namespace std; 7 | 8 | extern "C" char* lastklines(char* filename, int k_lines) { 9 | string lines[k_lines]; 10 | int index = 0; 11 | ifstream infile(filename); 12 | if (infile.is_open()) { 13 | string line; 14 | while (getline(infile, line)) { 15 | lines[index] = line + "\n"; 16 | index = (index + 1) % k_lines; 17 | } 18 | infile.close(); 19 | } 20 | string lastlines = ""; 21 | for (int i = 0; i < k_lines; i++) { 22 | lastlines.append(lines[i]); 23 | } 24 | char* ret = (char*)malloc(sizeof(char) * (lastlines.length() + 1)); 25 | strcpy(ret, lastlines.c_str()); 26 | return ret; 27 | } 28 | 29 | -------------------------------------------------------------------------------- /ch-13-java/.gitignore: -------------------------------------------------------------------------------- 1 | *.class 2 | -------------------------------------------------------------------------------- /ch-13-java/08-lambda-random.py: -------------------------------------------------------------------------------- 1 | # Choose a subset randomly in Java. 2 | 3 | # Build the Java .class file with `ant`. 4 | 5 | import RandoSet 6 | 7 | def random_subset(array, seed): 8 | rando = RandoSet(array, seed) 9 | subset = rando.randomSubset() 10 | return subset 11 | 12 | import unittest 13 | 14 | class Test(unittest.TestCase): 15 | def test_random_subset(self): 16 | array = ["one", "two", "three", "four", "five", "six", "seven", "eight"] 17 | subset = [str(x) for x in random_subset(array, 12)] 18 | self.assertEqual(subset, ["one", "seven", "eight"]) 19 | 20 | if __name__ == "__main__": 21 | unittest.main() 22 | 23 | -------------------------------------------------------------------------------- /ch-13-java/RandoSet.java: -------------------------------------------------------------------------------- 1 | import java.util.Random; 2 | import java.util.ArrayList; 3 | import java.util.concurrent.Callable; 4 | 5 | public class RandoSet { 6 | private String[] array; 7 | private Random random; 8 | 9 | public RandoSet(String[] array, int seed) { 10 | this.array = array; 11 | this.random = new Random(seed); 12 | } 13 | 14 | public ArrayList randomSubset() throws Exception { 15 | ArrayList subset = new ArrayList(); 16 | Callable include = () -> { return random.nextBoolean(); }; 17 | for (String item : array) { 18 | if (include.call()) { 19 | subset.add(item); 20 | } 21 | } 22 | return subset; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /ch-13-java/build.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /ch-14-databases/.gitignore: -------------------------------------------------------------------------------- 1 | join_example.db 2 | -------------------------------------------------------------------------------- /ch-14-databases/04-joins.py: -------------------------------------------------------------------------------- 1 | # Explain the various types of database table joins. 2 | 3 | # In SQL, the JOIN keyword is used to combine records in tables according to 4 | # a specified field in each. When the specified fields match, the records are 5 | # joined together. The different types of joins (INNER, LEFT, RIGHT, and 6 | # OUTER) only differ in how they handle records that do not have a match in 7 | # the other table. 8 | 9 | # INNER joins ignore records that do not match. OUTER joins include all records 10 | # whether or not they matched with a record from the other table. LEFT joins 11 | # include records in the left table whether or not they match, but do not 12 | # include unmatched records in the right table. RIGHT joins are the reverse 13 | # of LEFT joins. When a record is included that doe not match another record 14 | # NULL values are used where a matching records' values would otherwise be. 15 | 16 | # Consider the following two tables. An inner join on the author's name 17 | # would contain four rows for the combinations of Randall Munroe's comics 18 | # and books and one row for Matthew Inman. A left join with the Comic as the 19 | # first table would additionally return a row for Zach Weinersmith with NULL 20 | # values for the book.title, book.author, and book.year. A full outer join 21 | # would contain six rows. 22 | 23 | # Comic Table 24 | # +=============+==================+=============================+ 25 | # | Name | Author | URL | 26 | # +=============+==================+=============================+ 27 | # | XKCD | Randall Munroe | https://www.xkcd.com/ | 28 | # +-------------+------------------+-----------------------------+ 29 | # | What If? | Randall Munroe | https://what-if.xkcd.com/ | 30 | # +--------------+-----------------+-----------------------------+ 31 | # | The Oatmeal | Matthew Inman | http://theoatmeal.com/ | 32 | # +-------------+------------------+-----------------------------+ 33 | # | SMBC | Zach Weinersmith | http://www.smbc-comics.com/ | 34 | # +-------------+------------------+-----------------------------+ 35 | 36 | # Book Table 37 | # +=================================================+================+======+ 38 | # | Title | Author | Year | 39 | # +=================================================+================+======+ 40 | # | What If? | Randall Munroe | 2014 | 41 | # +-------------------------------------------------+----------------+------+ 42 | # | Thing Explainer | Randall Munroe | 2015 | 43 | # +-------------------------------------------------+----------------+------+ 44 | # | How to Tell if Your Cat is Plotting to Kill You | Matthew Inman | 2012 | 45 | # +-------------------------------------------------+----------------+------+ 46 | # | The Hitchhiker's Guide to the Galaxy | Douglas Adams | 1978 | 47 | # +-------------------------------------------------+----------------+------+ 48 | 49 | import unittest 50 | import sqlite3 51 | conn = sqlite3.connect('join-example.db') 52 | 53 | class Test(unittest.TestCase): 54 | def setUp(self): 55 | c = conn.cursor() 56 | c.execute('DROP TABLE IF EXISTS Comic;') 57 | c.execute('DROP TABLE IF EXISTS Book;') 58 | c.execute('CREATE TABLE Comic(Name TEXT, Author TEXT, URL TEXT);') 59 | c.execute('INSERT INTO Comic VALUES ("XKCD", "Randall Munroe", "https://www.xkcd.com/");') 60 | c.execute('INSERT INTO Comic VALUES ("What If?", "Randall Munroe", "https://what-if.xkcd.com/");') 61 | c.execute('INSERT INTO Comic VALUES ("The Oatmeal", "Matthew Inman", "http://theoatmeal.com/");') 62 | c.execute('INSERT INTO Comic VALUES ("SMBC", "Zach Weinersmith", "http://www.smbc-comics.com/");') 63 | c.execute('CREATE TABLE Book(Title TEXT, Author TEXT, Year INT);') 64 | c.execute('INSERT INTO Book VALUES ("What If?", "Randall Munroe", 2014);') 65 | c.execute('INSERT INTO Book VALUES ("Thing Explainer", "Randall Munroe", 2015);') 66 | c.execute('INSERT INTO Book VALUES ("How to Tell if Your Cat is Plotting to Kill You", "Matthew Inman", 2012);') 67 | c.execute('INSERT INTO Book VALUES ("The Hitchhiker\'s Guide to the Galaxy", "Douglas Adams", 1978);') 68 | conn.commit() 69 | 70 | def test_inner_join(self): 71 | c = conn.cursor() 72 | c.execute('SELECT Title, Book.Author, URL from Book INNER JOIN Comic ON Book.Author = Comic.Author;') 73 | rows = c.fetchall() 74 | self.assertEqual(len(rows), 5) 75 | self.assertEqual(rows, [ 76 | (u"What If?", u"Randall Munroe", u"https://what-if.xkcd.com/"), 77 | (u"What If?", u"Randall Munroe", u"https://www.xkcd.com/"), 78 | (u"Thing Explainer", u"Randall Munroe", u"https://what-if.xkcd.com/"), 79 | (u"Thing Explainer", u"Randall Munroe", u"https://www.xkcd.com/"), 80 | (u"How to Tell if Your Cat is Plotting to Kill You", u"Matthew Inman", "http://theoatmeal.com/")]) 81 | 82 | def test_left_join(self): 83 | c = conn.cursor() 84 | c.execute('SELECT Title, Book.Author, URL from Book LEFT JOIN Comic ON Book.Author = Comic.Author;') 85 | rows = c.fetchall() 86 | self.assertEqual(len(rows), 6) 87 | self.assertEqual(rows, [ 88 | (u"What If?", u"Randall Munroe", u"https://what-if.xkcd.com/"), 89 | (u"What If?", u"Randall Munroe", u"https://www.xkcd.com/"), 90 | (u"Thing Explainer", u"Randall Munroe", u"https://what-if.xkcd.com/"), 91 | (u"Thing Explainer", u"Randall Munroe", u"https://www.xkcd.com/"), 92 | (u"How to Tell if Your Cat is Plotting to Kill You", u"Matthew Inman", "http://theoatmeal.com/"), 93 | (u"The Hitchhiker\'s Guide to the Galaxy", u"Douglas Adams", None)]) 94 | 95 | if __name__ == "__main__": 96 | unittest.main() 97 | 98 | 99 | -------------------------------------------------------------------------------- /ch-14-databases/join-example.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/w-hat/ctci-solutions/434e595b63e6dd8eccc6d44c44fafc9334b84e9c/ch-14-databases/join-example.db -------------------------------------------------------------------------------- /ch-15-threads-and-locks/03-dining-philosophers.py: -------------------------------------------------------------------------------- 1 | # How can the philosophers successfully eat with their chopsticks? 2 | 3 | import time 4 | import threading 5 | 6 | class Chopstick(object): 7 | def __init__(self, priority): 8 | self.priority = priority 9 | self.lock = threading.Lock() 10 | 11 | def pick_up(self): 12 | self.lock.acquire() 13 | 14 | def set_down(self): 15 | self.lock.release() 16 | 17 | class Philosopher(threading.Thread): 18 | def __init__(self, table, number, left_chopstick, right_chopstick): 19 | super(Philosopher, self).__init__() 20 | self.table = table 21 | self.number = number 22 | self.left_chopstick = left_chopstick 23 | self.right_chopstick = right_chopstick 24 | 25 | def run(self): 26 | #for x in xrange(5): 27 | # print("I'm philosopher {}".format(self.number)) 28 | if self.left_chopstick.priority < self.right_chopstick.priority: 29 | first_chopstick = self.left_chopstick 30 | second_chopstick = self.right_chopstick 31 | else: 32 | first_chopstick = self.right_chopstick 33 | second_chopstick = self.left_chopstick 34 | first_chopstick.pick_up() 35 | time.sleep(0.1) 36 | second_chopstick.pick_up() 37 | self.eat_food() 38 | first_chopstick.set_down() 39 | second_chopstick.set_down() 40 | 41 | def eat_food(self): 42 | self.table.finish_eating(self) 43 | 44 | class Table(object): 45 | def __init__(self): 46 | self.lock = threading.Lock() 47 | self.count = 0 48 | 49 | def finish_eating(self, philosopher): 50 | self.lock.acquire() 51 | self.count += 1 52 | #print("Philosopher {} has eaten.".format(philosopher.number)) 53 | #print("Count: {}".format(self.count)) 54 | self.lock.release() 55 | 56 | def get_count(self): 57 | self.lock.acquire() 58 | count = self.count 59 | self.lock.release() 60 | return count 61 | 62 | import unittest 63 | 64 | class Test(unittest.TestCase): 65 | def test_dining_philosophers(self): 66 | n = 10 67 | t = Table() 68 | chops = [Chopstick(i) for i in xrange(n)] 69 | philosophers = [Philosopher(t,i,chops[i],chops[(i+1)%n]) for i in xrange(n)] 70 | for philosopher in philosophers: 71 | philosopher.start() 72 | time.sleep(0.5) 73 | self.assertEqual(t.get_count(), n) 74 | 75 | if __name__ == "__main__": 76 | unittest.main() 77 | 78 | -------------------------------------------------------------------------------- /ch-16-moderate/01-number-swapper.py: -------------------------------------------------------------------------------- 1 | # Swap two numbers without a temporary variable. 2 | 3 | def number_swapper(a, b): 4 | a ^= b 5 | b ^= a 6 | a ^= b 7 | return (a, b) 8 | 9 | import unittest 10 | 11 | class Test(unittest.TestCase): 12 | def test_number_swapper(self): 13 | a = 123456789 14 | b = 1010101010101010 15 | (a, b) = number_swapper(a, b) 16 | self.assertEqual(a, 1010101010101010) 17 | self.assertEqual(b, 123456789) 18 | 19 | if __name__ == "__main__": 20 | unittest.main() 21 | 22 | -------------------------------------------------------------------------------- /ch-16-moderate/02-word-frequencies.py: -------------------------------------------------------------------------------- 1 | # Report word frequencies for a text. 2 | 3 | class TextStats(object): 4 | def __init__(self, lines): 5 | self.total_words = 0 6 | self.word_counts = Counter() 7 | for line in lines: 8 | words = line.strip().split(" ") 9 | self.total_words += len(words) 10 | for word in words: 11 | self.word_counts[word] += 1 12 | 13 | def word_frequency(self, word): 14 | if self.total_words == 0: 15 | return None 16 | return float(self.word_counts[word]) / self.total_words 17 | 18 | class Counter(dict): 19 | def __missing__(self, item): 20 | return 0 21 | 22 | import unittest 23 | 24 | class Test(unittest.TestCase): 25 | def test_word_frequency(self): 26 | text = """ 27 | When the sun shines, we'll shine together 28 | Told you I'd be here forever 29 | Said I'll always be a friend 30 | Took an oath I'ma stick it out 'til the end 31 | Now that it's raining more than ever 32 | Know that we'll still have each other 33 | You can stand under my umbrella 34 | You can stand under my umbrella 35 | (Ella ella eh eh eh) 36 | Under my umbrella 37 | (Ella ella eh eh eh) 38 | Under my umbrella 39 | (Ella ella eh eh eh) 40 | Under my umbrella 41 | (Ella ella eh eh eh eh eh eh)""" 42 | stats = TextStats(text.strip().split("\n")) 43 | self.assertEqual(stats.total_words, 87) 44 | self.assertEqual(stats.word_frequency("umbrella"), 5 / 87.0) 45 | 46 | if __name__ == "__main__": 47 | unittest.main() 48 | 49 | -------------------------------------------------------------------------------- /ch-16-moderate/03-intersection.py: -------------------------------------------------------------------------------- 1 | # Return the intersection of two line segments. 2 | 3 | import math 4 | 5 | THRESHOLD = 0.00000001 6 | 7 | def intersection(s1, s2): 8 | a, b = s1.p2.y - s1.p1.y, -(s1.p2.x - s1.p1.x) 9 | c, d = s2.p2.y - s2.p1.y, -(s2.p2.x - s2.p1.x) 10 | det = a * d - b * c 11 | if det == 0: 12 | s2_len = s2.length() 13 | if abs(s1.p1.dist(s2.p1) + s1.p1.dist(s2.p2) - s2_len) < THRESHOLD: 14 | return s1.p1 15 | if abs(s1.p2.dist(s2.p1) + s1.p2.dist(s2.p2) - s2_len) < THRESHOLD: 16 | return s1.p2 17 | return None 18 | e, f = s1.p1.x * a + s1.p1.y * b, s2.p1.x * c + s2.p1.y * d 19 | x = (d * e - b * f) / float(det) 20 | y = (a * f - c * e) / float(det) 21 | p = Point(x, y) 22 | if s1.box_contains(p) and s2.box_contains(p): 23 | return Point(x, y) 24 | return None 25 | 26 | class Segment(object): 27 | def __init__(self, p1, p2): 28 | self.p1, self.p2 = p1, p2 29 | 30 | def length(self): 31 | return self.p1.dist(self.p2) 32 | 33 | def box_contains(self, p): 34 | minx, maxx = min(self.p1.x, self.p2.x), max(self.p1.x, self.p2.x) 35 | miny, maxy = min(self.p1.y, self.p2.y), max(self.p1.y, self.p2.y) 36 | return minx <= p.x and maxx >= p.x and miny <= p.y and maxy >= p.y 37 | 38 | class Point(object): 39 | def __init__(self, x, y): 40 | self.x, self.y = x, y 41 | 42 | def dist(self, other): 43 | if self.x == other.x and self.y == other.y: 44 | return 0 45 | return math.sqrt((self.x - other.x)**2 + (self.y - other.y)**2) 46 | 47 | def coords(self): 48 | return (self.x, self.y) 49 | 50 | import unittest 51 | 52 | class Test(unittest.TestCase): 53 | def test_intersection(self): 54 | seg1 = Segment(Point(1,1), Point(4,4)) 55 | seg2 = Segment(Point(3,3), Point(7,7)) 56 | self.assertEqual(intersection(seg1, seg2).coords(), (4,4)) 57 | seg1 = Segment(Point(1,1), Point(4,4)) 58 | seg2 = Segment(Point(5,5), Point(8,8)) 59 | self.assertEqual(intersection(seg1, seg2), None) 60 | seg1 = Segment(Point(1,1), Point(4,4)) 61 | seg2 = Segment(Point(3,-3), Point(-2,2)) 62 | self.assertEqual(intersection(seg1, seg2), None) 63 | seg1 = Segment(Point(-1,-1), Point(4,4)) 64 | seg2 = Segment(Point(3,-3), Point(-2,2)) 65 | self.assertEqual(intersection(seg1, seg2).coords(), (0,0)) 66 | seg1 = Segment(Point(0,-1), Point(5,4)) 67 | seg2 = Segment(Point(4,-3), Point(-1,2)) 68 | self.assertEqual(intersection(seg1, seg2).coords(), (1,0)) 69 | seg1 = Segment(Point(0,1), Point(5,6)) 70 | seg2 = Segment(Point(4,-1), Point(-1,4)) 71 | self.assertEqual(intersection(seg1, seg2).coords(), (1,2)) 72 | seg1 = Segment(Point(0,1), Point(10,31)) 73 | seg2 = Segment(Point(0,4.5), Point(10,28.5)) 74 | self.assertEqual(intersection(seg1, seg2).coords(), (35/6.0,18.5)) 75 | 76 | if __name__ == "__main__": 77 | unittest.main() 78 | 79 | -------------------------------------------------------------------------------- /ch-16-moderate/04-tic-tac-win.py: -------------------------------------------------------------------------------- 1 | # Determine if a game of tic-tac-toe is over. 2 | 3 | def tic_tac_win(board): 4 | n = len(board) 5 | if n == 0: 6 | return 0 7 | row_results = [0] * n 8 | col_results = [0] * n 9 | diag_results = [0] * 2 10 | for r in xrange(n): 11 | for c in xrange(n): 12 | if board[r][c] == "o": 13 | bit_mask = 0b10 14 | elif board[r][c] == "x": 15 | bit_mask = 0b01 16 | else: 17 | bit_mask = 0b11 18 | row_results[r] |= bit_mask 19 | col_results[c] |= bit_mask 20 | if r == c: 21 | diag_results[0] |= bit_mask 22 | if r == n - c: 23 | diag_results[1] |= bit_mask 24 | if row_results[r] != 0b11: 25 | return row_results[r] 26 | for c in xrange(n): 27 | if col_results[c] != 0b11: 28 | return col_results[c] 29 | for d in xrange(2): 30 | if diag_results[d] != 0b11: 31 | return diag_results[d] 32 | return 0 33 | 34 | import unittest 35 | 36 | class Test(unittest.TestCase): 37 | def test_tic_tac_win(self): 38 | board = [["o", "o", "o"], 39 | ["x", "x", " "], 40 | [" ", "x", "x"]] 41 | self.assertEqual(tic_tac_win(board), 0b10) 42 | board[0][0] = "x" 43 | self.assertEqual(tic_tac_win(board), 0b01) 44 | board[1][1] = "o" 45 | self.assertEqual(tic_tac_win(board), 0b00) 46 | board = [["o", "o", "o", "x"], 47 | ["x", "x", "o", "o"], 48 | ["x", " ", "x", "x"], 49 | ["o", "x", "o", "x"]] 50 | self.assertEqual(tic_tac_win(board), 0b00) 51 | board[0][3] = "o" 52 | self.assertEqual(tic_tac_win(board), 0b10) 53 | board[0][0] = "x" 54 | self.assertEqual(tic_tac_win(board), 0b01) 55 | board[2][2] = "o" 56 | self.assertEqual(tic_tac_win(board), 0b10) 57 | 58 | if __name__ == "__main__": 59 | unittest.main() 60 | 61 | -------------------------------------------------------------------------------- /ch-16-moderate/05-factorial-zeros.py: -------------------------------------------------------------------------------- 1 | # Count the number of zeros at the end of a factorial. 2 | 3 | def factorial_zeros(n): 4 | five_factors = 0 5 | while n > 4: 6 | n /= 5 7 | five_factors += n 8 | return five_factors 9 | 10 | import unittest 11 | 12 | class Test(unittest.TestCase): 13 | def test_factorial_zeros(self): 14 | self.assertEqual(factorial_zeros(4), 0) 15 | self.assertEqual(factorial_zeros(9), 1) 16 | self.assertEqual(factorial_zeros(10), 2) 17 | self.assertEqual(factorial_zeros(25), 6) 18 | self.assertEqual(factorial_zeros(55), 13) 19 | 20 | if __name__ == "__main__": 21 | unittest.main() 22 | 23 | -------------------------------------------------------------------------------- /ch-16-moderate/06-smallest-difference.py: -------------------------------------------------------------------------------- 1 | # Find the smallest difference between any two elements of two arrays. 2 | 3 | # Of course an O(n*lg(n) + m*lg(n)) solutions exists in which we only sort 4 | # one array and search through it for the closest to each element of the other, 5 | # but this O(n*lg(n) + m*lg(m)) solution feels more simple. 6 | # And, sometimes at least, simplicity counts for something. 7 | 8 | # Also, there is an O(d*n + m) solution where d is the smallest difference 9 | # found between a constant number of terms at the beginning of the two arrays. 10 | # This solution would continue by storing d items in a hash table for each 11 | # element in the first array. 12 | 13 | def smallest_difference(array1, array2): 14 | if len(array1) == 0 or len(array2) == 0: 15 | return None 16 | sorted1, sorted2 = sorted(array1), sorted(array2) 17 | smallest_diff = abs(sorted1[0] - sorted2[0]) 18 | ix1, ix2 = 0, 0 19 | while True: 20 | diff = sorted1[ix1] - sorted2[ix2] 21 | smallest_diff = min(smallest_diff, abs(diff)) 22 | if diff == 0: 23 | break 24 | if diff < 0: 25 | ix1 += 1 26 | if ix1 == len(sorted1): 27 | break 28 | else: 29 | ix2 += 1 30 | if ix2 == len(sorted2): 31 | break 32 | return smallest_diff 33 | 34 | import unittest 35 | 36 | class Test(unittest.TestCase): 37 | def test_smallest_difference(self): 38 | self.assertEqual(smallest_difference([11, 22, 33, 44], [77, 2, 66, 50]), 6) 39 | self.assertEqual(smallest_difference([11, 22, 33, 44], [77, 2, 34, 50]), 1) 40 | self.assertEqual(smallest_difference([11, 77, 33, 44], [77, 2, 34, 50]), 0) 41 | array1 = [10, 20, 30, 40, 50, 60, 70, 80, 90] 42 | array2 = [33, 45, 58, 17] 43 | self.assertEqual(smallest_difference(array1, array2), 2) 44 | self.assertEqual(smallest_difference(array2, array1), 2) 45 | 46 | if __name__ == "__main__": 47 | unittest.main() 48 | 49 | -------------------------------------------------------------------------------- /ch-16-moderate/07-number-max.py: -------------------------------------------------------------------------------- 1 | # Return the larger of two numbers without any comparison operators or if 2 | # statements. 3 | 4 | def number_max(a, b): 5 | diff = b - a 6 | sign = (diff & (1 << 63)) >> 63 7 | return a * sign + b * (sign ^ 1) 8 | 9 | import unittest 10 | 11 | class Test(unittest.TestCase): 12 | def test_number_max(self): 13 | self.assertEqual(number_max(10000, 10), 10000) 14 | self.assertEqual(number_max(0x10000, 0xFFFF), 0x10000) 15 | self.assertEqual(number_max(1212121, 1234567), 1234567) 16 | 17 | if __name__ == "__main__": 18 | unittest.main() 19 | 20 | -------------------------------------------------------------------------------- /ch-16-moderate/08-english-int.py: -------------------------------------------------------------------------------- 1 | # Return an English reading of the given number. 2 | 3 | SINGLE_DIGIT = {0: 'zero', 1: 'one', 2: 'two', 3: 'three', 4: 'four', 5: 'five', 4 | 6: 'six', 7: 'seven', 8: 'eight', 9: 'nine'} 5 | TEENS = {10: 'ten', 11: 'eleven', 12: 'twelve', 13: 'thirteen', 14: 'fourteen', 6 | 15: 'fifteen', 16: 'sixteen', 17: 'seventeen', 18: 'eighteen', 7 | 19: 'nineteen'} 8 | TENS = {20: 'twenty', 30: 'thirty', 40: 'forty', 50: 'fifty', 60: 'sixty', 9 | 70: 'seventy', 80: 'eighty', 90: 'ninety'} 10 | LARGE_NUMBERS = ['thousand', 'million', 'billion', 'trillion', 'quadrillion'] 11 | 12 | def english_int(n): 13 | if n == 0: 14 | return "zero" 15 | parts = [] 16 | hundreds = n % 1000 17 | if hundreds != 0: 18 | parts.append(english_int_hundreds(hundreds)) 19 | added_and = False 20 | magnitude_index = 0 21 | n /= 1000 22 | while n: 23 | if len(parts) > 0 and not added_and: 24 | parts.append("and") 25 | added_and = True 26 | hundreds = n % 1000 27 | if hundreds: 28 | parts.append(LARGE_NUMBERS[magnitude_index]) 29 | parts.append(english_int_hundreds(hundreds)) 30 | magnitude_index += 1 31 | n /= 1000 32 | return " ".join(reversed(parts)) 33 | 34 | 35 | def english_int_hundreds(n): 36 | if n == 0: 37 | return "" 38 | parts = [] 39 | tens_ones = n % 100 40 | if tens_ones < 10: 41 | if tens_ones > 0: 42 | parts.append(SINGLE_DIGIT[tens_ones]) 43 | elif tens_ones < 20: 44 | parts.append(TEENS[tens_ones]) 45 | else: 46 | ones = tens_ones % 10 47 | tens = tens_ones - ones 48 | if ones > 0: 49 | parts.append(SINGLE_DIGIT[ones]) 50 | parts.append(TENS[tens]) 51 | hundreds = n / 100 52 | if hundreds: 53 | if len(parts) > 0: 54 | parts.append("and") 55 | parts.append("hundred") 56 | parts.append(SINGLE_DIGIT[hundreds]) 57 | return " ".join(reversed(parts)) 58 | 59 | import unittest 60 | 61 | class Test(unittest.TestCase): 62 | def test_english_int(self): 63 | self.assertEqual(english_int(0), "zero") 64 | self.assertEqual(english_int(1), "one") 65 | self.assertEqual(english_int(8), "eight") 66 | self.assertEqual(english_int(15), "fifteen") 67 | self.assertEqual(english_int(92), "ninety two") 68 | self.assertEqual(english_int(113), "one hundred and thirteen") 69 | self.assertEqual(english_int(1001), "one thousand and one") 70 | self.assertEqual(english_int(2075), "two thousand and seventy five") 71 | self.assertEqual(english_int(20066), "twenty thousand and sixty six") 72 | self.assertEqual(english_int(500012), "five hundred thousand and twelve") 73 | self.assertEqual(english_int(950000), "nine hundred and fifty thousand") 74 | self.assertEqual(english_int(1000000), "one million") 75 | self.assertEqual(english_int(6000000000), "six billion") 76 | self.assertEqual(english_int(2006000001), "two billion six million and one") 77 | self.assertEqual(english_int(6020000000), "six billion and twenty million") 78 | self.assertEqual(english_int(3000000000042), "three trillion and forty two") 79 | 80 | if __name__ == "__main__": 81 | unittest.main() 82 | 83 | -------------------------------------------------------------------------------- /ch-16-moderate/09-operations.py: -------------------------------------------------------------------------------- 1 | # Implement operations with only addition (and negation). 2 | 3 | def multiply(a, b): 4 | if abs(a) < abs(b): 5 | s, l = a, b 6 | else: 7 | s, l = b, a 8 | product = 0 9 | for _ in xrange(abs(s)): 10 | product += l 11 | if s < 0: 12 | return -product 13 | return product 14 | 15 | def divide(a, b): 16 | if b == 0: 17 | return None 18 | product = 0 19 | quotient = 0 20 | while product + abs(b) < abs(a) + 1: 21 | product += abs(b) 22 | quotient += 1 23 | if int(a < 0) + int(b < 0) == 1: 24 | return -quotient 25 | return quotient 26 | 27 | def subtract(a, b): 28 | return a + (-b) 29 | 30 | import unittest 31 | 32 | class Test(unittest.TestCase): 33 | def test_multiply(self): 34 | self.assertEqual(multiply(3, 6), 18) 35 | self.assertEqual(multiply(7, 11), 77) 36 | self.assertEqual(multiply(-8, 10), -80) 37 | 38 | def test_divide(self): 39 | self.assertEqual(divide(3, 6), 0) 40 | self.assertEqual(divide(30, 6), 5) 41 | self.assertEqual(divide(34, -6), -5) 42 | self.assertEqual(divide(120, 10), 12) 43 | 44 | def test_subtract(self): 45 | self.assertEqual(subtract(34, 6), 28) 46 | self.assertEqual(subtract(31, -6), 37) 47 | 48 | if __name__ == "__main__": 49 | unittest.main() 50 | 51 | -------------------------------------------------------------------------------- /ch-16-moderate/10-living-people.py: -------------------------------------------------------------------------------- 1 | # Find the year with the most living people. 2 | 3 | def most_living_people(people): 4 | if len(people) == 0: 5 | return None 6 | change = [0] * 102 7 | alive = 0 8 | max_alive = 0 9 | max_year = 0 10 | for person in people: 11 | change[person.birth_year - 1900] += 1 12 | if person.death_year: 13 | change[person.death_year - 1899] -= 1 14 | for year in xrange(0, 101): 15 | alive += change[year] 16 | if alive > max_alive: 17 | max_alive = alive 18 | max_year = year 19 | return max_year + 1900 20 | 21 | class P(object): 22 | def __init__(self, born, died=None): 23 | self.birth_year = born 24 | self.death_year = died 25 | 26 | import unittest 27 | 28 | class Test(unittest.TestCase): 29 | def test_most_living_people(self): 30 | people=[P(1907,1942),P(1909,1982),P(1933,1967),P(1912,1954),P(1980),P(1988)] 31 | self.assertEqual(most_living_people(people), 1933) 32 | 33 | if __name__ == "__main__": 34 | unittest.main() 35 | 36 | -------------------------------------------------------------------------------- /ch-16-moderate/11-diving-board.py: -------------------------------------------------------------------------------- 1 | # How many lengths can the diving board be? 2 | 3 | def diving_board(k, shorter, longer): 4 | if shorter == longer: 5 | return [k * shorter] 6 | return range(k * shorter, k * longer + 1, longer - shorter) 7 | 8 | import unittest 9 | 10 | class Test(unittest.TestCase): 11 | def test_diving_board(self): 12 | self.assertEqual(diving_board(5, 3, 4), [15, 16, 17, 18, 19, 20]) 13 | self.assertEqual(diving_board(4, 2, 6), [8, 12, 16, 20, 24]) 14 | 15 | if __name__ == "__main__": 16 | unittest.main() 17 | 18 | -------------------------------------------------------------------------------- /ch-16-moderate/12-xml-encoding.py: -------------------------------------------------------------------------------- 1 | # Encode some XML. 2 | 3 | def xml_encoding(xml_object, mapping): 4 | parts = [str(mapping[xml_object.element])] 5 | for tag, value in sorted(xml_object.attributes.items(), reverse=True): 6 | parts.append(str(mapping[tag])) 7 | parts.append(str(value)) 8 | parts.append("0") 9 | for child in xml_object.children: 10 | parts.append(xml_encoding(child, mapping)) 11 | parts.append("0") 12 | return " ".join(parts) 13 | 14 | class XMLObject(object): 15 | def __init__(self, element, attributes=None, children=None): 16 | self.element = element 17 | if attributes is None: 18 | self.attributes = {} 19 | else: 20 | self.attributes = attributes 21 | if children is None: 22 | self.children = [] 23 | else: 24 | self.children = children 25 | 26 | import unittest 27 | 28 | class Test(unittest.TestCase): 29 | def test_xml_encoding(self): 30 | mapping = {"name": 1, "instrument": 2, "person": 3, "monkey": 4, "color": 5} 31 | xml = XMLObject("person", 32 | {"name": "The Man with the Yellow Hat", "instrument": "tuba"}, 33 | [XMLObject("monkey", {"name": "George", "color": "brown"})]) 34 | self.assertEqual(xml_encoding(xml, mapping), 35 | "3 1 The Man with the Yellow Hat 2 tuba 0 4 1 George 5 brown 0 0 0") 36 | 37 | if __name__ == "__main__": 38 | unittest.main() 39 | 40 | -------------------------------------------------------------------------------- /ch-16-moderate/13-bisect-squares.py: -------------------------------------------------------------------------------- 1 | # Bisect the squares. 2 | 3 | def bisect_squares(square1, square2): 4 | center1, center2 = square1.center, square2.center 5 | xdiff = center2.x - center1.x 6 | ydiff = center2.y - center1.y 7 | if xdiff == 0: 8 | if ydiff == 0: 9 | return lambda x: center1.y 10 | else: 11 | # Vertical line. 12 | return None 13 | slope = float(ydiff) / xdiff 14 | intercept = center1.y - center1.x * slope 15 | return lambda x: slope * x + intercept 16 | 17 | class Square(object): 18 | def __init__(self, center, side_length, rotation): 19 | self.center, self.side_length, self.rotation = center, side_length, rotation 20 | 21 | class Point(object): 22 | def __init__(self, x, y): 23 | self.x, self.y = x, y 24 | 25 | import unittest 26 | import random 27 | 28 | class Test(unittest.TestCase): 29 | def test_bisect_squares(self): 30 | sq1 = Square(Point(2, 4.5), 2, 15) 31 | sq2 = Square(Point(7, 4.5), 3, 45) 32 | line = bisect_squares(sq1, sq2) 33 | self.assertEqual(line(0), 4.5) 34 | self.assertEqual(line(11), 4.5) 35 | for _ in xrange(10): 36 | center1 = Point(random.uniform(-10, 10), random.uniform(-10, 10)) 37 | center2 = Point(random.uniform(-10, 10), random.uniform(-10, 10)) 38 | square1 = Square(center1, random.uniform(1, 9), random.uniform(0, 90)) 39 | square2 = Square(center2, random.uniform(1, 9), random.uniform(0, 90)) 40 | line = bisect_squares(square1, square2) 41 | self.assertAlmostEqual(line(center1.x), center1.y, 7) 42 | self.assertAlmostEqual(line(center2.x), center2.y, 7) 43 | mid_x = (center1.x + center2.x) / 2 44 | mid_y = (center1.y + center2.y) / 2 45 | self.assertAlmostEqual(line(mid_x), mid_y, 7) 46 | 47 | if __name__ == "__main__": 48 | unittest.main() 49 | 50 | -------------------------------------------------------------------------------- /ch-16-moderate/14-best-line.py: -------------------------------------------------------------------------------- 1 | # Find the best fit line to the points. 2 | 3 | def best_line(points): 4 | average_x = float(sum([p.x for p in points]))/len(points) 5 | average_y = float(sum([p.y for p in points]))/len(points) 6 | shifted_points = [Point(p.x - average_x, p.y - average_x) for p in points] 7 | slope = best_slope_through_origin(shifted_points) 8 | intercept = average_y - slope * average_x 9 | if not slope: 10 | return None 11 | return lambda x: slope * x + intercept 12 | 13 | def best_slope_through_origin(points): 14 | denominator = sum([p.x * p.x for p in points]) 15 | if denominator == 0: 16 | return None 17 | numerator = sum([p.x * p.y for p in points]) 18 | return float(numerator) / denominator 19 | 20 | class Point(object): 21 | def __init__(self, x, y): 22 | self.x, self.y = x, y 23 | 24 | import unittest 25 | 26 | class Test(unittest.TestCase): 27 | def test_best_line(self): 28 | points = [Point(0, 0), Point(1, 1), Point(2, 2), Point(3, 3), Point(4, 4)] 29 | line = best_line(points) 30 | self.assertEqual(line(0), 0) 31 | self.assertEqual(line(1.5), 1.5) 32 | self.assertEqual(line(10.3), 10.3) 33 | line = best_line([Point(p.x + 0.125, p.y + 5.4) for p in points]) 34 | self.assertEqual(line(0), 5.275) 35 | self.assertEqual(line(9), 14.275) 36 | points = [Point(1, 2), Point(2, 1), Point(3, 4), Point(4, 5), Point(5, 6)] 37 | line = best_line(points) 38 | self.assertAlmostEqual(line(1), 1.2, 14) 39 | self.assertEqual(line(5), 6) 40 | points[-1].y = 7 41 | self.assertAlmostEqual(line(2), 2.4, 14) 42 | 43 | if __name__ == "__main__": 44 | unittest.main() 45 | 46 | -------------------------------------------------------------------------------- /ch-16-moderate/15-mastermind.py: -------------------------------------------------------------------------------- 1 | # Determine the number of hits and pseudohits for a guess in Mastermind. 2 | 3 | COLOR_IX = {'B': 0, 'Y': 1, 'G': 2, 'R': 3} 4 | 5 | def mastermind_hits(code, guess): 6 | hit_count, pseudohit_count = 0, 0 7 | colors_included = [0] * 4 8 | colors_hit = [0] * 4 9 | colors_guessed = [0] * 4 10 | for i in xrange(4): 11 | code_color = COLOR_IX[code[i]] 12 | guess_color = COLOR_IX[guess[i]] 13 | if code[i] == guess[i]: 14 | hit_count += 1 15 | colors_hit[code_color] = 1 16 | colors_included[code_color] = 1 17 | colors_guessed[guess_color] = 1 18 | for i in xrange(4): 19 | pseudohit_count += colors_guessed[i] & colors_included[i] & ~colors_hit[i] 20 | return (hit_count, pseudohit_count) 21 | 22 | import unittest 23 | 24 | class Test(unittest.TestCase): 25 | def test_mastermind_hits(self): 26 | self.assertEqual(mastermind_hits("YYBB", "BBYY"), (0, 2)) 27 | self.assertEqual(mastermind_hits("BYBB", "BBYY"), (1, 1)) 28 | self.assertEqual(mastermind_hits("RGBY", "RGBY"), (4, 0)) 29 | self.assertEqual(mastermind_hits("RGBY", "RBGY"), (2, 2)) 30 | self.assertEqual(mastermind_hits("RGBY", "RRRR"), (1, 0)) 31 | self.assertEqual(mastermind_hits("RRRR", "RBGY"), (1, 0)) 32 | self.assertEqual(mastermind_hits("RRYY", "RYGY"), (2, 0)) 33 | 34 | if __name__ == "__main__": 35 | unittest.main() 36 | 37 | -------------------------------------------------------------------------------- /ch-16-moderate/16-sub-sort.py: -------------------------------------------------------------------------------- 1 | # Return the bounds of the minimal portion of an array would make the entire 2 | # array sorted if it were sorted. 3 | 4 | def sub_sort(array): 5 | n = len(array) 6 | if n == 0: 7 | return (0, 0) 8 | min_so_far = [0] * n 9 | max_so_far = [0] * n 10 | max_so_far[0] = array[0] 11 | for i in xrange(1, n): 12 | max_so_far[i] = max(array[i], max_so_far[i - 1]) 13 | min_so_far[-1] = array[-1] 14 | for i in xrange(n - 2, -1, -1): 15 | min_so_far[i] = min(array[i], min_so_far[i + 1]) 16 | start, end = 0, n - 1 17 | while end > 0 and min_so_far[end] == max_so_far[end]: 18 | end -= 1 19 | while start < end and min_so_far[start] == max_so_far[start]: 20 | start += 1 21 | return (start, end) 22 | 23 | import unittest 24 | 25 | class Test(unittest.TestCase): 26 | def test_sub_sort(self): 27 | array = [10, 11, 12, 13, 14, 15, 16, 17, 18, 19] 28 | self.assertEqual(sub_sort(array), (0, 0)) 29 | array = [10, 11, 12, 13, 14, 16, 15, 17, 18, 19] 30 | self.assertEqual(sub_sort(array), (5, 6)) 31 | array = [10, 18, 12, 13, 14, 16, 15, 17, 11, 19] 32 | self.assertEqual(sub_sort(array), (1, 8)) 33 | array = [90, 80, 70, 60, 50, 40, 30, 20, 10, 01] 34 | self.assertEqual(sub_sort(array), (0, 9)) 35 | 36 | if __name__ == "__main__": 37 | unittest.main() 38 | 39 | -------------------------------------------------------------------------------- /ch-16-moderate/17-contiguous-sequence.py: -------------------------------------------------------------------------------- 1 | # Return the bounds of the contiguous sequence with the largest sum. 2 | 3 | def contiguous_sequence(array): 4 | if len(array) == 0: 5 | return None 6 | largest_bounds = (0, 0) 7 | largest_sum = 0 8 | reach_start = 0 9 | reach_sum = 0 10 | for i, elem in enumerate(array): 11 | reach_sum += elem 12 | if reach_sum < 0: 13 | reach_sum = 0 14 | reach_start = i + 1 15 | if reach_sum > largest_sum: 16 | largest_sum = reach_sum 17 | largest_bounds = (reach_start, i + 1) 18 | return largest_bounds 19 | 20 | import unittest 21 | 22 | class Test(unittest.TestCase): 23 | def test_contiguous_sequence(self): 24 | seq = [-1, 4, 4, -7, 8, 2, -4, 3] 25 | self.assertEqual(contiguous_sequence(seq), (1, 6)) 26 | seq = [-1, 4, 4, -7, 8, 2, -4, -3, 9] 27 | self.assertEqual(contiguous_sequence(seq), (1, 9)) 28 | seq = [-1, -4, -54, -7, -8, 2, -4, -3, 9] 29 | self.assertEqual(contiguous_sequence(seq), (8, 9)) 30 | seq = [-1, -4, -54, -7, -8, -2, -4, -3, -9] 31 | self.assertEqual(contiguous_sequence(seq), (0, 0)) 32 | 33 | if __name__ == "__main__": 34 | unittest.main() 35 | 36 | -------------------------------------------------------------------------------- /ch-16-moderate/18-pattern-matching.py: -------------------------------------------------------------------------------- 1 | # Determine whether the given string matches the given pattern of a's and b's. 2 | 3 | def matches_pattern(string, pattern): 4 | if len(pattern) == 0: 5 | return len(string) == 0 6 | if len(string) == 0: 7 | return True 8 | pattern = normalize(pattern) 9 | a_count, b_count = 0, 0 10 | for letter in pattern: 11 | if letter == 'a': 12 | a_count += 1 13 | else: 14 | b_count += 1 15 | for a_len in xrange(1, len(string) / a_count + 1): 16 | letters_left = len(string) - a_count * a_len 17 | if b_count and letters_left % b_count == 0: 18 | a_val = string[:a_len] 19 | b_len = (len(string) - a_count * a_len) / b_count 20 | if b_len == 0: 21 | break 22 | matches = try_pattern(string, pattern, a_val, b_len) 23 | if matches: 24 | return True 25 | elif letters_left == 0: 26 | return try_pattern(string, pattern, string[:a_len], 0) 27 | return False 28 | 29 | def try_pattern(string, pattern, a_val, b_len): 30 | a_len, b_val = len(a_val), None 31 | ix = len(a_val) 32 | for letter in pattern[1:]: 33 | if letter == 'a': 34 | if string[ix : ix + a_len] == a_val: 35 | ix += a_len 36 | else: 37 | return False 38 | elif b_val is None: 39 | b_val = string[ix : ix + b_len] 40 | ix += b_len 41 | elif string[ix : ix + b_len] == b_val: 42 | ix += b_len 43 | else: 44 | return False 45 | return True 46 | 47 | def normalize(pattern): 48 | if pattern[0] == "a": 49 | return pattern 50 | inverted = [] 51 | for letter in pattern: 52 | if letter == "a": 53 | inverted.append("b") 54 | else: 55 | inverted.append("a") 56 | return "".join(inverted) 57 | 58 | import unittest 59 | 60 | class Test(unittest.TestCase): 61 | def test_matches_pattern(self): 62 | self.assertTrue(matches_pattern("dogdogturtledog", "aaba")) 63 | self.assertTrue(matches_pattern("dogdogturtledog", "bbab")) 64 | self.assertTrue(matches_pattern("dogdogturtledogdog", "aabaa")) 65 | self.assertTrue(matches_pattern("dogdogturtledogdog", "aba")) 66 | self.assertTrue(matches_pattern("dogdogturtledogdo", "aba")) 67 | self.assertFalse(matches_pattern("dogdogturtledogdg", "aba")) 68 | self.assertTrue(matches_pattern("catcatbirdbird", "aabb")) 69 | self.assertFalse(matches_pattern("catcatcatbirdbird", "aabb")) 70 | self.assertTrue(matches_pattern("buffalobuffalobuffalobuffalo", "aaaa")) 71 | self.assertFalse(matches_pattern("buffalobuffalouffalobuffalo", "aaaa")) 72 | 73 | if __name__ == "__main__": 74 | unittest.main() 75 | 76 | -------------------------------------------------------------------------------- /ch-16-moderate/19-pond-sizes.py: -------------------------------------------------------------------------------- 1 | # Determine the sizes of the ponds. 2 | 3 | def pond_sizes(terrain): 4 | if len(terrain) == 0 or len(terrain[0]) == 0: 5 | return [] 6 | sizes = set() 7 | n, m = len(terrain), len(terrain[0]) 8 | visited = [[False] * m for _ in xrange(n)] 9 | for r in xrange(n): 10 | for c in xrange(m): 11 | if not terrain[r][c] and not visited[r][c]: 12 | sizes.add(pond_size(terrain, visited, r, c)) 13 | return sizes 14 | 15 | def pond_size(terrain, visited, row, col): 16 | if terrain[row][col] or visited[row][col]: 17 | return 0 18 | visited[row][col] = True 19 | size = 1 20 | if row > 0: 21 | size += pond_size(terrain, visited, row - 1, col) 22 | if col > 0: 23 | size += pond_size(terrain, visited, row - 1, col - 1) 24 | if col < len(terrain[0]) - 1: 25 | size += pond_size(terrain, visited, row - 1, col + 1) 26 | if row < len(terrain) - 1: 27 | size += pond_size(terrain, visited, row + 1, col) 28 | if col > 0: 29 | size += pond_size(terrain, visited, row + 1, col - 1) 30 | if col < len(terrain[0]) - 1: 31 | size += pond_size(terrain, visited, row + 1, col + 1) 32 | if col > 0: 33 | size += pond_size(terrain, visited, row, col - 1) 34 | if col < len(terrain[0]) - 1: 35 | size += pond_size(terrain, visited, row, col + 1) 36 | return size 37 | 38 | import unittest 39 | 40 | class Test(unittest.TestCase): 41 | def test_pond_sizes(self): 42 | terrain = [[0, 0, 1, 2, 3, 1, 1, 1], 43 | [1, 1, 1, 2, 2, 2, 0, 1], 44 | [1, 0, 1, 1, 2, 1, 1, 2], 45 | [0, 1, 0, 1, 3, 1, 2, 3]] 46 | self.assertEqual(pond_sizes(terrain), {1, 2, 3}) 47 | terrain = [[0, 0, 1, 2, 3, 1, 0, 1, 1], 48 | [0, 1, 1, 2, 2, 2, 0, 0, 1], 49 | [1, 0, 1, 0, 0, 1, 1, 0, 2], 50 | [0, 1, 1, 1, 0, 1, 2, 0, 2], 51 | [0, 1, 2, 1, 1, 1, 1, 0, 0]] 52 | self.assertEqual(pond_sizes(terrain), {6, 3, 7}) 53 | 54 | if __name__ == "__main__": 55 | unittest.main() 56 | 57 | -------------------------------------------------------------------------------- /ch-16-moderate/20-t-nine.py: -------------------------------------------------------------------------------- 1 | # Determine which words match a sequence of T9 key presses. 2 | 3 | LETTERS = {0: [], 1: [], 2: ['a','b','c'], 3: ['d','e','f'], 4: ['g','h','i'], 4 | 5: ['j','k','l'], 6: ['m','n','o'], 7: ['p','q','r','s'], 5 | 8: ['t','u','v'], 9: ['w','x','y','z']} 6 | 7 | PREFIX_TREE = {'a':{'b':{'':True,'a':{'c':{'u':{'s':{'':True}}}}}}, 8 | 't':{'r':{'e':{'e':{'':True}}}}, 9 | 'u':{'s':{'':True,'e':{'':True,'d':{'':True}, 10 | 'r':{'':True},'s':{'':True}}}}, 11 | 'z':{'o':{'o':{'':True,'m':{'':True}}}}} 12 | 13 | def t9_words(digits): 14 | if len(digits) == 0: 15 | return [] 16 | partials = [('', PREFIX_TREE)] 17 | for digit in digits: 18 | next_partials = [] 19 | for partial, node in partials: 20 | for letter in LETTERS[int(digit)]: 21 | if letter in node: 22 | next_partials.append((partial + letter, node[letter])) 23 | partials = next_partials 24 | words = [] 25 | for word, node in partials: 26 | if '' in node: 27 | words.append(word) 28 | return words 29 | 30 | import unittest 31 | 32 | class Test(unittest.TestCase): 33 | def test_t9_words(self): 34 | digits = "222287" 35 | self.assertEqual(t9_words(digits), ['abacus']) 36 | digits = "8733" 37 | self.assertEqual(t9_words(digits), ['tree', 'used']) 38 | 39 | if __name__ == "__main__": 40 | unittest.main() 41 | 42 | -------------------------------------------------------------------------------- /ch-16-moderate/21-sum-swap.py: -------------------------------------------------------------------------------- 1 | # Return the index of two numbers which could be swapped to make the sum of 2 | # the two arrays equal. 3 | 4 | def equalize_sum_with_swap(arr1, arr2): 5 | sum1, sum2 = sum(arr1), sum(arr2) 6 | if (sum1 - sum2) % 2: 7 | return None 8 | target_diff = (sum1 - sum2) / 2 9 | elems = {} 10 | for i, elem in enumerate(arr1): 11 | elems[elem - target_diff] = i 12 | for i, elem in enumerate(arr2): 13 | if elem in elems: 14 | return (elems[elem], i) 15 | return None 16 | 17 | import unittest 18 | 19 | class Test(unittest.TestCase): 20 | def test_equalize_sum_with_swap(self): 21 | arr1 = [5, 5, 10] 22 | arr2 = [4, 4, 8] 23 | swap = equalize_sum_with_swap(arr1, arr2) 24 | self.assertEqual(swap, (2, 2)) 25 | arr1 = [5, 5, 5] 26 | arr2 = [6, 4, 6] 27 | swap = equalize_sum_with_swap(arr1, arr2) 28 | self.assertEqual(swap, None) 29 | arr1 = [5, 5, 14] 30 | arr2 = [7, 7, 8] 31 | swap = equalize_sum_with_swap(arr1, arr2) 32 | self.assertEqual(swap, None) 33 | arr1 = [5, 5, 14] 34 | arr2 = [4, 10, 8] 35 | swap = equalize_sum_with_swap(arr1, arr2) 36 | self.assertEqual(swap, (1, 0)) 37 | 38 | if __name__ == "__main__": 39 | unittest.main() 40 | 41 | -------------------------------------------------------------------------------- /ch-16-moderate/22-langtons-ant.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Draw the grid that Langton's ant walks on after k steps. 4 | 5 | WHITE = 0 6 | BLACK = 1 7 | 8 | def langtons_ant(k): 9 | grid = Grid() 10 | direction = (1, 0) 11 | x, y = 0, 0 12 | for _ in xrange(k): 13 | if grid.at(x, y) == WHITE: 14 | direction = (-direction[1], direction[0]) 15 | else: 16 | direction = (direction[1], -direction[0]) 17 | grid.flip(x, y) 18 | x += direction[0] 19 | y += direction[1] 20 | return str(grid) 21 | 22 | class Grid(object): 23 | def __init__(self): 24 | self.squares = DefaultZeroDict() 25 | self.min_x, self.max_x = -1, 1 26 | self.min_y, self.max_y = -1, 1 27 | 28 | def at(self, x, y): 29 | return self.squares[(x, y)] 30 | 31 | def flip(self, x, y): 32 | self.squares[(x, y)] ^= BLACK 33 | self.min_x = min(self.min_x, x - 1) 34 | self.max_x = max(self.max_x, x + 1) 35 | self.min_y = min(self.min_y, y - 1) 36 | self.max_y = max(self.max_y, y + 1) 37 | 38 | def __str__(self): 39 | num_cols = 2 * (self.max_x - self.min_x) + 3 40 | parts = ["\n+" + "-" * num_cols + "+\n"] 41 | for row in xrange(self.min_y, self.max_y + 1): 42 | parts.append('| ') 43 | for col in xrange(self.min_x, self.max_x + 1): 44 | if self.at(col, row) == WHITE: 45 | parts.append("□ ") 46 | else: 47 | parts.append("■ ") 48 | parts.append('|\n') 49 | parts.append("+" + "-" * num_cols + "+\n") 50 | return "".join(parts) 51 | 52 | class DefaultZeroDict(dict): 53 | def __missing__(self, item): 54 | return 0 55 | 56 | import unittest 57 | 58 | class Test(unittest.TestCase): 59 | def test_langtons_ant(self): 60 | string = """ 61 | +---------+ 62 | | □ □ □ □ | 63 | | □ □ ■ □ | 64 | | □ ■ ■ □ | 65 | | □ □ □ □ | 66 | +---------+""" 67 | self.assertEqual(langtons_ant(3).strip(), string.strip()) 68 | string = """ 69 | +-------------+ 70 | | □ □ □ □ □ □ | 71 | | □ ■ □ □ □ □ | 72 | | □ ■ □ ■ □ □ | 73 | | □ □ ■ □ ■ □ | 74 | | □ □ □ □ ■ □ | 75 | | □ □ ■ ■ □ □ | 76 | | □ □ □ □ □ □ | 77 | +-------------+""" 78 | self.assertEqual(langtons_ant(22).strip(), string.strip()) 79 | string = """ 80 | +-------------------------------------+ 81 | | □ □ □ □ □ □ □ □ □ □ □ □ □ □ □ □ □ □ | 82 | | □ □ □ □ □ □ □ □ □ □ □ □ ■ ■ □ □ □ □ | 83 | | □ □ □ □ □ □ □ □ □ □ □ □ □ ■ ■ □ □ □ | 84 | | □ □ □ □ □ □ □ □ □ □ ■ □ ■ ■ □ ■ □ □ | 85 | | □ □ □ ■ ■ □ □ □ □ ■ ■ □ ■ □ □ ■ □ □ | 86 | | □ □ ■ □ □ □ ■ ■ ■ ■ ■ □ ■ □ ■ □ □ □ | 87 | | □ ■ ■ ■ □ □ □ □ □ ■ ■ □ ■ □ □ □ □ □ | 88 | | □ ■ □ ■ □ ■ □ □ ■ ■ □ □ ■ ■ □ ■ □ □ | 89 | | □ □ □ □ □ ■ ■ ■ □ ■ □ ■ ■ □ ■ □ ■ □ | 90 | | □ □ □ □ □ □ ■ ■ □ ■ □ ■ □ □ □ □ ■ □ | 91 | | □ □ □ □ ■ □ ■ □ ■ □ ■ ■ □ □ □ ■ □ □ | 92 | | □ □ ■ □ ■ □ ■ □ ■ □ ■ □ □ □ ■ □ □ □ | 93 | | □ ■ □ □ ■ □ ■ ■ □ □ ■ □ ■ ■ ■ □ □ □ | 94 | | □ ■ □ ■ ■ □ ■ ■ □ □ □ ■ ■ ■ ■ ■ □ □ | 95 | | □ □ ■ ■ □ ■ □ ■ □ □ ■ ■ ■ ■ ■ ■ □ □ | 96 | | □ □ □ ■ ■ ■ □ □ ■ □ ■ □ ■ □ □ □ □ □ | 97 | | □ □ □ □ □ ■ □ ■ ■ ■ □ ■ □ □ ■ □ □ □ | 98 | | □ □ □ □ ■ □ □ □ ■ □ □ ■ □ ■ ■ □ □ □ | 99 | | □ □ □ □ ■ □ □ □ □ □ □ □ □ ■ □ □ □ □ | 100 | | □ □ □ □ □ ■ □ □ □ □ □ □ ■ ■ ■ □ □ □ | 101 | | □ □ □ □ □ □ ■ ■ ■ ■ ■ ■ □ □ □ □ □ □ | 102 | | □ □ □ □ □ □ □ □ □ □ □ □ □ □ □ □ □ □ | 103 | +-------------------------------------+ 104 | """ 105 | self.assertEqual(langtons_ant(1000).strip(), string.strip()) 106 | 107 | if __name__ == "__main__": 108 | unittest.main() 109 | 110 | -------------------------------------------------------------------------------- /ch-16-moderate/23-rand7-from-rand5.py: -------------------------------------------------------------------------------- 1 | # Implement rand7 from rand5. 2 | 3 | def rand7(): 4 | rand14 = rand5() 5 | while not rand14: 6 | rand14 = rand5() 7 | if rand14 % 2: 8 | offset = 5 9 | else: 10 | offset = 0 11 | rand09 = rand5() + offset 12 | if rand09 < 7: 13 | return rand09 14 | else: 15 | return rand7() 16 | 17 | MAPPING = {(0,0): 0, (0,1): 0, (0,2): 0, (0,3): 1, (0,4): 1, (1,0): 1, 18 | (1,1): 2, (1,2): 2, (1,3): 2, (1,4): 3, (2,0): 3, (2,1): 3, 19 | (2,2): 4, (2,3): 4, (2,4): 4, (3,0): 5, (3,1): 5, (3,2): 5, 20 | (3,3): 6, (3,4): 6, (4,0): 6, (4,1): 7, (4,2): 7, (4,3): 7, 21 | (4,4): 7} 22 | 23 | # This version is faster. 24 | def alt_rand7(): 25 | pair = (rand5(), rand5()) 26 | value = MAPPING[pair] 27 | if value < 7: 28 | return value 29 | else: 30 | return alt_rand7() 31 | 32 | import unittest 33 | import random 34 | 35 | random.seed = 0 36 | 37 | def rand5(): 38 | return random.randint(0, 4) 39 | 40 | class Counter(dict): 41 | def __missing__(self, item): 42 | return 0 43 | 44 | class Test(unittest.TestCase): 45 | def test_rand7(self): 46 | talleys = Counter() 47 | for _ in xrange(70000): 48 | talleys[rand7()] += 1 49 | self.assertEqual(talleys[-1], 0) 50 | self.assertEqual(talleys[7], 0) 51 | for i in xrange(7): 52 | self.assertTrue(talleys[i] > 9000) 53 | self.assertTrue(talleys[i] < 11000) 54 | 55 | def test_alt_rand7(self): 56 | talleys = Counter() 57 | for _ in xrange(70000): 58 | talleys[alt_rand7()] += 1 59 | self.assertEqual(talleys[-1], 0) 60 | self.assertEqual(talleys[7], 0) 61 | for i in xrange(7): 62 | self.assertTrue(talleys[i] > 9000) 63 | self.assertTrue(talleys[i] < 11000) 64 | 65 | if __name__ == "__main__": 66 | unittest.main() 67 | 68 | -------------------------------------------------------------------------------- /ch-16-moderate/24-pairs-with-sum.py: -------------------------------------------------------------------------------- 1 | # Return the pairs with a given sum. 2 | 3 | def pairs_with_sum(arr, s): 4 | values = set() 5 | for elem in arr: 6 | values.add(elem) 7 | pairs = set() 8 | for elem in arr: 9 | if (s - elem) in values: 10 | values.remove(elem) 11 | pairs.add((elem, s - elem)) 12 | return pairs 13 | 14 | import unittest 15 | 16 | class Test(unittest.TestCase): 17 | def test_pairs_with_sum(self): 18 | arr = [2, 3, 4, 11, -4] 19 | self.assertEqual(pairs_with_sum(arr, 7), {(3,4),(11,-4)}) 20 | arr = [0, 11, 22, 33, 44, 55, 66, 77, 88, 99, 10, 20, 30, -11] 21 | self.assertEqual(pairs_with_sum(arr, 55), {(0,55),(22,33),(66,-11),(11,44)}) 22 | 23 | if __name__ == "__main__": 24 | unittest.main() 25 | 26 | -------------------------------------------------------------------------------- /ch-16-moderate/25-lru-cache.py: -------------------------------------------------------------------------------- 1 | # Implement a least-recently-used cache. 2 | 3 | class LRUCache(object): 4 | def __init__(self, capacity): 5 | self.items = DefaultNoneDict() 6 | self.ages = [None] # A min heap. 7 | self.time = 0 8 | self.capacity = capacity 9 | 10 | def add(self, key, value): 11 | self.ages.append((key, self.time)) 12 | self.time += 1 13 | ix = len(self.ages) - 1 14 | self.items[key] = (value, ix) 15 | if ix > self.capacity: 16 | self.evict_one() 17 | 18 | def lookup(self, key): 19 | value_and_ages_ix = self.items[key] 20 | if value_and_ages_ix is None: 21 | return None 22 | (value, ix) = value_and_ages_ix 23 | self.ages[ix] = (key, self.time) 24 | self.bubble_down(ix) 25 | self.time += 1 26 | return value 27 | 28 | def evict_one(self): 29 | key = self.ages[1][0] 30 | del self.items[key] 31 | self.ages[1] = self.ages.pop() 32 | self.bubble_down(1) 33 | 34 | def bubble_down(self, ix): 35 | while 2 * ix < len(self.ages): 36 | next_ix = 2 * ix 37 | if next_ix + 1 < len(self.ages): 38 | if self.ages[next_ix + 1][1] < self.ages[next_ix][1]: 39 | next_ix += 1 40 | if self.ages[next_ix][1] > self.ages[ix][1]: 41 | break 42 | self.ages[ix], self.ages[next_ix] = self.ages[next_ix], self.ages[ix] 43 | key = self.ages[ix][0] 44 | (value, _) = self.items[key] 45 | self.items[key] = (value, ix) 46 | key = self.ages[next_ix][0] 47 | (value, _) = self.items[key] 48 | self.items[key] = (value, next_ix) 49 | ix = next_ix 50 | 51 | class DefaultNoneDict(dict): 52 | def __missing__(self, item): 53 | return None 54 | 55 | import unittest 56 | 57 | class Test(unittest.TestCase): 58 | def test_lru_cache(self): 59 | cache = LRUCache(4) 60 | cache.add('food', 'lasagna') 61 | cache.add('drink', 'orange juice') 62 | cache.add('color', 'green') 63 | cache.add('dance', 'bachata') 64 | self.assertEqual(cache.ages, 65 | [None, ('food', 0), ('drink', 1), ('color', 2), ('dance', 3)]) 66 | cache.add('sport', 'ultimate') 67 | self.assertEqual(cache.ages, 68 | [None, ('drink', 1), ('dance', 3), ('color', 2), ('sport', 4)]) 69 | self.assertEqual(cache.lookup('dance'), 'bachata') 70 | self.assertEqual(cache.ages, 71 | [None, ('drink', 1), ('sport', 4), ('color', 2), ('dance', 5)]) 72 | self.assertEqual(cache.lookup('food'), None) 73 | cache.add('spice', 'paprika') 74 | self.assertEqual(cache.ages, 75 | [None, ('color', 2), ('sport', 4), ('spice', 6), ('dance', 5)]) 76 | self.assertEqual(cache.lookup('drink'), None) 77 | self.assertEqual(cache.lookup('color'), 'green') 78 | self.assertEqual(cache.ages, 79 | [None, ('sport', 4), ('dance', 5), ('spice', 6), ('color', 7)]) 80 | 81 | if __name__ == "__main__": 82 | unittest.main() 83 | 84 | -------------------------------------------------------------------------------- /ch-16-moderate/26-calculator.py: -------------------------------------------------------------------------------- 1 | # Implement a simple calculator. 2 | 3 | def calculate(string): 4 | #return eval(string) 5 | tokens = tokenize(string) 6 | if len(tokens) == 0: 7 | return None 8 | products = [] 9 | i = 0 10 | while i < len(tokens): 11 | if tokens[i] == "*": 12 | products[-1] *= tokens[i+1] 13 | i += 2 14 | elif tokens[i] == "/": 15 | products[-1] /= tokens[i+1] 16 | i += 2 17 | else: 18 | products.append(tokens[i]) 19 | i += 1 20 | result = products[0] 21 | for i in xrange(1, len(products) - 1, 2): 22 | if products[i] == "+": 23 | result += products[i+1] 24 | elif products[i] == "-": 25 | result -= products[i+1] 26 | else: 27 | raise Exception("Unknown operation.") 28 | return result 29 | 30 | def tokenize(string): 31 | number_start = 0 32 | tokens = [] 33 | for i, char in enumerate(string): 34 | if char in "+-*/": 35 | if number_start == i: 36 | raise Exception("Missing number between operators") 37 | tokens.append(int(string[number_start:i])) 38 | tokens.append(char) 39 | number_start = i+1 40 | elif not char in "0123456789": 41 | raise Exception("Unknown character") 42 | tokens.append(int(string[number_start:])) 43 | return tokens 44 | 45 | import unittest 46 | 47 | class Test(unittest.TestCase): 48 | def test_calculate(self): 49 | self.assertEqual(calculate("1+1"), 2) 50 | self.assertEqual(calculate("0+4"), 4) 51 | self.assertEqual(calculate("0*7"), 0) 52 | self.assertEqual(calculate("9*0+1"), 1) 53 | self.assertEqual(calculate("1+1+1"), 3) 54 | self.assertEqual(calculate("1+6/5"), 2) 55 | self.assertEqual(calculate("3+7/8*7"), 3) 56 | self.assertEqual(calculate("1+11"), 12) 57 | self.assertEqual(calculate("200+423"), 623) 58 | 59 | if __name__ == "__main__": 60 | unittest.main() 61 | 62 | -------------------------------------------------------------------------------- /ch-17-hard/01-add-without-plus.py: -------------------------------------------------------------------------------- 1 | # Add two numbers without using +. 2 | 3 | import ctypes 4 | 5 | def add_without_plus(a, b): 6 | both_bits = a & b 7 | single_bits = a ^ b 8 | if both_bits == 0: 9 | return single_bits 10 | carry_bits = (both_bits << 1) & 0x1FFFFFFFFFFFFFFFF 11 | if carry_bits & (1 << 64): 12 | return ctypes.c_long(single_bits).value 13 | return add_without_plus(single_bits, carry_bits) 14 | 15 | import unittest 16 | 17 | class Test(unittest.TestCase): 18 | def test_add_without_plus(self): 19 | self.assertEqual(add_without_plus(1, 1), 2) 20 | self.assertEqual(add_without_plus(1, 2), 3) 21 | self.assertEqual(add_without_plus(1001, 234), 1235) 22 | self.assertEqual(add_without_plus(5, -1), 4) 23 | self.assertEqual(add_without_plus(7,-5), 2) 24 | self.assertEqual(add_without_plus(7,-29), -22) 25 | self.assertEqual(add_without_plus(-2,10), 8) 26 | 27 | if __name__ == "__main__": 28 | unittest.main() 29 | 30 | --------------------------------------------------------------------------------