我不一定认为它是“unpythonic”,您正在使用标准 Python 习惯用法来循环数据并生成单个结果或None。 Pythonic 一词含糊不清,是一个在"I know it when I see it" 参数中受损的主题。
并不是说你产生了一个正确的实现。当i 循环given_numbers 时,j 循环从i + 2 到len(given_numbers) 的整数,将given_list 的值与索引混合?对于您的样本输入,您从半开范围 [4, 7), [6, 7), [7, 7) (空), [3, 7), [8, 7) ( empty)、[42, 7) (empty) 和 [1, 7) 分别。它产生正确的答案完全是运气,而不是由于正确性;如果你给你的函数列表[2, 10],它将找不到解决方案!你想循环given_numbers再次,受限于切片,或者从i的当前索引开始生成索引,但是你的外部循环也需要添加一个enumerate()调用:
for ii, i in enumerate(given_numbers):
for j in given_numbers[ii + 1:]:
# ...
或
for ii, i in enumerate(given_numbers):
for jj in range(ii + 1, len(given_numbers)):
j = given_numbers[jj]
# ...
所有这一切都没有尽可能高效; Python 标准库为您提供了生成 i, j 对的工具,无需嵌套 for 循环或切片或其他形式的过滤。
您的双循环应生成整数输入的combinations,因此请使用itertools.combinations() object 生成唯一的i, j 对:
from itertools import combinations
def get_mult_num(given_list):
return [(i, j) for i, j in combinations(given_list, 2) if i * j == 20]
这假设可以有零个或多个这样的解决方案,而不仅仅是一个解决方案。
如果您只需要第一个结果或None,您可以使用next() function:
def get_mult_num(given_list):
multiplies_to_20 = (
(i, j) for i, j in combinations(given_list, 2)
if i * j == 20)
return next(multiplies_to_20, None)
接下来,您可能想要反转问题,而不是产生所有可能的组合。如果你把given_list 变成一个集合,你可以简单地检查目标数字 20 是否可以被任何给定的数字干净地整除,并且除法的结果更大 并且 也是一组数字中的一个整数。这会在线性时间内为您提供答案。
您可以通过除以小于目标值平方根的数字来进一步限制搜索,因为您不会在输入数字中找到更大的值来匹配(给定一个数字n,它是平方根@ 987654350@,根据定义,s * (s + 1) 将大于n)。
如果我们将目标编号的参数添加到函数并使其成为generator function,那么您会得到:
def gen_factors_for(target, numbers):
possible_j = set(numbers)
limit = abs(target) ** 0.5
for i in numbers:
if abs(i) < limit and target % i == 0:
j = target // i
if j in possible_j and abs(j) > abs(i):
yield i, j
这种方法比测试所有排列要快很多,尤其是当您需要找到所有可能的因素时。请注意,我在这里制作了两个函数生成器以平衡比较:
>>> import random, operator
>>> from timeit import Timer
>>> def gen_factors_for_division(target, numbers):
... possible_j = set(numbers)
... limit = abs(target) ** 0.5
... for i in numbers:
... if abs(i) < limit and target % i == 0:
... j = target // i
... if j in possible_j and abs(j) > abs(i):
... yield i, j
...
>>> def gen_factors_for_combinations(target, given_list):
... return ((i, j) for i, j in combinations(given_list, 2) if i * j == target)
...
>>> numbers = [random.randint(-10000, 10000) for _ in range(100)]
>>> targets = [operator.mul(*random.sample(set(numbers), 2)) for _ in range(5)]
>>> targets += [t + random.randint(1, 100) for t in targets] # add likely-to-be-unsolvable numbers
>>> for (label, t) in (('first match:', 'next({}, None)'), ('all matches:', 'list({})')):
... print(label)
... for f in (gen_factors_for_division, gen_factors_for_combinations):
... test = t.format('f(t, n)')
... timer = Timer(
... f"[{test} for t in ts]",
... 'from __main__ import targets as ts, numbers as n, f')
... count, total = timer.autorange()
... print(f"{f.__name__:>30}: {total / count * 1000:8.3f}ms")
...
first match:
gen_factors_for_division: 0.219ms
gen_factors_for_combinations: 4.664ms
all matches:
gen_factors_for_division: 0.259ms
gen_factors_for_combinations: 3.326ms
请注意,我生成了 10 个不同的随机目标,以尽量避免任何一种方法都遇到幸运的最佳情况。