def fib_table(output=True, decimals=3): """Generate table showing reduced recursive invocations of fibonacci.""" import math tbl = DataTable([8, 12, 12], ['N', 'FiRec', 'Model'], output=output, decimals=decimals) tbl.format('FiRec', 'd') def exp_model(n, a, b): """Formula for A*N^B .""" return a * math.pow(n, b) for n in range(3, 100): old = numRecursiveImproved[0] fib_with_lucas(n) model = exp_model(n, 0.28711343, 2.58031481) tbl.row([n, (numRecursiveImproved[0] - old), model]) if numpy_error: pass else: import numpy as np from scipy.optimize import curve_fit x_arr = np.array(tbl.column(tbl.labels[0])) y_arr = np.array(tbl.column(tbl.labels[1])) def np_exp_model(n, a, b): """Formula for A*N^B .""" return a * np.power(n, b) if output: [exp_coeffs, _] = curve_fit(np_exp_model, x_arr, y_arr) print('A*N^B = {:.12f}*N^{:f} '.format(exp_coeffs[0], exp_coeffs[1])) return tbl
def test_table(self): tbl = DataTable([8, 8, 8], ['N', 'Another', 'SquareRoot'], output=False, decimals=4) tbl.format('Another', 'd') for n in range(2, 10): tbl.row([n, n, n**0.5]) self.assertEqual(tbl.entry(3, 'Another'), 3) print('Testing that Table is print to console') tbl = DataTable([8, 8, 8], ['N', 'Another', 'SquareRoot'], decimals=4) tbl.format('Another', 'd') for n in range(2, 10): tbl.row([n, n, n**0.5]) self.assertEqual(list(range(2, 10)), tbl.column('Another')) model = tbl.best_model('Another')[0] if numpy_error: pass else: self.assertEqual(model[0], Model.LINEAR) self.assertAlmostEqual(model[3], 1.0000, places=5)
def test_allpairs_sp(self): from ch07.all_pairs_sp import floyd_warshall, all_pairs_path_to G = nx.Graph() G.add_edge('a', 'b', weight=3) G.add_edge('a', 'c', weight=5) G.add_edge('b', 'c', weight=9) G.add_edge('b', 'd', weight=2) G.add_edge('d', 'c', weight=1) G.add_edge('e', 'f', weight=1) # separate and disconnected edge... (dist_to, node_from) = floyd_warshall(G) path = all_pairs_path_to(node_from, 'b', 'c') self.assertEqual(3, dist_to['b']['c']) self.assertEqual(['b', 'd', 'c'], path) path = all_pairs_path_to(node_from, 'a', 'd') self.assertEqual(5, dist_to['a']['d']) self.assertEqual(['a', 'b', 'd'], path) with self.assertRaises(ValueError): all_pairs_path_to(node_from, 'a', 'e') tbl = DataTable([6, 6, 6, 6, 6], ['.', 'a', 'b', 'c', 'd'], output=False) tbl.format('.', 's') for f in 'abcd': tbl.format(f, 's') for u in 'abcd': row = [u] for v in 'abcd': if node_from[u][v]: row.append(node_from[u][v]) else: row.append(SKIP) tbl.row(row) self.assertEqual('d', tbl.entry('b', 'c'))
def time_results_open_addressing(num_rows=0, output=True, decimals=3): """Average time to insert a key in growing hashtable_open (in microseconds).""" sizes = [8192, 16384, 32768, 65536, 131072, 262144, 524288, 1048576] headers = [comma(s) for s in sizes] headers.insert(0,'N') tbl = DataTable([8,8,8,8,8,8,8,8,10], headers, output=output, decimals=decimals) # Now start with M words to be added into a table of size N. # Start at 1000 and work up to 2000 for num_to_add in [32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768]: all_words = english_words()[:num_to_add] line = [len(all_words)] for size in sizes: try: tbl.format(comma(size), '.3f') timing = min(timeit.repeat(stmt=''' table = Hashtable({}) for word in all_words: table.put(word, 99)'''.format(size), setup=''' from ch03.hashtable_open import Hashtable from resources.english import english_words all_words=english_words()[:{}]'''.format(num_to_add),repeat=1,number=100)) timing = (100000.0 * timing) / size except RuntimeError: timing = SKIP line.append(timing) num_rows -= 1 tbl.row(line) # Provide effective way to terminate early for testing. if num_rows == 0: break return tbl
def generate_hash(): """Results are different each time since Python salts hash values.""" s = 'a rose by any other name would smell as sweet' tbl = DataTable([8,20,20], ['key', 'hash(key)', 'hash(key) % 15']) tbl.format('key', 's') tbl.format('hash(key)', 'd') tbl.format('hash(key) % 15', 'd') for w in s.split(): tbl.row([w, hash(w), hash(w) % 15]) return tbl
def growth_table(output=True): """Generate table for growth of different computations.""" labels = ['N', 'log N', 'Linear', 'N log N', 'N^2', 'N^3', '2^N', 'N!'] tbl = DataTable([15, 15, 15, 15, 15, 15, 15, 15], labels, output=output) for hdr in labels: tbl.format(hdr, ',d') def fact(n): try: return int(math.factorial(n)) except ValueError: return float('inf') for n in [2**k for k in range(2, 12)]: fact_value = fact(n) if fact_value == float('inf'): fact_value = SKIP elif fact_value > 1e100: fact_value = SKIP elif fact_value > 1e8: tbl.format('N!', '.2e') exp_value = pow(2, n) if exp_value > 1e8: tbl.format('2^N', '.2e') if exp_value > 1e100: exp_value = SKIP cubic_value = n * n * n if cubic_value > 1e8: tbl.format('N^3', '.2e') tbl.row([ n, int(math.log(n) / math.log(2)), n, int(n * math.log(n) / math.log(2)), n * n, cubic_value, exp_value, fact_value ]) return tbl
def count_hash(output=True, decimals=2): """ For all English words, starting with a hashtable of size 1,024 and a load factor of 0.75, count how many times the hash code (i.e., %) is invoked. """ from ch03.hashtable_linked import DynamicHashtable ht = DynamicHashtable(1023) tbl = DataTable([20,10,10,10,10],['Word', 'M', 'N', '#insert', 'average'], output=output, decimals=decimals) tbl.format('Word', 's') tbl.format('N', ',d') tbl.format('M', ',d') tbl.format('#insert', ',d') last_word = None for w in english_words(): last_word = w last_m = ht.M last = CountableHash.hash_count ht.put(CountableHash(w), w) if CountableHash.hash_count != last + 1: tbl.row([w, last_m, ht.N, CountableHash.hash_count, CountableHash.hash_count/ht.N]) tbl.row([last_word, last_m, ht.N, CountableHash.hash_count, CountableHash.hash_count/ht.N]) # determine when next resize event would occur... for i in range(1, 200000): last = CountableHash.hash_count last_m = ht.M ht.put(CountableHash(last_word + str(i)), last_word) if CountableHash.hash_count != last + 1: tbl.row([last_word + str(i), last_m, ht.N, CountableHash.hash_count, CountableHash.hash_count/ht.N]) break return tbl
def iteration_order(output=True): """Generate iteration orders for multiple hashtable types.""" s = 'a rose by any other name would smell as sweet' from ch03.hashtable_open import Hashtable as Open_Hashtable from ch03.hashtable_linked import Hashtable as Linked_Hashtable from ch03.hashtable_open_perfect import Hashtable as Perfect_Hashtable ht_oa = Open_Hashtable(13) ht_ll = Linked_Hashtable(13) ht_ph = Perfect_Hashtable() for w in s.split(): ht_oa.put(w, w) ht_ll.put(w, w) ht_ph.put(w, w) tbl = DataTable([8,8,8], ['Open Addressing', 'Separate Chaining', 'Perfect Hash'], output=output) tbl.format('Open Addressing', 's') tbl.format('Separate Chaining', 's') tbl.format('Perfect Hash', 's') for p_oa,p_ll,p_ph in zip(ht_oa, ht_ll, ht_ph): tbl.row([p_oa[0], p_ll[0], p_ph[0]]) return tbl
def measure_performance_resize(max_d=50, output=True): """Generate table of statistics for table resizing up to (but not including maxd=50).""" from ch03.hashtable_linked import DynamicHashtable try: # Added in Python 3.7 from time import time_ns timing = time_ns except ImportError: from time import time timing = time if output: print('Dynamic Resizing Hashtable') tbl = DataTable([8, 15, 15, 10, 10], ['idx', 'word', 'time', 'old-size', 'new-size'], output=output, decimals=2) tbl.format('idx', 'd') tbl.format('word', 's') tbl.format('old-size', ',d') tbl.format('new-size', ',d') ht = DynamicHashtable(1023) idx = 1 last = None average = 0 words = english_words() for w in words: before = timing() old_size = len(ht.table) ht.put(w, w) new_size = len(ht.table) after = timing() average += (after - before) if last: if after - before > last: last = after - before tbl.row([idx, w, last, old_size, new_size]) else: last = after - before idx += 1 average /= len(words) ht = None if output: print('Average was ', average) print('Incremental Resizing Hashtable') tbl_ir = DataTable([8, 15, 15, 10, 10], ['idx', 'word', 'time', 'old-size', 'new-size'], output=output, decimals=2) tbl_ir.format('idx', 'd') tbl_ir.format('word', 's') tbl_ir.format('old-size', ',d') tbl_ir.format('new-size', ',d') ht = DynamicHashtableIncrementalResizing(1023, 10) idx = 1 last = None average = 0 words = english_words() for w in words: before = timing() old_size = len(ht.table) ht.put(w, w) new_size = len(ht.table) after = timing() average += (after - before) if last: if after - before > last: last = after - before tbl_ir.row([idx, w, last, old_size, new_size]) else: last = after - before idx += 1 ht = None average /= len(words) if output: print('Average was ', average) print('Incremental Resizing dependent on Delta') print() tbl_d = DataTable([8, 10], ['Delta', 'Average'], output=output) tbl_d.format('Delta', 'd') for delta in range(1, max_d): ht = DynamicHashtableIncrementalResizing(1023, delta) average = 0 words = english_words() for w in words: before = timing() ht.put(w, w) after = timing() average += (after - before) average /= len(words) tbl_d.row([delta, average]) return (tbl, tbl_ir, tbl_d)
def compare_removes(output=True): """Run trials that create a Hashtable and then remove all entries.""" build_time_oa = min( timeit.repeat(stmt=''' ht = HashtableOpenAddressingRemove(8) for w in words: ht.put(w,w)''', setup=''' from ch03.challenge import HashtableOpenAddressingRemove from resources.english import english_words words = english_words()[:20000]''', repeat=5, number=2)) / 2 build_time_sc = min( timeit.repeat(stmt=''' ht = Hashtable(8) for w in words: ht.put(w,w)''', setup=''' from ch03.hashtable_linked import Hashtable from resources.english import english_words words = english_words()[:20000]''', repeat=5, number=2)) / 2 delete_time_oa = min( timeit.repeat(stmt=''' for w in to_remove: ht.remove(w) if ht.N != 0: raise RuntimeError("should have emptied")''', setup=''' import random from ch03.challenge import HashtableOpenAddressingRemove from resources.english import english_words words = english_words()[:20000] ht = HashtableOpenAddressingRemove(8) for w in words: ht.put(w,w) to_remove = list(words) random.shuffle(to_remove)''', repeat=5, number=2)) / 2 delete_time_sc = min( timeit.repeat(stmt=''' for w in to_remove: ht.remove(w) if ht.N != 0: raise RuntimeError("should have emptied")''', setup=''' import random from ch03.hashtable_linked import Hashtable from resources.english import english_words words = english_words()[:20000] ht = Hashtable(8) for w in words: ht.put(w,w) to_remove = list(words) random.shuffle(to_remove)''', repeat=5, number=2)) / 2 tbl = DataTable([20, 8, 8], ['Hashtable Type', 'Build', 'Remove All'], output=output) tbl.format('Hashtable Type', 's') tbl.row(['Open Addressing:', build_time_oa, delete_time_oa]) tbl.row(['Separate Chaining:', build_time_sc, delete_time_sc]) return tbl
def compare_dynamic_build_and_access_time(repeat=10, num=5, max_m=640000, output=True): """Generate tables for build and access times for M up to (but not equal to) 640,000.""" # sufficient to allow 321,129 and to spare (divide by 0.75 to get 428,172). SUFF=428172 # When 'ht = HTLL(...) is inside the STMT, it measures BUILD TIME. # When it is included in the setup, we are measuring ACCESS TIME. ll_build = min(timeit.repeat(stmt=''' ht = HTLL({}) for w in words: ht.put(w,w)'''.format(SUFF), setup=''' from ch03.hashtable_linked import Hashtable as HTLL from resources.english import english_words words = english_words()''', repeat=repeat, number=num))/num ll_access = min(timeit.repeat(stmt=''' for w in words: ht.get(w)''', setup=''' from ch03.hashtable_linked import Hashtable as HTLL from resources.english import english_words ht = HTLL({}) words = english_words() for w in words: ht.put(w,w)'''.format(SUFF), repeat=repeat, number=num))/num oa_build = min(timeit.repeat(stmt=''' ht = HTOA({}) for w in words: ht.put(w,w)'''.format(SUFF), setup=''' from ch03.hashtable_open import Hashtable as HTOA from resources.english import english_words words = english_words()''', repeat=repeat, number=num))/num oa_access = min(timeit.repeat(stmt=''' for w in words: ht.get(w)''', setup=''' from ch03.hashtable_open import Hashtable as HTOA from resources.english import english_words ht = HTOA({}) words = english_words() for w in words: ht.put(w,w)'''.format(SUFF), repeat=repeat, number=num))/num tbl = DataTable([8,10,10,10,10],['M', 'BuildLL', 'AccessLL', 'BuildOA', 'AccessOA'], output=output, decimals=3) M = 625 while M <= max_m: t1_build = min(timeit.repeat(stmt=''' ht = DHL({}) for w in words: ht.put(w,w)'''.format(M), setup=''' from ch03.hashtable_linked import DynamicHashtable as DHL from resources.english import english_words words = english_words()''', repeat=repeat, number=num))/num t1_access = min(timeit.repeat(stmt=''' for w in words: ht.get(w)''', setup=''' from ch03.hashtable_linked import DynamicHashtable as DHL from resources.english import english_words ht = DHL({}) words = english_words() for w in words: ht.put(w,w)'''.format(M), repeat=repeat, number=num))/num t2_build = min(timeit.repeat(stmt=''' ht = DHL({}) for w in words: ht.put(w,w)'''.format(M), setup=''' from ch03.hashtable_open import DynamicHashtable as DHL from resources.english import english_words words = english_words()''', repeat=repeat, number=num))/num t2_access = min(timeit.repeat(stmt=''' for w in words: ht.get(w)''', setup=''' from ch03.hashtable_open import DynamicHashtable as DHL from resources.english import english_words ht = DHL({}) words = english_words() for w in words: ht.put(w,w)'''.format(M), repeat=repeat, number=num))/num tbl.row([M, t1_build, t1_access, t2_build, t2_access]) M = M * 2 tbl.format('M', 's') tbl.row(['Fixed', ll_build, ll_access, oa_build, oa_access]) return tbl
def trial_multiple_rotations(output=True, num_attempts=10000): """Some trial and error went into these ranges.""" from ch05.challenge import fib tbl = DataTable([6, 6, 6, 6], ['NumRot', 'Height', 'N', 'Random Tree'], output=output) tbl.format('Random Tree', 's') tbl.format('NumRot', 'd') tbl.format('Height', 'd') tbl.format('N', 'd') for extra in range(3): (structure, _) = find_multiple_rotations(extra, lo=4, hi=40, num_attempts=num_attempts, output=False) n = recreate_tree(structure) def count_nodes(n): if n is None: return 0 return 1 + count_nodes(n.left) + count_nodes(n.right) tbl.row([extra + 1, n.height, count_nodes(n), structure]) # Now use Fibonacci Trees to accomplish the same result. if output: print() tbl = DataTable([6, 6, 6, 13], ['NumRot', 'Height', 'N', 'Fib AVL Trees'], output=output) tbl.format('Fib AVL Trees', 's') tbl.format('NumRot', 'd') tbl.format('Height', 'd') tbl.format('N', 'd') for n in range(6, 14, 2): root = fibonacci_avl(n) root.compute_height() check_avl_property(root) # double-check structure = tree_structure(root) bt = ObservableBinaryTree() height = root.height bt.root = root count = count_nodes(root) num_rotations = rotations[0] to_delete = fib(n + 1) - 1 bt.remove(to_delete) check_avl_property(bt.root) num_rotations = rotations[0] - num_rotations tbl.row([num_rotations, height, count, structure]) return tbl