生成所有唯一的 k 子序列
Generate all unique k-subsequences
我正在尝试编写一个 Python(至少在最初)函数来生成某个长度为 k(其中 k > 0)的所有子序列。因为我只需要唯一的子序列,所以我将子序列和部分子序列都存储在 set
中。以下内容改编自一位同事,是我能想到的最好的。似乎...过于复杂...而且我应该能够滥用 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-strictly piecewise languages 的归纳感兴趣,它是常规语言的一个可有效学习的子类,语法可以用所有合法的 k 子序列来表征。
最终我也在考虑在 C++ 中这样做,其中 std::make_tuple
不如 Python tuple
强大。)
您需要 n
项的一组 r
组合(w/o 替换,<= (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 - more_itertools.distinct_combinations
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 不计算重复的中间体
通过 > pip install more_itertools
.
安装 more_itertools
另见 itertools.combinations
的 rough implementation 书面形式 Python。
我正在尝试编写一个 Python(至少在最初)函数来生成某个长度为 k(其中 k > 0)的所有子序列。因为我只需要唯一的子序列,所以我将子序列和部分子序列都存储在 set
中。以下内容改编自一位同事,是我能想到的最好的。似乎...过于复杂...而且我应该能够滥用 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-strictly piecewise languages 的归纳感兴趣,它是常规语言的一个可有效学习的子类,语法可以用所有合法的 k 子序列来表征。
最终我也在考虑在 C++ 中这样做,其中 std::make_tuple
不如 Python tuple
强大。)
您需要 n
项的一组 r
组合(w/o 替换,<= (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 - more_itertools.distinct_combinations
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 不计算重复的中间体
通过 > pip install more_itertools
.
more_itertools
另见 itertools.combinations
的 rough implementation 书面形式 Python。