Оптимизация моего кода

У меня есть задание на python:

(*) Необходимо из списка чисел путем вычеркивания некоторых чисел, не меняя порядка остальных, составить максимально длинный новый список чисел, образующих арифметическую прогрессию. Будем считать, что два любых числа (или одно для списка из одного элемента) всегда образуют такую прогрессию. Если можно составить несколько таких списков одинаковой максимальной длины, разрешается составить любой, например:

{ 11, 3, 2, 5, 4, 7, 7, 1, 4, 6, 9, 8, 8, 7, 1, 3, 2 } → { 3, 5, 7, 9 }

или { 2, 4, 6, 8 }

или { 5, 4, 3, 2 }

проблема в том что мой код слишком тяжелый, с тем же массивом { 11, 3, 2, 5, 4, 7, 7, 1, 4, 6, 9, 8, 8, 7, 1, 3, 2 } компьютер не выдерживает, подскажите, пожалуйста, как можно ускорить его работу, чтобы он работал нормально не только с маленькими массивами

def read_numbers_from_file(filename):
try:
    with open(filename, 'r') as file:
        numbers_str = file.read()
        numbers = [int(num) for num in numbers_str.split()]
        return numbers
except FileNotFoundError:
    print(f"Файл '{filename}' не найден.")
    return []


def solution(arr):
    max_progression = []
    arr_set = set(arr)


progressions = {}
for i in range(len(arr)):
    for j in range(i + 1, len(arr)):
        progression = [arr[i], arr[j]]
        diff = arr[j] - arr[i]
        next_term = arr[j] + diff
        length = 2

        while next_term in arr_set:
            progression.append(next_term)
            next_term += diff
            length += 1

     
        if length > progressions.get(tuple(progression), 0):
            progressions[tuple(progression)] = length
            if length > len(max_progression):
                max_progression = progression

return max_progression


numbers = read_numbers_from_file('input.txt')
print("Числа из файла:", numbers)

res = solution(numbers)
print("Максимально длинная арифметическая прогрессия:", res)

Ответы (2 шт):

Автор решения: SanLi

нашел решение похожей задачи на js,переделал на python,теперь выглядит как то так

def search(a):
    # Если в последовательности всего один элемент, он и будет прогрессией
    if len(a) == 1:
        return a

    max_progression = []

    for start in range(len(a)):
        for i in range(start + 1, len(a)):
            step = a[i] - a[start]
            progression = [a[start], a[i]]

            for j in range(i + 1, len(a)):
                if a[j] - step == progression[-1]:
                    progression.append(a[j])

            if len(progression) > len(max_progression):
                max_progression = progression

    return max_progression


def read_numbers_from_file(filename):
    try:
        with open(filename, 'r') as file:
            numbers_str = file.read()
            numbers = [int(num) for num in numbers_str.split()]
            return numbers
    except FileNotFoundError:
        print(f"Файл '{filename}' не найден.")
        return []


def write_progression_to_file(filename, progression):
    with open(filename, 'w') as file:
        file.write(", ".join(map(str, progression)) + "\n")


filename = 'input.txt'
arr1 = read_numbers_from_file(filename)
res1 = search(arr1)

output_filename = 'output.txt'
write_progression_to_file(output_filename, res1)

# Вывод результата в консоль
print("Максимально длинная арифметическая прогрессия:", res1)

теперь для { 11, 3, 2, 5, 4, 7, 7, 1, 4, 6, 9, 8, 8, 7, 1, 3, 2 } выводится верный результат { 3, 5, 7, 9 }

→ Ссылка
Автор решения: Stanislav Volodarskiy

Оптимизация кубического алгоритма. Я верю но не умею это доказывать что она работает за O(n2·log n). Внешний цикл перебирает первые элементы прогрессий. Внутрений цикл хранит прогрессии в словаре вида

{<следующее значение в прогрессии>: [(<длина прогрессии>, <шаг прогрессии>), ...]}.

В список со значением попадают все прогрессии, которые ожидают это значение следующим. Очередной элемент из массива разбирает свой список и переносит прогрессии в списки соответствующие следующим значениям.

Множество шагов используется чтобы прогрессии с одинаковым шагом не дублировались. Можно показать что если первый элемент прогрессии фиксирован, то достаточно хранить только одну прогрессию для одного шага.

import sys


def max_ap(a):
    n = len(a)
    if n <= 1:
        return a

    max_len = 1
    max_ap = 0, a[0]
    for i, ai in enumerate(a):
        futures = {}
        d_set = set()
        for j in range(i + 1, n):
            aj = a[j]
            for m, d in futures.pop(aj, []):
                futures.setdefault(aj + d, []).append((m + 1, d))

            d = aj - ai 
            if d not in d_set:
                d_set.add(d)
                futures.setdefault(aj + d, []).append((2, d))

        m, d = max(
            (md for aps in futures.values() for md in aps),
            default=(1, 0)
        )
        if m > max_len:
            max_len = m
            max_ap = d, ai

    d, a_next = max_ap
    ap = []
    for ai in a:
        if ai == a_next:
            ap.append(ai)
            a_next += d
    return ap


def main():
    a = tuple(int(word) for line in sys.stdin for word in line.split())
    print(*max_ap(a))


main()
→ Ссылка