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