Задать вопрос
Koren1
@Koren1

Как правильно заниматься перебором: a³ + b³ + c³ = d³?

Правильно ли я занимаюсь перебором?
import sqlite3
import sys

DB_NAME = 'ramanujan_triple_cube.db'
MIN_VAL = -100
MAX_VAL = 100

def create_tables(conn):
    with conn:
        conn.execute('''
            CREATE TABLE IF NOT EXISTS solutions (
                a INTEGER,
                b INTEGER,
                c INTEGER,
                d INTEGER,
                PRIMARY KEY (a, b, c, d)
            )
        ''')
        conn.execute('''
            CREATE TABLE IF NOT EXISTS progress (
                id INTEGER PRIMARY KEY CHECK (id = 1),
                last_a INTEGER,
                last_b INTEGER,
                last_c INTEGER
            )
        ''')
        cur = conn.execute('SELECT COUNT(*) FROM progress')
        if cur.fetchone()[0] == 0:
            conn.execute('INSERT INTO progress (id, last_a, last_b, last_c) VALUES (1, ?, ?, ?)', (MIN_VAL, MIN_VAL, MIN_VAL))

def is_nontrivial_solution(a, b, c, d):
    # Исключаем случаи, где сумма двух кубов взаимно противоположна
    if a == -b or a == -c or b == -c:
        return False
    # Исключаем случаи, где есть нули
    if 0 in (a, b, c, d):
        return False
    return True

def save_solution(conn, a, b, c, d):
    try:
        with conn:
            conn.execute('INSERT INTO solutions (a, b, c, d) VALUES (?, ?, ?, ?)', (a, b, c, d))
        print(f'Найдено решение: {a}³ + {b}³ + {c}³ = {d}³')
    except sqlite3.IntegrityError:
        # Решение уже есть
        pass

def save_progress(conn, a, b, c):
    with conn:
        conn.execute('UPDATE progress SET last_a = ?, last_b = ?, last_c = ? WHERE id = 1', (a, b, c))

def load_progress(conn):
    cur = conn.execute('SELECT last_a, last_b, last_c FROM progress WHERE id = 1')
    return cur.fetchone()

def main():
    conn = sqlite3.connect(DB_NAME)
    create_tables(conn)

    last_a, last_b, last_c = load_progress(conn)
    print(f'Продолжаем с позиции: a={last_a}, b={last_b}, c={last_c}')

    try:
        for a in range(last_a, MAX_VAL + 1):
            b_start = last_b if a == last_a else MIN_VAL
            for b in range(b_start, MAX_VAL + 1):
                c_start = last_c if a == last_a and b == last_b else MIN_VAL
                for c in range(c_start, MAX_VAL + 1):
                    lhs = a**3 + b**3 + c**3
                    if lhs == 0:
                        # d=0, но фильтруем решения с нулём
                        continue
                    d = round(abs(lhs) ** (1/3))
                    for candidate_d in (d, -d):
                        if candidate_d**3 == lhs:
                            if is_nontrivial_solution(a, b, c, candidate_d):
                                save_solution(conn, a, b, c, candidate_d)
                    save_progress(conn, a, b, c)
                last_c = MIN_VAL
            last_b = MIN_VAL
    except KeyboardInterrupt:
        print('\nПрерывание программы. Сохраняем прогресс и завершаем работу...')
        save_progress(conn, a, b, c)
        conn.close()
        sys.exit(0)

    conn.close()
    print('Поиск завершён.')

if __name__ == '__main__':
    main()


Может напрасно я выбрал Python? Может лучше сделать это где-то в BOINC? Или еще где-то? Быть может ответы уже где-то есть? Придумал название: "Уравнение тройного куба Рамануджана", может напрасно, быть может название уже есть?
  • Вопрос задан
  • 106 просмотров
Подписаться 1 Средний 10 комментариев
Пригласить эксперта
Ответы на вопрос 1
wataru
@wataru Куратор тега Математика
Разработчик на С++, экс-олимпиадник.
Циклы в питоне - это ужасно медленно, да. Не лучший язык для написания числодробилки.

Еще, оффтопик, база даных тут совсем не нужна. Достаточно выводить числа на экран или в текстовый файл. так быстрее будет.

У вас самый наивный перебор в лоб, его можно ускорить. Сначала сгенерируйте все кубы и схраните их в массив. Их будет примерно кубический корень из |MAX_VAL-MIN_VAL| - это достаточно маленькая величина.

Теперь задача: найти 3 числа в массиве, дающих в сумме число из массива. Это все еще O(n^3), если исползовать 4 индекса. Но можно ускорить решение методом "встреча по середине".
Вместо A+B+C=D из массива будем искать A+B=C-D. Для этого переберем все пары чисел, подсчитаем их сумму и сохраним в dict() вместе со списком индексами этих чисел (список всех пар, дающих эту сумму). Потом опять переберем все пары, подсчитаем их разность и посмотрим в словаре, а была ли пара с такой же суммой. Если была - вот мы нашли 4 числа, таких что A+B=C-D. извлекаем корни и выдаем это в ответ.
Это будет уже O(n^2) - заметно быстрее.
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы