Быстрый (самый) способ обработки расширяющейся линейной последовательности в Python

У меня есть следующие условия:

  • Число u(0) = 1 — первое в u.
  • Для каждого x в u тогда y = 2 * x + 1 и z = 3 * x + 1 также должны находиться в u.
  • Других чисел в тебе нет.
  • Дубликатов быть не должно.
  • Числа должны идти в порядке возрастания.

Пример: u = [1, 3, 4, 7, 9, 10, 13, 15, 19, 21, 22, 27, ...]

Программа хочет, чтобы я указал значение члена по заданному индексу.

Я уже нашел способы решить эту проблему с помощью insort, а также составил минимальное дерево двоичного поиска. К сожалению, этот процесс должен быть быстрее, чем у меня, и я не знаю, что делать дальше. Я думал, что BST это сделает, но он недостаточно быстр.

Вот мой код BST:

class BSTNode:
    def __init__(self, val=None):
        self.left = None
        self.right = None
        self.val = val

    def insert(self, val):
        if not self.val:
            self.val = val
            return

        if self.val == val:
            return

        if val < self.val:
            if self.left:
                self.left.insert(val)
                return
            self.left = BSTNode(val)
            return

        if self.right:
            self.right.insert(val)
            return
        self.right = BSTNode(val)

    def inorder(self, vals):
        if self.left is not None:
            self.left.inorder(vals)
        if self.val is not None:
            vals.append(self.val)
        if self.right is not None:
            self.right.inorder(vals)
        return vals

и вот моя функция:

from sys import setrecursionlimit

def twotimeslinear(n):
    #setrecursionlimit(2000)
    i = 0
    u = [1]
    ended = False

    bst = BSTNode()
    bst.insert(1)
    while i < n and not ended:
        
        for j in range(2, 4):
            k = 1
            cur = j * bst.inorder([])[i] + 1
            bst.insert(cur)
            
            if len(u) == n:
                ended = True
                break
        i+= 1
    return bst.inorder([])[n]

Мне просто нужны указания, что я могу сделать, чтобы ускорить этот процесс. Я мог бы решить проблему, если бы только знал, чего мне не хватает. Вероятно, я упускаю из виду какую-то структуру данных, которая работала бы лучше, но я даже не знаю, что искать. Спасибо за любую помощь.

То, что вы описываете, — это не последовательность (даже если вы иногда используете словарь последовательности, например u(0)=1, а просто набор). Если бы это была последовательность, мы бы знали, в каком порядке их вычислять. И ты не сказал.

chrslg 30.07.2024 19:39

Спасибо; добавление уточнения к вопросу.

ep84 30.07.2024 19:43

Он имеет бесконечную длину. Так вам нужно написать бесконечный генератор или около того?

no comment 30.07.2024 19:50

Добавлю уточнения к вопросу. Программа просто хочет получить значение числа по заданному индексу.

ep84 30.07.2024 19:53

Звучит как работа для кучи или sortedcontainers.SortedList.

user2357112 30.07.2024 19:59

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

user2357112 30.07.2024 20:01

Насколько большим может быть индекс и сколько времени отводится?

no comment 30.07.2024 20:03

Я считаю, что это вызов, и финальный тест составляет 60 000 элементов.

ep84 30.07.2024 20:04

Ах, черт, я не думал о необходимости математически вычислять N-е, не пересекая его. Я думал, что справлюсь с этим с минимальными вычислениями. Это очень помогает.

ep84 30.07.2024 20:05

Если это всего лишь 60 000, то вычисление каждого элемента должно быть в порядке. Обычно подобные задачи заставляют вас вычислять что-то вроде миллиардного элемента.

user2357112 30.07.2024 20:06

Эту последовательность можно найти в Интернет-энциклопедии целочисленных последовательностей: oeis.org/A002977

John Coleman 30.07.2024 20:07

Проверьте мой отредактированный ответ

Suramuthu R 30.07.2024 20:54
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
2
12
64
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Генерация и объединение ys и zs:

from heapq import merge
from itertools import groupby

def twotimeslinear(n):
    u = [1]
    ys = (2*x+1 for x in u)
    zs = (3*x+1 for x in u)
    for x, _ in groupby(merge(ys, zs)):
        u.append(x)
        if n < len(u):
            return u[n]

print(*map(twotimeslinear, range(20)))

Попробуйте это онлайн!

Занимает ~0,05 секунды для индекса лимита 60 000 и ~0,7 секунды для индекса 1 000 000.

Альтернативная базовая реализация:

def twotimeslinear(n):
    u = [1]
    i = j = 0
    while n >= len(u):
        y = 2*u[i] + 1
        z = 3*u[j] + 1
        m = min(y, z)
        u.append(m)
        if y == m:
            i += 1
        if z == m:
            j += 1
    return u[n]

print(*map(twotimeslinear, range(20)))

Попробуйте это онлайн!

И еще вариант, закинув в него тонны батареек :-)

from heapq import merge
from itertools import groupby, tee, chain, islice, repeat
from operator import itemgetter, mul, add

def twotimeslinear(n):
    parts = [[1]]
    whole = chain.from_iterable(parts)
    output, feedback1, feedback2 = tee(whole, 3)
    ys = map(1 .__add__, map(2 .__mul__, feedback1))
    zs = map(add, map(mul, repeat(3), feedback2), repeat(1))  # different way just for fun
    merged = map(itemgetter(0), groupby(merge(ys, zs)))
    parts.append(merged)
    return next(islice(output, n, None))

print(*map(twotimeslinear, range(20)))

После настройки с помощью кода Python он полностью выполняется в коде C. Это мое глупое хобби.

Попробуйте это онлайн!

Спасибо. Это сработало отлично. Мне бы хотелось это понять, но я еще далеко не знаком со стандартной библиотекой Python.

ep84 30.07.2024 20:10

@ ep84 Эх, дело не в библиотеке, а в алгоритме. Я добавил более базовую версию.

no comment 30.07.2024 20:20

@ ep84 Но теперь я пошел в противоположном направлении и добавил версию, использующую нездоровое количество использованных библиотек...

no comment 30.07.2024 20:40

Потрясающая работа. Я не хочу свести к минимуму количество навыков, необходимых для такого алгоритмического мышления. Я могу только надеяться, что мне станет лучше.

ep84 30.07.2024 20:41

вам действительно не следует использовать 1 .__add_ и 2.__mul__ напрямую. используйте operator и functools.partial или выражение lambda

juanpa.arrivillaga 30.07.2024 23:36

@juanpa.arrivillaga Почему? Все значения являются целыми числами, ваша обычная проблема с типами не применима.

no comment 30.07.2024 23:57

Другие вопросы по теме