繁体   English   中英

找到两个乘以20的整数。我可以使这个代码更“pythonic”吗?

[英]Finding two integers that multiply to 20. Can I make this code more “pythonic”?

我做了这个代码,在所说的列表中找到两个整数(在这种情况下为[2,4,5,1,6,40,-1]),乘以二十。 我在开始时遇到了一点困难,但添加一个功能解决了我的问题。 我向我的一位朋友展示了这段代码,他是一名程序员,他说我可以让这段代码更“pythonic”,但我不知道如何。

这是代码:

num_list = [2,4,5,1,6,40,-1]

def get_mult_num(given_list):
    for i in given_list:
        for j in range(i+1, len(given_list)): #for j not to be == i and to be in the list
            mult_two_numbers = i * j
            if mult_two_numbers == 20:
                return i,j

print(get_mult_num(num_list)) 

我不一定认为它是'unpythonic',你使用标准的Python惯用语循环数据并生成单个结果或None Pythonic这个词是模糊的,一个主题是“当我看到它时我知道它”的参数。

并不是说你产生了正确的实现。 i循环遍历given_numbersj循环遍历从i + 2len(given_numbers)的整数,将given_list值与索引混合? 对于你的样本输入,你从半开范围[4,7],[6,7],[7,7](空),[3,7],[8,7](空)中取j ,[42,7](空)和[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循环或切片或其他形式的过滤的工具。

你的双循环应该生成整数输入的组合 ,所以使用itertools.combinations()对象生成唯一的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()函数

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和它的平方根s ,根据定义s * (s + 1)将大于n )。

如果我们为函数添加目标数的参数并使其成为生成器函数 ,那么您将获得:

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个不同的随机目标,以避免两种方法都出现幸运的最佳情况。

[(i,j) for i in num_list for j in num_list if i<j and i*j==20]

我可以想到使用list-comprehension。 如果它们存在于给定列表中,这也有助于找到多个这样的对。

num_list = [2,4,5,1,6,40,-1]

mult_num = [(num_list[i],num_list[j]) for i in range(len(num_list)) for j in range(i+1, len(num_list)) if num_list[i]*num_list[j] == 20]
print mult_num

输出:

[(4, 5)]

这是我对它的看法,它使用enumerate

def get_mult_num(given_list):
    return [
        item1, item2
        for i, item1 in enumerate(given_list)
        for item2 in given_list[:i]
        if item1*item2 == 20
    ]

我认为你的朋友可能会暗示在使代码更清晰时使用理解 (有时它不会)。

您可以通过使用itertools.combinations而不是嵌套循环来使其更加pythonic,以查找所有数字对。 并非总是如此,但经常迭代索引, for i in range(len(L)):比直接迭代值更少pythonic,就像for v in L:for v in L:

Python还允许你通过yield关键字将你的函数变成一个生成器,这样你就可以通过遍历函数调用来获得每一对,而不是只返回乘以20的第一对。

import itertools

def factors(x, numbers):
    """ Generate all pairs in list of numbers that multiply to x.
    """
    for a, b in itertools.combinations(numbers, 2):
        if a * b == x:
            yield (a, b)

numbers = [2, 4, 5, 1, 6, 40, -1]
for pair in factors(20, numbers):
    print(pair)

我想出了这个。 它稍微改变了方法,因为它在num_list搜索迭代值val将乘以20所需的配对伙伴。 这使得代码更容易,并且不需要导入,即使它不是最有效的方式。

for val in num_list:    
    if 20 / val in num_list:
        print(val, int(20/val))

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM