Я пытаюсь написать функцию Python (по крайней мере, изначально) для генерации всех подпоследовательностей некоторой длины k (где k> 0). Поскольку мне нужны только уникальные подпоследовательности, я сохраняю как подпоследовательности, так и частичные подпоследовательности в set s. Следующее, адаптированное от коллеги, - лучшее, что я мог придумать. Это кажется ... слишком сложным ... и как будто я могу злоупотреблять itertools или рекурсией, чтобы делать то, что я хочу делать. Кто-нибудь может сделать лучше?

from typing import Set, Tuple


def subsequences(string: str, k: int) -> Set[Tuple[str, ...]]:
    if len(string) < k:
        return set()
    start = tuple(string[:k])
    result = {start}
    prev_state = [start]
    curr_state = set()
    for s in string[k:]:
        for p in prev_state:
            for i in range(k):
                new = p[:i] + p[i + 1 :] + (s,)
                curr_state.add(new)
        result.update(curr_state)
        prev_state = list(curr_state)
        curr_state.clear()
    return result

(Для контекста меня интересует индукция k-строго кусочных языков , эффективно усваиваемый подкласс обычных языков, а грамматика может быть охарактеризована всеми допустимыми k-подпоследовательностями.

В конечном счете, я также думаю об этом в C ++, где std::make_tuple не так мощен, как Python tuple.)

3
structural ambiguity 22 Окт 2019 в 20:25

1 ответ

Лучший ответ

Вам нужен набор комбинаций r из n элементов (без замены, <= (n choose r).

Дано

import itertools as it

import more_itertools as mit

Код

Вариант 1 - itertools.combinations < / а>

set(it.combinations("foo", 2))
# {('f', 'o'), ('o', 'o')}

set(it.combinations("foobar", 3))
# {('b', 'a', 'r'),
#  ('f', 'a', 'r'),
#  ('f', 'b', 'a'),
#  ('f', 'b', 'r'),
#  ('f', 'o', 'a'),
#  ('f', 'o', 'b'),
#  ('f', 'o', 'o'),
#  ('f', 'o', 'r'),
#  ('o', 'a', 'r'),
#  ('o', 'b', 'a'),
#  ('o', 'b', 'r'),
#  ('o', 'o', 'a'),
#  ('o', 'o', 'b'),
#  ('o', 'o', 'r')}

Вариант 2 - {{X0} }

list(mit.distinct_combinations("foo", 2))
# [('f', 'o'), ('o', 'o')]

list(mit.distinct_combinations("foobar", 3))
# [('f', 'o', 'o'),
#  ('f', 'o', 'b'),
#  ('f', 'o', 'a'),
#  ('f', 'o', 'r'),
#  ('f', 'b', 'a'),
#  ('f', 'b', 'r'),
#  ('f', 'a', 'r'),
#  ('o', 'o', 'b'),
#  ('o', 'o', 'a'),
#  ('o', 'o', 'r'),
#  ('o', 'b', 'a'),
#  ('o', 'b', 'r'),
#  ('o', 'a', 'r'),
#  ('b', 'a', 'r')]

Оба варианта дают одинаковый (неупорядоченный) вывод. Тем не мение:

  • Вариант 1 берет набор всех комбинаций (включая дубликаты)
  • Вариант 2 не вычисляет повторяющиеся промежуточные звенья

Установите more_itertools через > pip install more_itertools.

См. Также примерную реализацию itertools.combinations в написан на Python.

2
pylang 22 Окт 2019 в 19:24