# Задание 1.
# Проанализировать скорость и сложность одного любого алгоритма из разработанных в рамках домашнего задания первых трех уроков.
# Примечание. Идеальным решением будет:
# a. выбрать хорошую задачу, которую имеет смысл оценивать,
# b. написать 3 варианта кода (один у вас уже есть),
# c. проанализировать 3 варианта и выбрать оптимальный,
# d. результаты анализа вставить в виде комментариев в файл с кодом (не забудьте указать, для каких N вы проводили замеры),
# e. написать общий вывод: какой из трёх вариантов лучше и почему.

# взял задачу № 6 из предыдущего урока:
#    6. В одномерном массиве найти сумму элементов, находящихся между минимальным и максимальным элементами.
#    Сами минимальный и максимальный элементы в сумму не включать.
#Примечание: так как у нас случайная выборка в последовательности то минимально еи максимальное значение
#    могут оказаться соседними в ряду и в таком случае сумма будет считаться моментально.
#    Поэтому для полноты эксперимента, я сумму считаю всегда с 1 по предпоследний член в последовательности
import random
import cProfile
import timeit
l = [random.randint(1,1000) for i in range(10000)]
print(len(l))
#print(l)
def msm1(l): # первый вариант - исходный (с учетом примечания)
     nmin,nmax = l[0],l[0]
     imin,imax = 0,0
     ind = 0
     for n in l[1:]:
          ind += 1
          if n < nmin:
               nmin = n
               imin = ind
          if n > nmax:
               nmax = n
               imax = ind
     if imin > imax:
          imin,imax = imax,imin
     nsum = 0
     for i in l[1:-1]: # [imin+1:imax]: - здесь заменил код на то чтобы считало сумму по максимуму
          nsum +=i
     return nsum

def msm2(l): # Второй вариант: поиск min-max с помощью функции, остальное также
     nmin,nmax = min(l), max(l)
     imin,imax = l.index(nmin),l.index(nmax)
     if imin > imax:
          imin,imax = imax,imin
     nsum = 0
     for i in l[1:-1]:
          nsum +=i
     return nsum

def msm3(l): # Третий вариант: и поиск min-max и вычисление суммы с помощью встроенных функций
     nmin,nmax = min(l), max(l)
     imin,imax = l.index(nmin),l.index(nmax)
     if imin > imax:
          imin,imax = imax,imin
     nsum = sum(l[1:-1])
     return nsum

setup_code = '''
from __main__ import msm1,msm2,msm3
from __main__ import l
'''
testcode1 = '''
msm1(l)
'''
testcode2 = '''
msm2(l)
'''
testcode3 = '''
msm3(l)
'''

n = 1000
test1 = timeit.repeat(stmt=testcode1, setup = setup_code, repeat = 5, number = n )
print(f'Время: {round(min(test1),5)}  для {n} повторений (поиск min-max: в цикле, вычисление суммы: в цикле)')
test2 = timeit.repeat(stmt=testcode2, setup = setup_code, repeat = 5, number = n )
print(f'Время: {round(min(test2),5)}  для {n} повторений (поиск min-max: с помощью функции, вычисление суммы: в цикле)')
test3 = timeit.repeat(stmt=testcode3, setup = setup_code, repeat = 5, number = n )
print(f'Время: {round(min(test3),5)}  для {n} повторений (поиск min-max: с помощью функции, вычисление суммы: с помощью функции)')

#cProfile.run('msm1(l)')

# испытания проводились на последовательности из 10 000 случайных чисел (в диапазоне до 1000)
# Время: 0.97114  для 1000 повторений (поиск min-max: в цикле, вычисление суммы: в цикле)
# Время: 0.72781  для 1000 повторений (поиск min-max: с помощью функции, вычисление суммы: в цикле)
# Время: 0.47521  для 1000 повторений (поиск min-max: с помощью функции, вычисление суммы: с помощью функции)

# Вывод:
# поиск min-max в цикле и вычисление суммы в цикле занимает больше времени чем в встроенных функциях,
# поэтому оптимальный вариант пользоваться встроенными функциями (выигрыш по времени примерно в 2 раза)
# примечание: зависимость от количества членов в последовательности - линейная для всех трех вариантов,
# так как вывоз каждой из функций происходит только 1 раз