假设有以下代码检查数字的相乘数字是否等于输入数字:
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 == 1
,第二行代表countOfDigits == 2
,第三行代表countOfDigits == 3
。
答案 0 :(得分:7)
哦,继续吧:
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
除了可能的长度之外还有其他无趣的感觉。)
答案 1 :(得分:2)
编辑:
你想要的是一个可以迭代的东西,但是直到尽可能晚才起作用。这不是一个列表,因此列表理解是错误的工具。幸运的是,您可以使用生成器表达式。您的代码非常复杂,因此我们希望在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
答案 2 :(得分:1)
我认为你不想要列表理解。我认为发电机在这里会更好:
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)
。
(您还需要import itertools
,from functools import reduce
和import math
位于顶部。)
((math.pow(2,digits)> x)只是休息条件。)
答案 3 :(得分:1)
您有多个序列,每个序列可能包含也可能不包含解决方案。将它们转换为生成器,使用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
,因此总是会有很多),然后请求第一个。< / p>
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)
您还可以消除上面的中间变量,并将所有内容组合成一个表达式;但有什么意义呢?