forked from heineman/LearningAlgorithms
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathchallenge.py
More file actions
336 lines (285 loc) · 9.67 KB
/
challenge.py
File metadata and controls
336 lines (285 loc) · 9.67 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
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
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
"""
Challenge Exercises for Chapter 2.
"""
import timeit
import math
from algs.table import DataTable, ExerciseNum, caption
from algs.modeling import quadratic_model, log_model, numpy_error
def fragment_1(N):
"""Fragment-1 for exercise."""
ct = 0
for _ in range(100):
for _ in range(N):
for _ in range(10000):
ct += 1
return ct
def fragment_2(N):
"""Fragment-2 for exercise."""
ct = 0
for _ in range(N):
for _ in range(N):
for _ in range(100):
ct += 1
return ct
def fragment_3(N):
"""Fragment-3 for exercise."""
ct = 0
for _ in range(0,N,2):
for _ in range(0,N,2):
ct += 1
return ct
def fragment_4(N):
"""Fragment-4 for exercise."""
ct = 0
while N > 1:
ct += 1
N = N // 2
return ct
def fragment_5(N):
"""Fragment-5 for exercise."""
ct = 0
for _ in range(2,N,3):
for _ in range(3,N,2):
ct += 1
return ct
def f4(N):
"""Fragment for exercise."""
ct = 1
while N >= 2:
ct = ct + 1
N = N ** 0.5
return ct
def fragment_counting(max_k=10, output=True):
"""Generate table for counts of fragments up to (but including) 2**max_k."""
trials = [2**k for k in range(5,max_k)]
tbl = DataTable([8,15,8,8,8,8],['N', 'F1', 'F2', 'F3', 'F4', 'F5'], output=output)
for i in range(1,6):
tbl.format('F{}'.format(i), 'd')
for N in trials:
tbl.row([N, fragment_1(N), fragment_2(N), fragment_3(N), fragment_4(N), fragment_5(N)])
return tbl
def another_fragment_counting(max_k=20, output=True):
"""Generate table for counts of fragments up to (but including) 2**max_k."""
if numpy_error:
a = 0,0
else:
import numpy as np
from scipy.optimize import curve_fit
def log_log_model(n, a):
"""Formula for A*Log_2(Log_2(N)) with single coefficient."""
logn = np.log2(n)
return a*np.log2(logn)
# Train Model
trials = [2**k for k in range(5,15)]
nvals = []
yvals = []
for N in trials:
nvals.append(N)
yvals.append(f4(N))
[a, _] = curve_fit(log_log_model, np.array(nvals), np.array(yvals))
if output:
print('LOG_LOG_MODEL = {}*log(log(N))'.format(a))
trials = [2**k for k in range(5, max_k)]
tbl = DataTable([8,8,8],['N', 'F4', 'Model'], output=output)
tbl.format('F4', 'd')
for N in trials:
tbl.row([N, f4(N), a[0]*math.log2(math.log2(N))])
return tbl
def factorial_model(n, a):
"""Formula for A*N! with single coefficient."""
if numpy_error:
return a*math.factorial(n)
from scipy.special import factorial
return a*factorial(n)
def max_sort(A):
"""Evaluate the space complexity of this sorting algorithm."""
result = []
while len(A) > 1:
index_max = max(range(len(A)), key=A.__getitem__)
result.insert(0, A[index_max])
A = list(A[:index_max]) + list(A[index_max+1:])
return A + result
def run_max_sort_worst_case(max_k=14, output=True, decimals=4):
"""Generate table for max sort up to (but not including 2**max_k)."""
xvals = []
yvals = []
for n in [2 ** k for k in range(5, 12)]:
sort_time = timeit.timeit(stmt='max_sort(x)', setup='''
from ch02.challenge import max_sort
import random
x=list(range({},0,-1))
random.shuffle(x)'''.format(n), number=10)
xvals.append(n)
yvals.append(sort_time)
if numpy_error:
quadratic_coeff = [0, 0]
else:
import numpy as np
from scipy.optimize import curve_fit
[quadratic_coeff, _] = curve_fit(quadratic_model, np.array(xvals), np.array(yvals))
if output:
print('Quadratic N = {:.12f}*N*N + {:.12f}*N'.format(quadratic_coeff[0], quadratic_coeff[1]))
tbl = DataTable([8,8,8], ['N', 'MaxSort', 'Model'], output=output, decimals=decimals)
for n in [2 ** k for k in range(5, max_k)]:
sort_time = timeit.timeit(stmt='max_sort(x)', setup='''
from ch02.challenge import max_sort
import random
x=list(range({},0,-1))
random.shuffle(x)'''.format(n), number=10)
tbl.row([n, sort_time, quadratic_model(n, quadratic_coeff[0], quadratic_coeff[1])])
return tbl
def run_permutation_sort(max_n=12, output=True, decimals=4):
"""Generate table for permutation sort up to (but not including) max_n."""
xvals = []
yvals = []
for n in range(1,6):
sort_time = timeit.timeit(stmt='permutation_sort(x)', setup='''
from ch02.random_sort import permutation_sort
x=list(range({},0,-1))'''.format(n), number=10)
xvals.append(n)
yvals.append(sort_time)
if numpy_error:
factorial_coeff = [0]
else:
import numpy as np
from scipy.optimize import curve_fit
[factorial_coeff, _] = curve_fit(factorial_model, np.array(xvals), np.array(yvals))
if output:
print('Factorial N = {:.12f}*N! '.format(factorial_coeff[0]))
print('Estimated time to sort 20 values is {:,.2f} years'.format(
factorial_model(20, factorial_coeff[0])/(60*60*24*365)))
tbl = DataTable([8,8,8], ['N', 'PermutationSort', 'Model'],
output=output, decimals=decimals)
for n in range(max_n):
sort_time = timeit.timeit(stmt='permutation_sort(x)', setup='''
from ch02.random_sort import permutation_sort
x=list(range({},0,-1))'''.format(n), number=10)
tbl.row([n, sort_time, factorial_model(n, factorial_coeff[0])])
return tbl
def performance_bas(max_k=22, output=True, decimals=3):
"""
Generate performance tables for binary array search up to (but not including)
2**max_k.
"""
# Train on five values...
trials = [2**k for k in range(5,12)]
xvals = []
yvals = []
num = 50000
for n in trials:
search_time = timeit.timeit(stmt='binary_array_search(x, random.randint(0,{}*4))'.format(n),
setup='''
import random
from ch02.bas import binary_array_search
x=sorted(random.sample(range({0}*4), {0}))'''.format(n), number=num)
xvals.append(n)
yvals.append(search_time)
if numpy_error:
log_coeff = [0]
else:
import numpy as np
from scipy.optimize import curve_fit
[log_coeff, _] = curve_fit(log_model, np.array(xvals), np.array(yvals))
if output:
print('Log N = {:.12f}*log2(N)'.format(log_coeff[0]))
tbl = DataTable([15, 10, 10], ['N', 'T(N)', 'Model'], output=output, decimals=decimals)
trials = [2**k for k in range(5,max_k)]
for n in trials:
search_time = timeit.timeit(stmt='binary_array_search(x, random.randint(0,{}*2))'.format(n),
setup='''
import random
from ch02.bas import binary_array_search
x=sorted(random.sample(range({0}*4), {0}))'''.format(n), number=num)
tbl.row([n, search_time, log_model(n, log_coeff[0])])
return tbl
def worst_range(A, target):
"""
Given a sorted list, find the range A[lo:hi] such that all values in the range
equal to target.
"""
if not target in A:
return None
lo = A.index(target)
if lo == len(A)-1:
return (lo, lo)
hi = lo + 1
while hi < len(A):
if A[hi] != target:
return (lo, hi-1)
hi += 1
return (lo, hi-1)
def best_range(A, target):
"""
Given a sorted list, find the range A[lo:hi] such that all values in the range
equal to target.
"""
if len(A) == 0:
return None
lo = 0
hi = len(A)-1
while lo <= hi:
mid = (lo + hi) // 2
if target < A[mid]:
hi = mid-1
elif target > A[mid]:
lo = mid+1
else:
break
if lo > hi:
return None
# find left-edge of range. This time use < to exit loop when on final one
left_hi = mid - 1
while lo < left_hi:
m = (lo + left_hi) // 2
if target == A[m]: # keep going to the left
left_hi = m-1
else:
lo = m + 1
if A[lo] != target:
lo += 1
# find right-edge of range. This time use < to exit loop when on final one
right = mid + 1
while right < hi:
m = (right + hi) // 2
if target == A[m]: # keep going to the right
right = m+1
else:
hi = m - 1
if right == len(A) or A[right] != target:
right -= 1
return (lo, right)
#######################################################################
if __name__ == '__main__':
chapter = 2
with ExerciseNum(1) as exercise_number:
fragment_counting()
print(caption(chapter, exercise_number),
'Fragment Evaluation')
with ExerciseNum(2) as exercise_number:
another_fragment_counting()
print(caption(chapter, exercise_number),
'Second Fragment Evaluation')
print()
with ExerciseNum(3) as exercise_number:
run_permutation_sort()
print(caption(chapter, exercise_number),
'Permutation Sort Exercise')
print()
with ExerciseNum(4) as exercise_number:
performance_bas()
print(caption(chapter, exercise_number),
'Binary Array Search Evidence')
print()
with ExerciseNum(5) as exercise_number:
run_max_sort_worst_case()
print(caption(chapter, exercise_number),
'Max sort')
print()
with ExerciseNum(6) as exercise_number:
print(caption(chapter, exercise_number),
'Galactic algorithms')
print()
with ExerciseNum(7) as exercise_number:
print(caption(chapter, exercise_number),
'Performance Measurements')
print()