簡體   English   中英

Python 2查找正數的正整數列表

[英]Python 2 lists of positive integers finding prime number

給定2個正整數列表,請找到從每個列表中選擇一個數字的方式,以使它們的和為質數。

我的代碼太慢了,因為我同時擁有list1和list 2,每個列表都包含50000個數字。 那么有什么方法可以使其更快,以便在數分鍾而不是數天內解決? :)

    # 2 is the only even prime number
    if n == 2: return True

    # all other even numbers are not primes
    if not n & 1: return False

    # range starts with 3 and only needs to go 
    # up the squareroot of n for all odd numbers
    for x in range(3, int(n**0.5)+1, 2):
        if n % x == 0: return False

    return True



for i2 in l2:
    for i1 in l1:
        if isprime(i1 + i2):
            n = n + 1 # increasing number of ways
            s = "{0:02d}: {1:d}".format(n, i1 + i2)
            print(s) # printing out

草圖:

  1. 按照@Steve的建議,首先找出所有素數<= max(l1) + max(l2) 我們稱該列表為primes 注意: primes實際上不必是列表; 您可以一次生成最多一次的灌注

  2. 交換列表(如有必要),使l2是最長的列表。 然后將其變成一個集合: l2 = set(l2)

  3. 排序l1l1.sort() )。

然后:

for p in primes:
    for i in l1:
        diff = p - i
        if diff < 0:
            # assuming there are no negative numbers in l2;
            # since l1 is sorted, all diffs at and beyond this
            # point will be negative
            break
        if diff in l2:
           # print whatever you like
           # at this point, p is a prime, and is the
           # sum of diff (from l2) and i (from l1)

l2 ,例如,如果l2是:

l2 = [2, 3, 100000000000000000000000000000000000000000000000000]

這是不切實際的。 它依賴於此,例如在您的示例中, max(max(l1), max(l2))是“合理小”的。

撲出

哼! 您在評論中說,列表中的數字最長為5位數字。 因此,他們不到100,000。 您在一開始就說該列表每個都有50,000個元素。 因此,它們每個都包含100,000以下所有可能整數的一半,並且您將有大量的質數之和。 如果您想進行微優化,那一切就很重要;-)

無論如何,由於最大可能的總和小於200,000,所以任何篩分方法都將足夠快-這將是運行時的重要部分。 這是其余的代碼:

def primesum(xs, ys):
    if len(xs) > len(ys):
        xs, ys = ys, xs
    # Now xs is the shorter list.
    xs = sorted(xs)  # don't mutate the input list
    sum_limit = xs[-1] + max(ys)  # largest possible sum
    ys = set(ys)     # make lookups fast
    count = 0
    for p in gen_primes_through(sum_limit):
        for x in xs:
            diff = p - x
            if diff < 0:
                # Since xs is sorted, all diffs at and
                # beyond this point are negative too.
                # Since ys contains no negative integers,
                # no point continuing with this p.
                break
            if diff in ys:
                #print("%s + %s = prime %s" % (x, diff, p))
                count += 1
    return count

我不會提供gen_primes_through() ,因為它無關緊要。 從其他答案中選擇一個,或編寫自己的答案。

這是提供測試用例的便捷方法:

from random import sample
xs = sample(range(100000), 50000)
ys = sample(range(100000), 50000)
print(primesum(xs, ys))

注意:我正在使用Python3。如果您正在使用Python 2,請使用xrange()而不是range()

在兩次運行中,它們各自花費了大約3.5分鍾。 那就是您一開始所要求的(“分鍾而不是幾天”)。 Python 2可能會更快。 返回的計數為:

219,334,097

219,457,533

當然,可能的總和為50000 ** 2 == 2,500,000,000。

關於計時

此處討論的所有方法(包括您的原始方法)所花費的時間與兩個列表長度的乘積成比例。 所有的擺弄都是為了減少常數因子。 這是對您原始作品的巨大改進:

def primesum2(xs, ys):
    sum_limit = max(xs) + max(ys)  # largest possible sum
    count = 0
    primes = set(gen_primes_through(sum_limit))
    for i in xs:
        for j in ys:
            if i+j in primes:
                # print("%s + %s = prime %s" % (i, j, i+j))
                count += 1
    return count

也許您會更好地理解這一點。 為什么會有很大的改善? 因為它用快速的查找設置代替了昂貴的isprime(n)函數。 它仍然需要與len(xs) * len(ys)成正比的時間,但是通過用非常便宜的運算代替非常昂貴的內循環運算,可以削減“比例常數”。

而且,實際上,在許多情況下, primesum2()也比我的primesum()更快。 您的特定情況下 ,使primesum()更快的原因是,只有不到18,000個質數小於200,000。 因此,遍歷素數(與primesum()一樣)比遍歷具有50,000個元素的列表要快得多。

針對此問題的“快速”通用功能將需要根據輸入選擇不同的方法。

您應該使用Eratosthenes篩子來計算素數。

您還在為每種可能的總和組合計算素數。 而是考慮從列表中求和可以找到最大值。 生成所有素數直至該最大值的列表。

在添加數字的同時,您可以查看數字是否出現在素數列表中。

我會在每個范圍內找到最高的數字。 素數的范圍是最大數字的總和。

這是篩選素數的代碼:

def eras(n):
    last = n + 1
    sieve = [0, 0] + list(range(2, last))
    sqn = int(round(n ** 0.5))
    it = (i for i in xrange(2, sqn + 1) if sieve[i])
    for i in it:
        sieve[i * i:last:i] = [0] * (n // i - i + 1)
    return filter(None, sieve)

查找最多為10000000的素數大約需要3秒鍾。然后,我將使用與生成總和相同的n ^ 2算法。 我認為有一個n logn算法,但我無法提出。

它看起來像這樣:

from collections import defaultdict
possible = defaultdict(int)
for x in range1:
    for y in range2:
        possible[x + y] += 1

def eras(n):
    last = n + 1
    sieve = [0, 0] + list(range(2, last))
    sqn = int(round(n ** 0.5))
    it = (i for i in xrange(2, sqn + 1) if sieve[i])
    for i in it:
        sieve[i * i:last:i] = [0] * (n // i - i + 1)
    return filter(None, sieve)

n = max(possible.keys())
primes = eras(n)
possible_primes = set(possible.keys()).intersection(set(primes))

for p in possible_primes:
    print "{0}: {1} possible ways".format(p, possible[p])

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM