Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .idea/.gitignore

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions .idea/algorithms-python-17-04-21.iml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions .idea/inspectionProfiles/profiles_settings.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions .idea/misc.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions .idea/modules.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions .idea/vcs.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

47 changes: 47 additions & 0 deletions task1.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
"""
Задание 1.
Приведен код, который позволяет сохранить в
массиве индексы четных элементов другого массива
Сделайте замеры времени выполнения кода с помощью модуля timeit
Оптимизируйте, чтобы снизить время выполнения
Проведите повторные замеры.
Добавьте аналитику: что вы сделали и почему!!!
Без аналитики задание не принимается
"""

# Оказывается list comprehension работает медленнее обычного цикла, интересно.
# Оптимизированный вариант - func_4. По сути я просто совместил индексы и элементы от индексов в enumerate объекте,
# что видимо ускорило работу, потому что каждый раз находить элемент по индексу дольше, чем один раз использовать
# enumerate().

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Даниил. "Оказывается list comprehension работает медленнее обычного цикла, интересно."
они быстрее
вы неверно сделали замеры


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()))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[1, 3, 1, 3, 4, 5, 1] - на таких массивах вы не получите объективные цифры
создайте массив из миллиона элементов и сравните

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[1, 3, 1, 3, 4, 5, 1] - на таких массивах вы не получите объективные цифры
создайте массив из миллиона элементов и сравните

Хорошо, что на уроке об этом говорилось. Ах да, не говорилось. Я пробовал на массиве побольше, но не настолько

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()))
85 changes: 85 additions & 0 deletions task2.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
"""
Задание 2.
Приведен код, который формирует из введенного числа
обратное по порядку входящих в него цифр.
Задача решена через рекурсию
Выполнена попытка оптимизировать решение через мемоизацию.
Сделаны замеры обеих реализаций.
Сделайте аналитику, нужна ли здесь мемоизация или нет и почему?!!!
Будьте внимательны, задание хитрое. Не все так просто, как кажется.
"""

# Мемоизация не нужна в данном случае, потому что данные, возвращаемые каждой новой вызываемой рекурсией - разные.
# Но в контексте 10000 повторений одной и той же рекурсии с одним и тем же входным числом (!!!),
# мемоизация становится незаменима, потому что по сути рекурсия работает 1 раз, а потом все данные берутся из кэша.
# Отсюда при замерах и создаётся ощущение, что мемоизация помогает. На самом деле это обманка.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

выполнено


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))
75 changes: 75 additions & 0 deletions task3.py
Original file line number Diff line number Diff line change
@@ -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)')

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

выполнено

run('revers_2(1234567890)')
run('revers_3(1234567890)')
run('reverse_4(1234567890)')
run('reverse_5(1234567890)')
65 changes: 65 additions & 0 deletions task4.py
Original file line number Diff line number Diff line change
@@ -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()))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

выполнено

65 changes: 65 additions & 0 deletions task5.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
"""
Задание 5.**
Приведен наивный алгоритм нахождения i-го по счёту
простого числа (через перебор делителей).
Попробуйте решить эту же задачу,
применив алгоритм "Решето Эратосфена" (https://younglinux.info/algorithm/sieve)
Подсказка:
Сравните алгоритмы по времени на разных порядковых номерах чисел:
10, 100, 1000
Опишите результаты, сделайте выводы, где и какой алгоритм эффективнее
Подумайте и по возможности определите сложность каждого алгоритма.
Укажите формулу сложности О-нотация каждого алгоритма
и сделайте обоснование результатам.
"""

# Сложность обоих алгоритмов - O(n^2)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

у Решета другая сложность
посмотрите описание сложности в статье
У Решета с ростом n растет скорость очень существенно

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

у Решета другая сложность
посмотрите описание сложности в статье
У Решета с ростом n растет скорость очень существенно

Какая же там тогда сложность? Как бы, у simple тоже сложность по факту не 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()))