簡體   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