【问题标题】:Nested Permutations from Model模型中的嵌套排列
【发布时间】:2017-06-03 16:37:05
【问题描述】:

我正在编写一个程序,它采用字符串的排列“模型”,并根据该模型输出所有排列。模型看起来像这样:

model = Mix([
    [
        "this",
        PickOne(["is", "isn't"])
    ],
    PickOne([
        Mix([
            "absolutely",
            "great"
        ])
    ])
])

在输出的排列中,

  1. listobjects 会依次输出包含的objects
  2. Mix 对象将以所有可能的顺序输出包含的对象,最大长度(包括零)
  3. PickOne 对象一次只会输出一个包含它的对象

因此,上述示例的期望输出将是:

[
    ["this", "is"],
    ["this", "isn't"],
    ["this", "is", "absolutely"],
    ["this", "is", "great"],
    ["this", "isn't", "absolutely"],
    ["this", "isn't", "great"],
    ["absolutely"],
    ["great"],
    ["absolutely", "this", "is"],
    ["great", "this", "is"],
    ["absolutely", "this", "isn't"],
    ["great", "this", "isn't"],
    []
]

到目前为止,我已经为 Mix 类实现了如下排列:

class Mix(list):
    def __init__(self, *args, **kwargs):
        super(Mix, self).__init__(*args, **kwargs)
        self.permutations = []
        for L in range(0, len(self)+1):
            for subset in itertools.combinations(self, L):
                subset_permutations = itertools.permutations(subset)
                self.permutations.extend(subset_permutations)

而我的PickOne 课程就是这样

class PickOne(list):
    pass

我计划在我的主要功能中进行测试并进行相应处理 (if type(obj) is PickOne: ...)。

itertools 为更简单的用例提供了简化和效率,但是我不知道它将如何帮助我实现这个(超出我已经在Mix 中实现的)。关于如何使用itertools 来帮助实现上述 Pythonic 实现的任何想法?

【问题讨论】:

  • 我很难理解这一点。所以即使是一个简单的“列表”,如果它包含其他“排列模型”,也应该产生这些模型所有可能结果的乘积对吧?
  • 没错。

标签: python algorithm permutation combinatorics itertools


【解决方案1】:

我在考虑所有这些组合时遇到了一些麻烦,但我认为您的 Mix 类也必须产生所有 permutations 中的 product。我创建了一个类似的模型来测试这一点。这可能与您的工作方式有些不同,但应该很容易适应:

class CombModel:
    def __init__(self, *options):
        self.options = options

class Atom(CombModel):
    def __iter__(self):
        for x in self.options:
            yield x

class All(CombModel):
    def __iter__(self):
        for prod in itertools.product(*self.options):
            yield prod

class One(CombModel):
    def __iter__(self):
        for x in self.options:
            for y in x:
                yield y

class Mix(CombModel):
    def __iter__(self):
        for n in range(len(self.options) + 1):
            for perm in itertools.permutations(self.options, n):
                for prod in itertools.product(*perm):
                    yield prod

CombModel 只为所有子类提供了一个 var-arg 构造函数。 Atom 是模型中的一个“叶子”,它就在那里,因此我甚至可以“迭代”单个字符串或整数。这在所有其他类中节省了一些 if/else 逻辑。 All 似乎是模型中的简单列表,产生不同选项的乘积。 OneMix 对应于您的 PickOneMix

使用这个相当简单的模型comb = Mix(All(Atom(1), Atom(21, 22)), One(Atom(31, 32), Atom(4))) 我得到了这些组合:

()
((1, 21),)
((1, 22),)
(31,)
(32,)
(4,)
((1, 21), 31)
((1, 21), 32)
((1, 21), 4)
((1, 22), 31)
((1, 22), 32)
((1, 22), 4)
(31, (1, 21))
(31, (1, 22))
(32, (1, 21))
(32, (1, 22))
(4, (1, 21))
(4, (1, 22))

您的模型转换为:

model = Mix(
    All(
        Atom("this"),
        One(Atom("is"), Atom("isn't"))
    ),
    One(
        Mix(
            Atom("absolutely"),
            Atom("great")
        )
    )
)

并给出这些组合:

()
(('this', 'is'),)
(('this', "isn't"),)
((),)
(('absolutely',),)
(('great',),)
(('absolutely', 'great'),)
(('great', 'absolutely'),)
(('this', 'is'), ())
(('this', 'is'), ('absolutely',))
(('this', 'is'), ('great',))
(('this', 'is'), ('absolutely', 'great'))
(('this', 'is'), ('great', 'absolutely'))
(('this', "isn't"), ())
(('this', "isn't"), ('absolutely',))
(('this', "isn't"), ('great',))
(('this', "isn't"), ('absolutely', 'great'))
(('this', "isn't"), ('great', 'absolutely'))
((), ('this', 'is'))
((), ('this', "isn't"))
(('absolutely',), ('this', 'is'))
(('absolutely',), ('this', "isn't"))
(('great',), ('this', 'is'))
(('great',), ('this', "isn't"))
(('absolutely', 'great'), ('this', 'is'))
(('absolutely', 'great'), ('this', "isn't"))
(('great', 'absolutely'), ('this', 'is'))
(('great', 'absolutely'), ('this', "isn't"))

请注意,这些仍然是嵌套列表(实际上是元组),但之后很容易成为flattened。此外,由于Mix 的“零个或多个”性质,存在一些伪重复项(在展平时将是重复项)。但除此之外,这看起来与您的预期输出相当接近。


仔细观察,可能确实需要先展平,这样 PickOne 只会从 Mix 中选择一个,而不是整个 Mix...

class All(CombModel):
    def __iter__(self):
        for prod in itertools.product(*self.options):
            yield flat(prod) # flatten here

class One(CombModel):
    def __iter__(self):
        for x in self.options:
            for y in flat(x): # flatten here
                yield y

class Mix(CombModel):
    def __iter__(self):
        for n in range(len(self.options) + 1):
            for perm in itertools.permutations(self.options, n):
                for prod in itertools.product(*perm):
                    yield flat(prod) # flatten here

剔除重复后的结果是这样的:

()
('absolutely',)
('absolutely', 'this', 'is')
('absolutely', 'this', "isn't")
('great',)
('great', 'this', 'is')
('great', 'this', "isn't")
('this', 'is')
('this', 'is', 'absolutely')
('this', 'is', 'great')
('this', "isn't")
('this', "isn't", 'absolutely')
('this', "isn't", 'great')

【讨论】:

  • 嗯,当 One 包含 All 对象时,这似乎不起作用 - 它不是选择整个 All,而是选择其内部内容之一。这是一个要点:gist.github.com/flux627/a30bf5a3da08730c7cf2e0083046d694
  • @Julien 这不应该是这样工作的吗?至少这与 One(Mix(...)) 在您的示例中的工作方式一致。另外,我的第一个版本,没有展平,应该像这样工作,一个人选择所有的内容。
  • 你是对的。我的规范并不完美,但我只需在All 中省略flatten 就可以做到这一点。再次感谢
猜你喜欢
  • 2016-05-02
  • 2018-10-22
  • 1970-01-01
  • 2021-01-20
  • 1970-01-01
  • 2018-07-05
  • 2017-09-12
  • 2017-07-06
  • 2019-01-26
相关资源
最近更新 更多