-
Notifications
You must be signed in to change notification settings - Fork 173
homework_04 #1267
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
homework_04 #1267
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -13,6 +13,7 @@ | |
|
|
||
| from timeit import timeit | ||
| from random import randint | ||
| from textwrap import dedent | ||
|
|
||
|
|
||
| def recursive_reverse(number): | ||
|
|
@@ -21,28 +22,6 @@ def recursive_reverse(number): | |
| return f'{str(number % 10)}{recursive_reverse(number // 10)}' | ||
|
|
||
|
|
||
| num_100 = randint(10000, 1000000) | ||
| num_1000 = randint(1000000, 10000000) | ||
| num_10000 = randint(100000000, 10000000000000) | ||
|
|
||
| print('Не оптимизированная функция recursive_reverse') | ||
| print( | ||
| timeit( | ||
| "recursive_reverse(num_100)", | ||
| setup='from __main__ import recursive_reverse, num_100', | ||
| number=10000)) | ||
| print( | ||
| timeit( | ||
| "recursive_reverse(num_1000)", | ||
| setup='from __main__ import recursive_reverse, num_1000', | ||
| number=10000)) | ||
| print( | ||
| timeit( | ||
| "recursive_reverse(num_10000)", | ||
| setup='from __main__ import recursive_reverse, num_10000', | ||
| number=10000)) | ||
|
|
||
|
|
||
| def memoize(f): | ||
| cache = {} | ||
|
|
||
|
|
@@ -63,19 +42,95 @@ def recursive_reverse_mem(number): | |
| return f'{str(number % 10)}{recursive_reverse_mem(number // 10)}' | ||
|
|
||
|
|
||
| # Сначала сделаем числа разной длины, чтобы они порождали | ||
| # разное число рекурсивных вызовов | ||
| num_1 = 1 | ||
| num_5 = 12345 | ||
| num_10 = 1234567890 | ||
|
|
||
|
|
||
| N = 1 | ||
| print('Не оптимизированная функция recursive_reverse') | ||
| print( | ||
| timeit( | ||
| "recursive_reverse(num_1)", | ||
| setup='from __main__ import recursive_reverse, num_1', | ||
| number=N)) | ||
| print( | ||
| timeit( | ||
| "recursive_reverse(num_5)", | ||
| setup='from __main__ import recursive_reverse, num_5', | ||
| number=N)) | ||
| print( | ||
| timeit( | ||
| "recursive_reverse(num_10)", | ||
| setup='from __main__ import recursive_reverse, num_10', | ||
| number=N)) | ||
| # --- | ||
| # Не оптимизированная функция recursive_reverse | ||
| # 0.5288310369942337 | ||
| # 1.658883001015056 | ||
| # 3.1454150729696266 | ||
| # --- | ||
| # Видно, что время зависит от длины числа, то есть рекурсия происходит | ||
|
|
||
|
|
||
| print('Оптимизированная функция recursive_reverse_mem') | ||
| print( | ||
| timeit( | ||
| 'recursive_reverse_mem(num_100)', | ||
| setup='from __main__ import recursive_reverse_mem, num_100', | ||
| number=10000)) | ||
| 'recursive_reverse_mem(num_1)', | ||
| setup='from __main__ import recursive_reverse_mem, num_1', | ||
| number=N)) | ||
| print( | ||
| timeit( | ||
| 'recursive_reverse_mem(num_1000)', | ||
| setup='from __main__ import recursive_reverse_mem, num_1000', | ||
| number=10000)) | ||
| 'recursive_reverse_mem(num_5)', | ||
| setup='from __main__ import recursive_reverse_mem, num_5', | ||
| number=N)) | ||
| print( | ||
| timeit( | ||
| 'recursive_reverse_mem(num_10)', | ||
| setup='from __main__ import recursive_reverse_mem, num_10', | ||
| number=N)) | ||
| # --- | ||
| # Оптимизированная функция recursive_reverse_mem | ||
| # 0.17349300003843382 | ||
| # 0.169658282015007 | ||
| # 0.1851398529834114 | ||
| # --- | ||
| # Странные результаты, совершенно не зависят от длины числа | ||
| # Можно предположить, что функция отрабатывает только | ||
| # в первый раз, а дальше мы имеем 999999 чтений из словаря. | ||
| # Сделаем аргументы случайными: | ||
|
|
||
| N = 1000000 | ||
| print('Не оптимизированная функция recursive_reverse') | ||
| print( | ||
| timeit( | ||
| dedent(''' | ||
| n = randint(10**8, 10**9) | ||
| recursive_reverse(n)'''), | ||
| setup=dedent(''' | ||
| from random import randint | ||
| from __main__ import recursive_reverse'''), | ||
| number=N)) | ||
|
|
||
| print('Оптимизированная функция recursive_reverse_mem') | ||
| print( | ||
| timeit( | ||
| 'recursive_reverse_mem(num_10000)', | ||
| setup='from __main__ import recursive_reverse_mem, num_10000', | ||
| number=10000)) | ||
| dedent(''' | ||
| n = randint(10**8, 10**9) | ||
| recursive_reverse_mem(n)'''), | ||
| setup=dedent(''' | ||
| from random import randint | ||
| from __main__ import recursive_reverse_mem'''), | ||
| number=N)) | ||
|
|
||
| # --- | ||
| # Не оптимизированная функция recursive_reverse | ||
| # 3.585237400024198 | ||
| # Оптимизированная функция recursive_reverse_mem | ||
| # 4.854937668016646 | ||
| # --- | ||
|
|
||
| # Что и требовалось доказать, мемоизация в данном случае | ||
| # делает только хуже | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. мемоизация здесь просто не нужна |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -9,6 +9,9 @@ | |
|
|
||
| Сделайте вывод, какая из трех реализаций эффективнее и почему | ||
| """ | ||
| from cProfile import Profile | ||
| from pstats import Stats | ||
| from timeit import timeit | ||
|
|
||
|
|
||
| def revers(enter_num, revers_num=0): | ||
|
|
@@ -34,3 +37,48 @@ def revers_3(enter_num): | |
| revers_num = enter_num[::-1] | ||
| return revers_num | ||
|
|
||
|
|
||
| n = 846207850378 | ||
| N = 100000 | ||
|
|
||
| profiler = Profile() | ||
| profiler.enable() | ||
| for i in range(N): | ||
| revers(n) | ||
| revers_2(n) | ||
| revers_3(n) | ||
| profiler.disable() | ||
| Stats(profiler).strip_dirs().print_stats() | ||
| # --- | ||
| # 1500001 function calls (300001 primitive calls) in 0.744 seconds | ||
| # | ||
| # Random listing order was used | ||
| # | ||
| # ncalls tottime percall cumtime percall filename:lineno(function) | ||
| # 1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects} | ||
| # 1300000/ | ||
| # 100000 0.488 0.000 0.488 0.000 task_3.py:17(revers) | ||
| # 100000 0.209 0.000 0.209 0.000 task_3.py:27(revers_2) | ||
| # 100000 0.047 0.000 0.047 0.000 task_3.py:35(revers_3) | ||
| # --- | ||
|
|
||
|
|
||
| print("revers\t\t", timeit("revers(n)", globals=globals(), number=N)) | ||
| print("revers_2\t", timeit("revers_2(n)", globals=globals(), number=N)) | ||
| print("revers_3\t", timeit("revers_3(n)", globals=globals(), number=N)) | ||
| # --- | ||
| # revers 0.29492326197214425 | ||
| # revers_2 0.20011793699814007 | ||
| # revers_3 0.03318392898654565 | ||
| # --- | ||
|
|
||
| # Встроенная функция предсказуемо лидирует в двух испытаниях. | ||
| # Остается неясным, почему cProfile и timeit дают разное | ||
| # отношение времен для цикла и рекурсии, cProfile дает 2, | ||
| # а timeit дает 1.5 Мне кажется, доверять нужно timeit, | ||
| # как самому "тупому" и поэтому надежному методу. | ||
| # | ||
| # cProfile строит свою статистику на основе измерений вызовов | ||
| # всех функций. Очевидно, при таком подходе чаще всего | ||
| # вызывается revers_2(0). При усреднении это приводит | ||
| # к занижению результатов по времени. | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Остается неясным, почему cProfile и timeit |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -10,25 +10,32 @@ | |
| Сделайте замеры и опишите, получилось ли у вас ускорить задачу. | ||
| """ | ||
|
|
||
| array = [1, 3, 1, 3, 4, 5, 1] | ||
| from timeit import timeit | ||
| from operator import itemgetter | ||
| from random import randint | ||
|
|
||
|
|
||
| def func_1(): | ||
| array7 = [1, 3, 1, 3, 4, 5, 1] | ||
| # Добавим еще один массив побольше | ||
| array1000 = [randint(1, 100) for _ in range(1000)] | ||
|
|
||
|
|
||
| def func_1(): # O(n^2) | ||
| m = 0 | ||
| num = 0 | ||
| for i in array: | ||
| count = array.count(i) | ||
| for i in array: # O(n^2) | ||
| count = array.count(i) # O(n) | ||
| if count > m: | ||
| m = count | ||
| num = i | ||
| return f'Чаще всего встречается число {num}, ' \ | ||
| f'оно появилось в массиве {m} раз(а)' | ||
|
|
||
|
|
||
| def func_2(): | ||
| def func_2(): # O(n^2) | ||
| new_array = [] | ||
| for el in array: | ||
| count2 = array.count(el) | ||
| for el in array: # O(n^2) | ||
| count2 = array.count(el) # O(n) | ||
| new_array.append(count2) | ||
|
|
||
| max_2 = max(new_array) | ||
|
|
@@ -37,5 +44,66 @@ def func_2(): | |
| f'оно появилось в массиве {max_2} раз(а)' | ||
|
|
||
|
|
||
| def func_3(): # O(n) | ||
| # словарь {номер: число его повторений} | ||
| reg = {} | ||
| for el in array: # O(n) | ||
| # увеличиваем счетчик для данного значения | ||
| reg[el] = reg.get(el, 0) + 1 # O(1) | ||
| # ищем max не по ключу, а по значению | ||
| elem, max_2 = max(reg.items(), key=itemgetter(1)) | ||
| return f'Чаще всего встречается число {elem}, ' \ | ||
| f'оно появилось в массиве {max_2} раз(а)' | ||
|
|
||
|
|
||
| # Сначала пройдем много раз по маленькому массиву | ||
| array = array7 | ||
| N = 10000 | ||
| print(func_1()) | ||
| print(timeit("func_1()", number=N, globals=globals())) | ||
| print(func_2()) | ||
| print(timeit("func_2()", number=N, globals=globals())) | ||
| print(func_3()) | ||
| print(timeit("func_3()", number=N, globals=globals())) | ||
| # output: | ||
| # --- | ||
| # Чаще всего встречается число 1, оно появилось в массиве 3 раз(а) | ||
| # 0.01287864800542593 | ||
| # Чаще всего встречается число 1, оно появилось в массиве 3 раз(а) | ||
| # 0.01819467602763325 | ||
| # Чаще всего встречается число 1, оно появилось в массиве 3 раз(а) | ||
| # 0.018526640022173524 | ||
| # --- | ||
|
|
||
| # Самым быстрым оказывается алгоритм №1, | ||
| # самым медленным алгоритм №3. | ||
|
|
||
|
|
||
| # Затем несколько раз пройдем большой массив | ||
| array = array1000 | ||
| N = 100 | ||
| print(func_1()) | ||
| print(timeit("func_1()", number=N, globals=globals())) | ||
| print(func_2()) | ||
| print(timeit("func_2()", number=N, globals=globals())) | ||
| print(func_3()) | ||
| print(timeit("func_3()", number=N, globals=globals())) | ||
| # output: | ||
| # --- | ||
| # Чаще всего встречается число 86, оно появилось в массиве 23 раз(а) | ||
| # 1.1288615339435637 | ||
| # Чаще всего встречается число 86, оно появилось в массиве 23 раз(а) | ||
| # 1.1044497270486318 | ||
| # Чаще всего встречается число 86, оно появилось в массиве 23 раз(а) | ||
| # 0.00952054699882865 | ||
| # --- | ||
|
|
||
| # Здесь самым быстрым является алгоритм №3, | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. здесь решается в одну строку - ф-ция max |
||
| # из-за того, что его линейная сложность | ||
| # проявляется на достаточно на больших n. | ||
| # Самым медленным оказывается алгоритм №1. | ||
|
|
||
| # Вопрос, почему тогда на маленьких массивах | ||
| # все наоборот? Я думаю, из-за того, что не | ||
| # успевает "окупиться" создание дополнительных | ||
| # объектов, списка для №2 и словаря для №3 | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
выполнено