diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..26d3352 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git a/.idea/algorithms-python-17-04-21.iml b/.idea/algorithms-python-17-04-21.iml new file mode 100644 index 0000000..74d515a --- /dev/null +++ b/.idea/algorithms-python-17-04-21.iml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml new file mode 100644 index 0000000..105ce2d --- /dev/null +++ b/.idea/inspectionProfiles/profiles_settings.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..81dd32a --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..c95a575 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/task1.py b/task1.py new file mode 100644 index 0000000..f142bcf --- /dev/null +++ b/task1.py @@ -0,0 +1,47 @@ +""" +Задание 1. +Приведен код, который позволяет сохранить в +массиве индексы четных элементов другого массива +Сделайте замеры времени выполнения кода с помощью модуля timeit +Оптимизируйте, чтобы снизить время выполнения +Проведите повторные замеры. +Добавьте аналитику: что вы сделали и почему!!! +Без аналитики задание не принимается +""" + +# Оказывается list comprehension работает медленнее обычного цикла, интересно. +# Оптимизированный вариант - func_4. По сути я просто совместил индексы и элементы от индексов в enumerate объекте, +# что видимо ускорило работу, потому что каждый раз находить элемент по индексу дольше, чем один раз использовать +# enumerate(). + +from timeit import timeit + + +def func_1(nums): + new_arr = [] + for i in range(len(nums)): + if nums[i] % 2 == 0: + new_arr.append(i) + return new_arr + + +def func_2(nums): + return [i for i in range(len(nums)) if nums[i] % 2 == 0] + + +def func_3(nums): + return [i for i, j in enumerate(nums) if j % 2 == 0] + + +def func_4(nums): + new_arr = [] + for i, j in enumerate(nums): + if j % 2 == 0: + new_arr.append(i) + return new_arr + + +print(timeit("func_1([1, 3, 1, 3, 4, 5, 1])", globals=globals())) +print(timeit("func_2([1, 3, 1, 3, 4, 5, 1])", globals=globals())) +print(timeit("func_3([1, 3, 1, 3, 4, 5, 1])", globals=globals())) +print(timeit("func_4([1, 3, 1, 3, 4, 5, 1])", globals=globals())) \ No newline at end of file diff --git a/task2.py b/task2.py new file mode 100644 index 0000000..50a3843 --- /dev/null +++ b/task2.py @@ -0,0 +1,85 @@ +""" +Задание 2. +Приведен код, который формирует из введенного числа +обратное по порядку входящих в него цифр. +Задача решена через рекурсию +Выполнена попытка оптимизировать решение через мемоизацию. +Сделаны замеры обеих реализаций. +Сделайте аналитику, нужна ли здесь мемоизация или нет и почему?!!! +Будьте внимательны, задание хитрое. Не все так просто, как кажется. +""" + +# Мемоизация не нужна в данном случае, потому что данные, возвращаемые каждой новой вызываемой рекурсией - разные. +# Но в контексте 10000 повторений одной и той же рекурсии с одним и тем же входным числом (!!!), +# мемоизация становится незаменима, потому что по сути рекурсия работает 1 раз, а потом все данные берутся из кэша. +# Отсюда при замерах и создаётся ощущение, что мемоизация помогает. На самом деле это обманка. + + +from timeit import timeit +from random import randint + + +def recursive_reverse(number): + if number == 0: + return '' + 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 = {} + + def decorate(*args): + + if args in cache: + return cache[args] + else: + cache[args] = f(*args) + return cache[args] + return decorate + + +@memoize +def recursive_reverse_mem(number): + if number == 0: + return '' + return f'{str(number % 10)}{recursive_reverse_mem(number // 10)}' + + +print('Оптимизированная функция recursive_reverse_mem') +print( + timeit( + 'recursive_reverse_mem(num_100)', + setup='from __main__ import recursive_reverse_mem, num_100', + number=10000)) +print( + timeit( + 'recursive_reverse_mem(num_1000)', + setup='from __main__ import recursive_reverse_mem, num_1000', + number=10000)) +print( + timeit( + 'recursive_reverse_mem(num_10000)', + setup='from __main__ import recursive_reverse_mem, num_10000', + number=10000)) \ No newline at end of file diff --git a/task3.py b/task3.py new file mode 100644 index 0000000..e5d323a --- /dev/null +++ b/task3.py @@ -0,0 +1,75 @@ +""" +Задание 3. +Приведен код, формирующий из введенного числа +обратное по порядку входящих в него +цифр и вывести на экран. +Сделайте профилировку каждого алгоритма через cProfile и через timeit +Обязательно предложите еще свой вариант решения и также запрофилируйте! +Сделайте вывод, какая из четырех реализаций эффективнее и почему!!! +Без аналитики задание считается не принятым +""" + +# Я не нашёл информации о том какой сложности является функция str(), но логически это либо линейная, либо константная. +# Этого хватит для аналитики этого задания, хотя хотелось бы узнать, какая всё-таки у неё сложность, пожалуйста с: +# Для простоты, в этом задании говорю об str() как о линейной, потому что во-первых я всё же склоняюсь к этому варианту, +# А во вторых это бОльшая из двух. + +# Самый эффективный вариант - через срез (3), что вполне логично, +# потому что в данной функции всего две операции, обе линейные и простые. +# Вариант через reverse() (5) на втором месте по скорости. Тут уже 4 линейные операции, но всё ещё простые. +# На третьем - вариант через цикл (2). Цикл ещё и с набором математических операций, +# поэтому медленнее простых вариантов выше, но быстрее рекурсий ниже +# Рекурсия данная в задании (1) выполняется немного быстрее, чем моя рекурсия (4), потому что в ней нет +# постоянных конвертаций чисел в строку и наоборот, а всегда происходит работа только с числами. + +from timeit import timeit +from cProfile import run + + +def revers_1(enter_num, revers_num=0): + if enter_num == 0: + return revers_num + else: + num = enter_num % 10 + revers_num = (revers_num + num / 10) * 10 + enter_num //= 10 + return revers_1(enter_num, revers_num) + + +def revers_2(enter_num, revers_num=0): + while enter_num != 0: + num = enter_num % 10 + revers_num = (revers_num + num / 10) * 10 + enter_num //= 10 + return revers_num + + +def revers_3(enter_num): + enter_num = str(enter_num) + revers_num = enter_num[::-1] + return revers_num + + +def reverse_4(num): + if num // 10 == 0: + return num % 10 + else: + return f'{num % 10}{reverse_4(num // 10)}' + + +def reverse_5(num): + num = list(str(num)) + num.reverse() + return ''.join(num) + + +print(timeit('revers_1(1234567890)', globals=globals())) +print(timeit('revers_2(1234567890)', globals=globals())) +print(timeit('revers_3(1234567890)', globals=globals())) +print(timeit('reverse_4(1234567890)', globals=globals())) +print(timeit('reverse_5(1234567890)', globals=globals())) +run('revers_1(1234567890)') +run('revers_2(1234567890)') +run('revers_3(1234567890)') +run('reverse_4(1234567890)') +run('reverse_5(1234567890)') diff --git a/task4.py b/task4.py new file mode 100644 index 0000000..0ad0bf1 --- /dev/null +++ b/task4.py @@ -0,0 +1,65 @@ +""" +Задание 4. +Приведены два алгоритма. В них определяется число, +которое встречается в массиве чаще всего. +Сделайте профилировку каждого алгоритма через timeit +Попытайтесь написать третью версию, которая будет самой быстрой. +Сделайте замеры и опишите, получилось ли у вас ускорить задачу. +Без аналитики задание считается не принятым! +""" + +# func_2 = n * (n + 1) + n + n +# func_1 = n * (n + 1) +# func_3 = n * (n) + n + +# Вторая функция самая долгая, потому что помимо квадратичной сложности полученной при взаимодействии цикла и +# функции count() в ней также метод append() повторяется n раз, а также в конце функции выполняются две операции +# с линейной сложностью. +# Первая функция работает значительно быстрее второй, потому что при всё той же квадратичной сложности, +# в ней уже n раз выполняется не append(), а простые операции сравнения. +# К тому же в первой функции нет конечных операций с линейной сложностью. +# Моя функция (третья) выполняется чуть медленнее первой при небольшом размере списка, но быстрее уже при длине +# списка в 2 раза бОльшей, чем у списка представленного в задании. Связанно это с тем, что при бОльшей длине +# время на операцию с линейной сложностью в конце функции невелируется более быстро работающей составляющей с +# квадратичной сложностью (max и count), чем составляющая с этой же сложностью в первой функции. + + +from timeit import timeit + +array = [1, 3, 1, 3, 4, 5, 1] +# array = [1, 3, 1, 3, 4, 5, 1, 6, 7, 6, 8, 9, 7, 10] + + +def func_1(): + m = 0 + num = 0 + for i in array: + count = array.count(i) + if count > m: + m = count + num = i + return f'Чаще всего встречается число {num}, ' \ + f'оно появилось в массиве {m} раз(а)' + + +def func_2(): + new_array = [] + for el in array: + count2 = array.count(el) + new_array.append(count2) + + max_2 = max(new_array) + elem = array[new_array.index(max_2)] + return f'Чаще всего встречается число {elem}, ' \ + f'оно появилось в массиве {max_2} раз(а)' + + +def func_3(): + num = max(array, key=array.count) + return f'Чаще всего встречается число {num}, ' \ + f'оно появилось в массиве {array.count(num)} раз(а)' + + +print(timeit('func_1()', globals=globals())) +print(timeit('func_2()', globals=globals())) +print(timeit('func_3()', globals=globals())) \ No newline at end of file diff --git a/task5.py b/task5.py new file mode 100644 index 0000000..22cb33e --- /dev/null +++ b/task5.py @@ -0,0 +1,65 @@ +""" +Задание 5.** +Приведен наивный алгоритм нахождения i-го по счёту +простого числа (через перебор делителей). +Попробуйте решить эту же задачу, +применив алгоритм "Решето Эратосфена" (https://younglinux.info/algorithm/sieve) +Подсказка: +Сравните алгоритмы по времени на разных порядковых номерах чисел: +10, 100, 1000 +Опишите результаты, сделайте выводы, где и какой алгоритм эффективнее +Подумайте и по возможности определите сложность каждого алгоритма. +Укажите формулу сложности О-нотация каждого алгоритма +и сделайте обоснование результатам. +""" + +# Сложность обоих алгоритмов - O(n^2) +# Но суть в том, что при одинаковом первом линейном множителе (n - количество чисел с 2 до искомого простого числа), +# второй линейный множитель абсолютно разный. В первом варианте это количество всех чисел до числа, +# перебираемого в данный момент, а во втором это количество простых чисел до этого числа, что в разы меньше. +# Именно поэтому новый алгоритм выполняется в разы быстрее старого. + + +from timeit import timeit + + +def simple(i): # n * (const * n + const) => O(n^2) + """Без использования «Решета Эратосфена»""" + count = 1 + n = 2 + while count <= i: + t = 1 + is_simple = True + while t <= n: + if n % t == 0 and t != 1 and t != n: + is_simple = False + break + t += 1 + if is_simple: + if count == i: + break + count += 1 + n += 1 + return n + + +def eratosfen(i): # n * (const * n + const) => O(n^2) + simples = [] + n = 2 + while len(simples) < i: + is_simple = True + for j in simples: + if n % j == 0: + is_simple = False + break + if is_simple: + simples.append(n) + n += 1 + return simples[-1] + + +i = int(input('Введите порядковый номер искомого простого числа: ')) +print(simple(i)) +print(eratosfen(i)) +print(timeit(f'simple({i})', globals=globals())) +print(timeit(f'eratosfen({i})', globals=globals())) \ No newline at end of file