按总和顺序生成组合

问题描述 投票:0回答:1

Itertools组合好像是按照字典顺序出来的:

>>> for c in combinations([9,8,7,2,2,1], 2):
...     print(c, sum(c))
... 
(9, 8) 17
(9, 7) 16
(9, 2) 11
(9, 2) 11
(9, 1) 10
(8, 7) 15
(8, 2) 10
(8, 2) 10
(8, 1) 9
(7, 2) 9
(7, 2) 9
(7, 1) 8
(2, 2) 4
(2, 1) 3
(2, 1) 3

但我想按总和最大的顺序生成它们:

>>> for c in sorted(combinations([9,8,7,2,2,1], 2), key=sum, reverse=True):
...     print(c, sum(c))
... 
(9, 8) 17
(9, 7) 16
(8, 7) 15
(9, 2) 11
(9, 2) 11
(9, 1) 10
(8, 2) 10
(8, 2) 10
(8, 1) 9
(7, 2) 9
(7, 2) 9
(7, 1) 8
(2, 2) 4
(2, 1) 3
(2, 1) 3

实际上全部生成它们并且无法排序,因为结果太大(这是一个较小的例子)。

我认为可以将 docs 中提供的配方修改为“大致等效”的 python 代码:

def combinations(iterable, r):
    pool = tuple(iterable)
    n = len(pool)
    if r > n:
        return
    indices = list(range(r))
    yield tuple(pool[i] for i in indices)
    while True:
        for i in reversed(range(r)):
            if indices[i] != i + n - r:
                break
        else:
            return
        indices[i] += 1
        for j in range(i+1, r):
            indices[j] = indices[j-1] + 1
        yield tuple(pool[i] for i in indices)

但想不通。

如果有帮助,r 将始终为 2,并且输入足够小,可以将其排序到单调非递减列表中。

python combinations python-itertools combinatorics
1个回答
2
投票

反向排序。然后每个数字可以与所有后面的数字配对,提供具有非升序和的对序列。合并所有这些序列。

from heapq import merge

def combinations(xs):
    xs.sort(reverse=True)
    def pairs(ix):
        i, x = ix
        for j in range(i+1, len(xs)):
            yield x, xs[j]
    return merge(
        *map(pairs, enumerate(xs)),
        key=sum,
        reverse=True
    )

for c in combinations([9,8,7,2,2,1]):
    print(c, sum(c))

替代版本,玩转迭代工具:

from heapq import merge
from itertools import repeat
from copy import copy

def combinations(xs):
    xs.sort(reverse=True)
    it = iter(xs)
    return merge(
        *map(zip,
             map(repeat, it),
             map(copy, repeat(it))),
        key=sum,
        reverse=True
    )

for c in combinations([9,8,7,2,2,1]):
    print(c, sum(c))
© www.soinside.com 2019 - 2024. All rights reserved.