使用Map Iterator测试集合交点

时间:2014-08-28 23:08:56

标签: python python-3.x

我试图编写一个函数来查找弱Sidon序列(一个序列( i ),其中 i + a j < / sub>对于任意长度的所有i

这是我到目前为止所做的:

def weakSidon(sequence_length):
    """Finds a weak Sidon sequence (a sequence (a_i) of integers where a_i + a_j for i < j are all unique) of the necessary length."""
    sequence = [1]
    sums = []
    while len(sequence) < sequence_length:
        test_integer = sequence[-1] + 1
        test_sums = list(map(lambda x: test_integer + x, sequence))
        while any(x in list(test_sums) for x in sums):
            test_integer = test_integer + 1
            test_sums = list(map(lambda x: test_integer + x, sequence))
        sequence.append(test_integer)
        sums = sums + test_sums
    return sequence

这是有效的(并且,正如我在实现这一点后所认识到的那样,最终只是生成斐波纳契序列的第一个元素的愚蠢方法),但是将地图迭代器转换为列表然后立即在生成器中迭代它在下一行似乎很愚蠢,如果可能的话,我想知道如何避免未来的污染。

任何其他简化的一般性建议(特别是对于重复的test_sums任务)当然是值得赞赏的。

2 个答案:

答案 0 :(得分:0)

为某些迭代器x in a评估a会消耗部分或全部a。评估list(a)将消耗所有a。在您的生成器(x in list(test_sums) for x in sums)中,对x in list(test_sums)中的每个元素计算表达式sums一次。

因此,如果您没有立即对map的结果进行分类,那么您的支票(x in list(test_sums) for x in sums)将无法按预期工作 - 到sums的后续元素进行测试时, test_sums已被使用,表达式list(test_sums)[]。我发现在解释器中查看这种情况的一个更简单的例子很有帮助。

Python实际上有一个内置的设置数据类型(link)。确定集合中的成员资格是一个固定时间操作,所以如果你正在进行大量的成员资格检查(比如这里),那么使用它们是个好主意。但是我们实际上可以摆脱any(x in a for x in b)构造(它很漂亮)并且只是直接构造,因为Python还定义了集合上的交集和并集。如果ab是两套,那么a | b就是他们的联盟,a & b就是他们的交集。

所以使用集合的代码将是:

def weakSidon(sequence_length):
    sequence = [1]
    sums = set()
    while len(sequence) < sequence_length:
        test_integer = sequence[-1] + 1
        test_sums = {test_integer + n for n in sequence}
        while test_sums & sums:
            test_integer += 1
            test_sums = {test_integer + n for n in sequence}
        sequence.append(test_integer)
        sums |= test_sums
    return sequence

|=运算符是| +=+的所有内容。

我之前从未听说过Sidon序列,但我认为Fibonaccis变成一个非常有趣。

答案 1 :(得分:0)

您的代码存在两个主要问题。

首先,每当您测试一笔金额是否在list列表中时,您就会调用test_sumsall(x in list(test_sums) for x in sums)多次调用list,因为test_sums已经是一个列表,所以这只是浪费的周期。

第二个问题是列表成员资格测试通常很慢,即使您没有先复制列表。测试set中的成员资格要快得多,所以你应该在这里使用一个。

如果您设置sums一个集合,并在all调用中更改正在迭代的序列以利用快速set.__contain__测试,您将获得类似的内容这样:

def weakSidon(sequence_length):
    """Finds a weak Sidon sequence (a sequence (a_i) of integers where
       a_i + a_j for i < j are all unique) of the necessary length."""
    sequence = [1]
    sums = set()                                   # start with an empty set
    while len(sequence) < sequence_length:
        test_integer = sequence[-1] + 1
        test_sums = list(map(lambda x: test_integer + x, sequence))
        while any(x in sums for x in test_sums):   # testing set membership is O(1)
            test_integer = test_integer + 1
            test_sums = list(map(lambda x: test_integer + x, sequence))
        sequence.append(test_integer)
        sums.update(test_sums)                     # update set items
    return sequence

您无法摆脱一个list调用将map生成器变为实际列表,因为您需要再次访问test_sumsupdate如果没有碰撞,请执行步骤。