【问题标题】:Generate kth combination without generating/iterating previous生成第 k 个组合而不生成/迭代先前的
【发布时间】:2013-08-30 01:44:30
【问题描述】:

给定一组项目,例如:

[ 1, 2, 3, 4, 5, 6 ]

我想生成一定长度的所有可能的重复组合。扭曲是我想从预先确定的组合开始(组合列表中的一种偏移量)。

例如,从这个开始:

[ 1, 5, 6 ]

第一个(下一个)组合是:

[ 1, 6, 6 ]

我已经成功地使用itertools.combinations_with_replacement() 生成组合,但是这个项目需要使用一个生成太多组合的集合——首先创建它们并迭代到正确的点是不可能的。

我发现this example for generating kth combination 对我来说似乎不太好用。 This answer 似乎是另一种可能性,但我似乎无法将它从 C 移植到 Python。

这是我目前使用kth combination example的代码:

import operator as op
items = [ 1,2,3,4,5,6 ]

# https://stackoverflow.com/a/4941932/1167783
def nCr(n, r):
    r = min(r, n-r)
    if r == 0: 
        return 1
    numer = reduce(op.mul, xrange(n, n-r, -1))
    denom = reduce(op.mul, xrange(1, r+1))
    return numer // denom

# https://stackoverflow.com/a/1776884/1167783
def kthCombination(k, l, r):
    if r == 0:
        return []
    elif len(l) == r:
        return l
    else:
        i = nCr(len(l)-1, r-1)
        if k < i:
            return l[0:1] + kthCombination(k, l[1:], r-1)
        else:
            return kthCombination(k-i, l[1:], r)

# get 1st combination of 3 values from list 'items' 
print kthCombination(1, items, 3)

# returns [ 1, 2, 4 ]

任何帮助都会很棒!

【问题讨论】:

    标签: python combinations itertools


    【解决方案1】:

    如果您假设数组中的所有值都是以 n 为基数的编号系统中的数字,其中 n 是数组的长度,则第 k 个组合将等效于以基数 n 表示的 k。

    如果您想从给定的组合(即 [1,6,5])开始并从那里继续,只需将此起点读取为 base-n 中的数字。然后,您可以通过递增开始迭代连续组合。

    编辑:进一步解释:

    让我们从数组开始。该数组包含 6 个值,因此我们使用 base-6。我们将假设数组中每个元素的索引是元素的 base-6 值。

    base-6 的值范围从 0 到 5。这可能会让人感到困惑,因为我们的示例使用了数字,但我们可以使用任何组合来做到这一点。我将在我们组合的数字周围加上“引号”。

    给定一个组合 ['1', '6', '5'],我们首先需要将其转换为 base-6 值。 '1' 变为 0,'6' 变为 5,'5' 变为 4。使用它们在起始值中的位置作为它们在 base-6 中的幂,我们得到:

    (0 * 6^0) + (5 * 6^1) + (4 * 6^2) = 174(十进制)

    如果我们想知道下一个组合,我们可以加1。如果我们想知道前面的20个组合,我们就加20。我们也可以减去向后退。让我们将 1 添加到 174 并将其转换回 base-6:

    175 (十进制) = (1 + 6^0) + (5 * 6^1) + (4 * 6^2) = 451 (base-6) = ['2', '6', '5 '](组合)

    有关数字基础的更多信息,请参阅 http://en.wikipedia.org/wiki/Radixhttp://en.wikipedia.org/wiki/Base_%28exponentiation%29

    【讨论】:

    • 正如马里奥·罗西(Mario Rossi)的回答中提到的,我的数学不太好,我在遵循这一点时遇到了麻烦。这个答案和马里奥的基本一样吗?
    • 我的回答是寻找排列的一般情况。马里奥的回答是针对 itertools 的。
    【解决方案2】:

    您可以映射数字,而不是发明轮子时间数字 37,289,423,987,239,489,826,364,653(仅计算人类)。 itertools 将返回第一个组合 [1,1,1],但您想要 [1,5,6]。只需将 [0,4,5] mod 6 添加到每个位置。当然,您还可以在数字、对象和模数之间来回映射。

    即使每个位置的元素数量不同,这也有效。

    不过,您会从开始的内容中获得更多乐趣。

    【讨论】:

    • 因为二次复杂度?
    • @pqnet 根据 OP,扭曲是从预定的组合开始的,而不是性能。此外,排列生成不是你说的 cuadratic,而是 n^m。问题是,这不是解决方案 的大小,而是问题 的大小。换句话说,我们问题的“N”实际上是 n^m。从这个角度来看,所有的置换生成算法实际上都是 O(1)(或非常接近)。
    • @pqnet 现在,如果您向我提出参数nm 的问题,并且在我的解决方案中我说“我们需要生成这些组合/排列”,那么是的:对于那个问题,我的算法n^m
    • 由于所有排列实际上都是 n^m,因此不可能在比打印出来更短的时间内生成它们
    • 有趣的解决方案 - 我的数学有点生疏,无法在这里跟随 cmets :) 简而言之,这个想法是将偏移(起始)组合添加到第一个组合(例如 [ 1, 1, 1 ]并从那里继续?我假设一个人会在 7 = 0 等处“环绕”?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-06-18
    • 2017-09-13
    • 2013-07-28
    • 1970-01-01
    • 1970-01-01
    • 2011-01-10
    • 1970-01-01
    相关资源
    最近更新 更多