【问题标题】:all permutations of +-r, +-s+-r, +-s 的所有排列
【发布时间】:2017-07-26 08:48:43
【问题描述】:

给定两个数字rs,我想获得n +-rm +-s 的所有排列列表。例如(使用r=3.14s=2.71),

n = 1
m = 1
out = [
    (+r, +s), (+r, -s), (-r, +s), (-r, -s), 
    (+s, +r), (+s, -r), (-s, +r), (-s, -r)
    ]
n = 1
m = 2
out = [
    (+r, +s, +s), (+r, -s, +s), (-r, +s, +s), (-r, -s, +s), ...
    (+s, +r, +s), (-s, +r, +s), (+s, -r, +s), (-s, -r, +s), ...
    ...
    ]

使用itertools.product([+r, -r], repeat=n),我可以分别获得rs 和ss 的列表,我只需要将它们交织在一起,但我不确定这是否是正确的做法。

效率并不过分重要,所以我不介意产生许多重复结果的解决方案,只是为了使它们在之后变得独一无二。

【问题讨论】:

    标签: python combinatorics itertools


    【解决方案1】:

    更新:添加了通用解决方案。

    这里有一个解决方案,代码稍微复杂一些,但不会产生重复元素,可以懒惰地求值:

    from itertools import combinations, product, chain
    
    r = 3.14
    s = 2.71
    n = 1
    m = 2
    idx = combinations(range(n + m), n)
    vs = ((r if j in i else s for j in range(n + m)) for i in idx)
    res = chain.from_iterable(product(*((+vij, -vij) for vij in vi)) for vi in vs)
    print("\n".join(map(str, res)))
    

    输出:

    (3.14, 2.71, 2.71)
    (3.14, 2.71, -2.71)
    (3.14, -2.71, 2.71)
    (3.14, -2.71, -2.71)
    (-3.14, 2.71, 2.71)
    (-3.14, 2.71, -2.71)
    (-3.14, -2.71, 2.71)
    (-3.14, -2.71, -2.71)
    (2.71, 3.14, 2.71)
    (2.71, 3.14, -2.71)
    (2.71, -3.14, 2.71)
    (2.71, -3.14, -2.71)
    (-2.71, 3.14, 2.71)
    (-2.71, 3.14, -2.71)
    (-2.71, -3.14, 2.71)
    (-2.71, -3.14, -2.71)
    (2.71, 2.71, 3.14)
    (2.71, 2.71, -3.14)
    (2.71, -2.71, 3.14)
    (2.71, -2.71, -3.14)
    (-2.71, 2.71, 3.14)
    (-2.71, 2.71, -3.14)
    (-2.71, -2.71, 3.14)
    (-2.71, -2.71, -3.14)
    

    说明

    我们可以将输出视为包含 n +/- r 元素和 m +/- s 元素的排列,或者换句话说,n + m 元素的元组其中n 是 +/- r,其余的是 +/- sidx 包含具有 +/- r 元素的所有可能位置的元组;例如,第一个结果是(0,)

    然后,对于这些元组i 中的每一个,我们在vs 中创建“模板”元组,它们只是大小为n + m 的元组,其中i 中的索引是r,其余的是s。因此,对于idx 中的元组(0,),您将得到(r, s, s)。如果n + m 非常大,您可以考虑上一步idx = map(set, idx) 以更快地进行in 操作,但我不确定在哪一点值得这样做。

    最后,对于v 中的每个vi 模板,我需要考虑对其每个元素使用正值和负值的所有可能性。所以它是(+vi[0], -vi[0]), (+vi[1], -vi[1]), ... 的笛卡尔积。最后,您只需链接每个产品的每个生成器即可获得最终结果。

    一般解决方案

    要为任意数量的不同元素构建问题的通用解决方案,您需要考虑索引集的分区。例如,对于n = 3m = 5,您可以使用所有可能的方式将{0, 1, 2, 3, 4, 5, 6, 7} 分成大小为3 和5 的两部分。下面是一个实现:

    from itertools import chain, repeat, permutations, product
    
    
    def partitions(*sizes):
        if not sizes or all(s <= 0 for s in sizes):
            yield ()
        for i_size, size in enumerate(sizes):
            if size <= 0:
                continue
            next_sizes = sizes[:i_size] + (sizes[i_size] - 1,) + sizes[i_size + 1:]
            for p in partitions(*next_sizes):
                yield (i_size,) + p
    
    
    def signed_permutations(*elems):
        values, sizes = zip(*elems)
        templates = partitions(*sizes)
        return chain.from_iterable(
            product(*((+values[ti], -values[ti]) for ti in t)) for t in templates)
    
    
    r = 3.14
    s = 2.71
    n = 1
    m = 2
    res = signed_permutations((r, n), (s, m))
    print("\n".join(map(str, res)))
    

    这个想法是一样的,你构建“模板”(这次它们包含值的索引而不是值本身),然后是它们的笛卡尔积。

    【讨论】:

    • 这似乎不起作用:list(itertools.product(*([[+r, -r]] * 1 + [[+s, -s]] * 1))) 给了[(3.14, 2.71), (3.14, -2.71), (-3.14, 2.71), (-3.14, -2.71)] -- 3.14 永远不会排在第二位。
    • 一两句解释肯定有助于理解代码。
    • @NicoSchlömer 对,我已经添加了一些解释,如果不清楚,请告诉我。
    • @NicoSchlömer 又一次更新,为任意数量的不同元素提供了通用解决方案。
    • 可以使用 Knuth 的“算法 L”,而不是您自己的 partitions,例如在 stackoverflow.com/questions/4250125/… 实现。
    【解决方案2】:

    您还可以将rspermutations+1-1zipproduct 结合起来。这样,整个结构就更易读了,恕我直言:

    >>> n, m = 1, 2
    >>> r, s = 3.14, 2.71
    >>> [[x*i for x,i in zip(perm, prod)] for perm in permutations([r]*n + [s]*m) 
    ...                                   for prod in product((+1, -1), repeat=n+m)]
    [[3.14, 2.71, 2.71],
     [3.14, 2.71, -2.71],
     ...
     [-2.71, -2.71, 3.14],
     [-2.71, -2.71, -3.14]]
    

    【讨论】:

      【解决方案3】:

      首先使用product,然后在每个元素上使用permutations。然后连接所有结果并将它们传递给set() 以删除重复项:

      arr = set(itertools.chain.from_iterable([
          itertools.permutations(x)
          for x in itertools.product(*([[+r, -r]] * n + [[+s, -s]] * m))
          ]))
      print(arr)
      

      【讨论】:

        猜你喜欢
        • 2018-10-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-06-21
        • 2020-04-17
        • 1970-01-01
        • 2018-03-07
        相关资源
        最近更新 更多