1 # C-4.9
2 import os
3
4
5 def find_max_min_num(S, index=0):
6 if index == len(S) - 1:
7 return S[index], S[index]
8 else:
9 max_value, min_value = find_max_min_num(S, index+1)
10 return max(S[index], max_value), min(S[index], min_value)
11
12 #
13 # test_S = [1, 2, 3, 5, 4, 7, 3]
14 # max_val = find_max_min_num(test_S)
15 # print(max_val)
16
17
18 # C-4.10
19 def find_log_int(num):
20 if num // 2 < 1:
21 return 0
22 if num // 2 >= 1:
23 return 1 + find_log_int(num // 2)
24
25
26 # print(find_log_int(7))
27
28
29 # C-4.11
30 def confirm_unique(data):
31 if len(data) == 1:
32 return True
33 else:
34 if data[0] in data[1:]:
35 return False
36 else:
37 return confirm_unique(data[1:])
38
39
40 # print(confirm_unique([1, 2, 3, 4, 5, 6, 3]))
41
42
43 # C-4.12
44 def calculate_multiplication(m, n):
45 if m == 1:
46 return n
47 else:
48 return n + calculate_multiplication(m-1, n)
49
50
51 # print(calculate_multiplication(3, 5))
52
53
54 # C-4.14
55 def han_nuo_ta(n):
56 if n == 1:
57 return 1
58 else:
59 return han_nuo_ta(n-1) + 1 + han_nuo_ta(n-1)
60
61
62 # print(han_nuo_ta(5))
63
64
65 # ---------C4-14-----------------------
66 import time
67 from IPython.display import clear_output
68
69
70 class TowerOfHanoi():
71 def __init__(self, n=4):
72 self._n = n
73 self._array = [[], [], list(reversed(range(n)))]
74 self._lengths = [0, 0, n]
75
76 def draw_towers(self):
77 rows = []
78 rows.append(['\t 1\t', '\t 2\t', '\t 3\t'])
79 rows.append(['\t_____\t', '\t_____\t', '\t_____\t'])
80 for i in range(max(self._lengths)):
81 row = []
82 for j in range(3):
83 if i < self._lengths[j]:
84 row.append('\t ' + str(self._array[j][i]) + '\t')
85 else:
86 row.append('\t \t')
87 rows.append(row)
88
89 for r in reversed(rows):
90 print(''.join(r))
91
92 def __getitem__(self, index):
93 return self._array[index]
94
95 def pop(self, index):
96 self._lengths[index] -= 1
97 return self[index].pop()
98
99 def getlen(self):
100 return self._lengths
101
102 def __setitem__(self, index, value):
103 if self[index] and self[index][-1] < value:
104 raise ValueError(f'Illegal move. Cannot place block with size {value} on block {self[index][-1]}')
105 else:
106 self[index].append(value)
107 self._lengths[index] += 1
108
109 def _move_stack(self, n_disks, start_peg, help_peg, target_peg):
110 time.sleep(0.5)
111 clear_output()
112 self.draw_towers()
113
114 if n_disks == 1:
115 self._count += 1
116 value = self.pop(start_peg)
117 try:
118 self[target_peg] = value
119 except Exception as e:
120 print(e)
121 self[start_peg]
122
123 else:
124 # Move the upper stack to the helper peg
125 self._move_stack(n_disks - 1, start_peg, target_peg, help_peg)
126 # Move the lowest item to the target peg
127 self._move_stack(1, start_peg, help_peg, target_peg)
128 # Move the upper stack to the target peg
129 self._move_stack(n_disks - 1, help_peg, start_peg, target_peg)
130
131 def solve_hanoi(self):
132 self._count = 0
133 self._move_stack(self.getlen()[2], 2, 1, 0)
134
135 time.sleep(0.5)
136 clear_output()
137 self.draw_towers()
138
139 print(f'\nThis took a total of {self._count} moves!')
140
141
142 # t = TowerOfHanoi(5)
143 # t.solve_hanoi()
144
145 # ---------------C4-15-----------------------------
146 """
147 Here, we want combinations, not permutations
148
149
150 We rely on the addition of an empty set value 'UNK'
151
152 For each stage of the recursion, we add one more value from the sequence
153 and for each current item either add another UNK or that value
154
155 The final layer of that tree will contain exactly one copy of each unique set
156
157 """
158 UNK = chr(1000)
159
160
161 def sub_rec(U, S):
162 """
163 U is the current set
164 S is the remaining sequence
165 """
166 if len(S) == 0:
167 print('{', str([x for x in U if x != UNK])[1:-1], '}')
168
169 else:
170 val = S.pop()
171 U.append(UNK)
172 sub_rec(U, S)
173 U.pop()
174
175 U.append(val)
176 sub_rec(U, S)
177 U.pop()
178 S.append(val)
179
180
181 def print_subsets(U):
182 sub_rec([], list(U))
183
184
185 # print_subsets({1, 2, 3, 4, 5})
186
187
188 # C-4.16
189 def reverse_str(word, index=0):
190 if index == len(word) - 1:
191 return [word[index]]
192 else:
193 ans = reverse_str(word, index+1)
194 ans.append(word[index])
195 if index == 0:
196 ans = ''.join(ans)
197 return ans
198
199
200 # print(reverse_str('abc'))
201
202
203 # C-4.17
204 def confirm_hui_str(word):
205 if len(word) == 1:
206 return True
207 else:
208 if word[0] != word[len(word)-1]:
209 return False
210 return confirm_hui_str(word[1:-1])
211
212
213 # print(confirm_hui_str('abacaba'))
214
215
216 # C-4.18
217 def confirm_vowel(word):
218 vowel_list = ['a', 'e', 'i', 'o', 'u']
219 if len(word) == 1:
220 if word[0] in vowel_list:
221 return 1
222 else:
223 return -1
224 elif word[0] in vowel_list:
225 return 1 + confirm_vowel(word[1:])
226 else:
227 return -1 + confirm_vowel(word[1:])
228
229
230 # print(confirm_vowel('abcdeae'))
231
232
233 # C-4.19(没有独立完成哈哈)
234 # def realign_num(data):
235 # if len(data) == 1:
236 # return data
237 # elif data[0] % 2 == 1:
238 # data.append(data.pop(0))
239 # return data[:1] + realign_num(data[:-1])
240 # else:
241 # return data[:1] + realign_num(data[:-1])
242 #
243
244 # print(realign_num([1, 2, 3, 4]))
245
246
247 # --------------C4-18-----------------
248 """
249 Approaches:
250 1) Two lists that you join at the end
251 2) Add new even numbers to the front and odd ones to the back
252
253
254 """
255
256
257 def evenoddbylists(S, index=0):
258 if index == len(S) - 1:
259 if len(S) == 1:
260 return S
261 elif S[index] % 2 == 0:
262 return [S[index]], []
263 else:
264 return [], [S[index]]
265 else:
266 evens, odds = evenoddbylists(S, index + 1)
267 if S[index] % 2 == 0:
268 evens.append(S[index])
269 else:
270 odds.append(S[index])
271 if index == 0:
272 return evens + odds
273 else:
274 return evens, odds
275
276
277 def evenoddbyappending(S, index=0):
278 if index == len(S) - 1:
279 return [S[index]]
280
281 else:
282 if S[index] % 2 == 1: # If odd, we want to add it to the end of the list
283 return evenoddbyappending(S, index + 1) + [S[index]]
284 else:
285 return [S[index]] + evenoddbyappending(S, index + 1)
286
287
288 # sequences = [[1, 2, 3, 4, 5, 6, 7, 8], [4, 3, 65, 23, 5, 46, 765, 3, 45, 23], [1], [2, 2]]
289 # print('Two lists approach')
290 # for s in sequences:
291 # print(evenoddbylists(s))
292 #
293 # print('\n\nAdding each element approach')
294 # for s in sequences:
295 # print(evenoddbyappending(s))
296
297 """
298 Final notes: the second approach is not desirable with lists because it is O(n) due to the fact that
299 appending an entire list of length m to another requires O(m) time. This could be solved with Linked Lists when
300 we get to that part of the book
301
302
303 Note also that both answers are correct, but the numbers are in a different order
304 """
305
306
307 # C-4.20
308 # def sort_by_k(S, k, index=0):
309 # if index == len(S) - 1:
310 # return [S[index]]
311 # else:
312 # if S[index] <= k:
313 # return [S[index]] + sort_by_k(S, k, index+1)
314 # else:
315 # return sort_by_k(S, k, index+1) + [S[index]]
316
317
318 # test_s = [1, 6, 5, 2, 3, 5, 7, 9, 11]
319 # print(sort_by_k(test_s, 5.5))
320
321 def add_lists(a, b):
322 if a and b:
323 return a + b
324 else:
325 return b or a
326
327
328 def split_by_k(S, k, index=0):
329 if len(S) == 1:
330 return S
331 elif index == len(S) - 1:
332 if S[index] < k:
333 return [S[index]], [], []
334 elif S[index] > k:
335 return [], [], [S[index]]
336 else:
337 return [], [S[index]], []
338 else:
339 low, mid, high = split_by_k(S, k, index+1)
340 if S[index] < k:
341 low = add_lists(low, [S[index]])
342 elif S[index] > k:
343 high = add_lists(high, [S[index]])
344 else:
345 mid = add_lists(mid, [S[index]])
346 if index == 0:
347 return low + mid + high
348 else:
349 return low, mid, high
350
351
352 S_test = [1, 2, 3, 5, 6, 7, 8, 10, 12]
353 # print(split_by_k(S_test, 6))
354
355
356 # C-4.21
357 def find_sum(S, k, index1=0, index2=1):
358 if S[index1] + S[index2] == k:
359 return S[index1], S[index2]
360 elif index2 == len(S) - 1:
361 index1 += 1
362 index2 = index1 + 1
363 return find_sum(S, k, index1, index2)
364 else:
365 return find_sum(S, k, index1, index2+1)
366
367
368 # S_test = [1, 2, 3, 5, 6, 7, 8, 10, 12]
369 # print(find_sum(S_test, 22))
370
371
372 # ----------------C4-21----------------------
373 """
374 Note: there is a brute force approach, but we will use the one with dictionaries instead
375
376
377 Additional Note: I learned an important thing about using mutable objects as defaults in python
378 Successive calls used the growing key_set, rather than initializing it as set() every time.
379
380 """
381
382
383 # The following line is the wrong way to do the initialization...
384 # def find_pair(S, target, key_set = set(), index=0):
385
386 def find_pair(S, k, key_set=None, index=0):
387 if key_set is None:
388 key_set = set()
389 key = k - S[index]
390 if len(S) <= 1:
391 return None
392 elif index == len(S) - 1:
393 if key in key_set:
394 return key, S[index]
395 else:
396 return None
397 else:
398 if key in key_set:
399 return key, S[index]
400 else:
401 key_set.add(S[index])
402 return find_pair(S, k, key_set, index+1)
403
404
405 sequence = [1, 2, 3, 4, 5, 6, 7, 8]
406
407 # for i in range(20):
408 # print(f'For the value {i}, the resulting pair is {find_pair(sequence, i)}')
409
410
411 # C-4.22
412 def power_loop(x, n):
413 factor = n
414 partial = 1
415
416 counter = 0
417 while factor:
418 factor = factor >> 1
419 counter += 1
420
421 while counter + 1:
422 partial *= partial
423 if n >> counter & 1:
424 partial *= x
425 counter -= 1
426 return partial
427
428
429 # combos = [(2, 13), (2, 15), (3, 15), (10, 7), (2, 5)]
430 # for b, p in combos:
431 # print('\n', power_loop(b, p), b ** p)