[英]How to build a dynamically growing nested list comprehension?
假设有以下代码检查数字的乘数是否等于输入数字:
results = [a for a in range(10) if a == input]
results += [a*b for a in range(10) for b in range(10) if a*b == input]
results += [a*b*c for a in range(10) for b in range(10) for c in range(10) if a*b*c == input]
...
我希望更改它,以便在没有找到结果的情况下继续动态搜索匹配项。 所以:
我想以一种优雅的方式做到这一点,即使这种方式不太复杂,也可以采用单线方式。 如果根本没有匹配项,我还需要一个中断条件来避免无限循环。 例如,如果输入的质数> 10,则没有结果。 中断条件应类似于:
if(math.pow(2, countOfDigits) > input):
return
其中countOfDigits
是嵌套列表countOfDigits
中当前检查的位数。 换句话说,我的初始示例的第一行代表countOfDigits == 1
,第二行countOfDigits == 2
和第三行countOfDigits == 3
。
哦,那就继续:
next(
sum(x)
for i in range(1, input) # overkill
for x in itertools.product(range(10), repeat = i)
if reduce(operator.mul, x) == input
)
编辑:您已更改问题以返回乘积而不是总和,所以吐出input
而不是sum(x)
。
我不确定您是否要进行第一个匹配,还是想要所有匹配都等于最小因子的匹配。 如果是后者,则可以通过将input, i
从此迭代器中吐出,然后使用itertools.groupby
根据元组中的第二个值来收集它们,然后仅取结果中的第一个值并进行迭代来实现它可以获取所有匹配项(尽管,由于您现在输出的input
可能与长度无关,所以没有意思)。
编辑:
您想要的是可以迭代的东西,但是直到尽可能晚才起作用。 那不是列表,因此列表理解是错误的工具。 幸运的是,您可以使用生成器表达式 。 您的代码非常复杂,因此我们可能希望使用在标准库itertools
定义的一些帮助程序。
让我们先看一下零件的一般情况:
[n
for x0 in range(10)
for x1 in range(10)
...
for xn in range(10)
if x0 * x1 * ... * xn == input]
我们可以归纳为三个部分。 我们将从嵌套的for循环开始,作为N的参数。为此,我们将使用itertools.product
,它需要一个序列序列,例如[range(10), range(10), ... , range(10)]
并从这些序列中产生所有可能的项目组合。 在多次遍历一个序列的特殊情况下,您可以将嵌套深度作为repeat
传递,这样我们就可以:
[n
for x in itertools.product(xrange(10), repeat=n)
if x[0] * x[1] * ... * x[n] == input]
对于输出中的总和,我们可以使用sum()
将其展平为单个值,对于乘积,实际上并没有等效项。 我们可以用reduce
做成一个函数,并将两个数字相乘,这可以从另一个标准库中获得: operator.mul
:
(n
for x in itertools.product(xrange(10), repeat=n)
if reduce(operator.mul, x, 1) == input)
到目前为止,到目前为止,我们只需要对每个n值重复此内部部分。 假设我们要永远搜索,我们可以使用itertools.count(1)
获得一个无穷无尽的数字序列,最后,我们只需要将此平坦的序列转换为单个序列,就可以使用itertools.chain.from_iterable
itertools.chain.from_iterable(
(n
for x in itertools.product(xrange(10), repeat=n)
if reduce(operator.mul, x, 1) == input)
for n in itertools.count(1)
if 2 ** n > input))
In [32]: input = 32
In [33]: next(itertools.chain.from_iterable(
(n
for x in itertools.product(xrange(10), repeat=n)
if reduce(operator.mul, x, 1) == input)
for n in itertools.count(1) if 2 ** n > input))
Out[33]: 6
我认为您不希望列表理解。 我认为发电机在这里会更好:
def generate(x):
for digits in itertools.count(1):
for i in itertools.product(range(1, 10), repeat=digits):
if reduce(operator.mul, i) == x:
yield i
if (math.pow(2, digits) > x):
break
然后,您for i in generate(input_number)
做。
(您还需要from functools import reduce
import itertools
到顶部的from functools import reduce
和import math
)。
((math.pow(2,digits)> x)只是中断条件。)
您有几个序列,每个序列可能包含也可能不包含解决方案。 将它们转换为生成器,使用itertools
将序列“链接”在一起,然后询问结果序列的第一个元素(请注意,不包含解决方案的序列将为空)。
首先,您需要一种为任何n
生成n
元组的序列的方法。 itertools
模块具有几个功能,这些功能的效果可与嵌套的for循环相比。 与您的算法匹配的一个是itertools.product
。 以下生成所有n
位元组:
tuples = itertools.product(range(10), repeat=n)
使用itertools.combinations_with_replacement
实际上更好,因为(4,5)
和(5,4)
都没有测试点。 它具有相似的语法。 所以这是一个生成器,它将为您提供无限个n元组序列,以增加n
:
sequences = ( itertools.product(range(10), repeat=n) for n in itertools.count(1) )
接下来,您想将序列串在一起(实际上尚未遍历它们),成为一个序列。 由于这些是生成器,因此仅在需要时才对其进行评估。
bigchain = itertools.chain.from_iterable(sequences)
bigchain
将吐出您需要检查的所有元组。 为了测试它们,您需要一种将任意长度的元组相乘的方法。 让我们定义一下:
def mytest(x):
return reduce(operator.mul, x, 1) == target
现在,您可以使用此测试来“过滤”此序列,以仅选择匹配的元组(由于组合中包含数字1
,因此将总是有很多元组),然后请求第一个。
print itertools.islice(ifilter(mytest, bigchain), 1).next()
我更改了代码,以元组形式返回解决方案,因为否则,您会得到想要搜索的数字(例如32
),但不会告诉您您不知道的任何内容!
这是一起:
from itertools import *
import operator
target = 32
sequences = ( combinations_with_replacement(range(10), n) for n in count(1) )
bigchain = chain.from_iterable(sequences)
def mytest(x):
return reduce(operator.mul, x, 1) == target
print islice(ifilter(mytest, bigchain), 1).next()
# prints (4, 8)
您还可以消除上面的中间变量,并将所有内容组合为一个表达式。 但是有什么意义呢?
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.