簡體   English   中英

什么是計算Python中整數列表中唯一乘法和加法對的數量的有效方法?

[英]What is an efficient way of counting the number of unique multiplicative and additive pairs in a list of integers in Python?

給定一個排序的數組A = [n,n + 1,n + 2,... n + k]元素,我試圖計算乘法和加法對的唯一數,使得條件xy> = x + y是滿意。 其中x和y是列表的索引,y> x。

這是我使用天真蠻力方法的最低工作示例:

def minimum_working_example(A):
    A.sort()
    N = len(A)
    mpairs = []
    x = 0
    while x < N:
        for y in range(N):
            if x<y and (A[x]*A[y])>=(A[x]+A[y]):
                mpairs.append([A[x], A[y]])               
            else:
                continue    
        x+=1
    return len(mpairs)  

A = [1,2,3,4,5]
print(minimum_working_example(A))
#Output = 6, Unique pairs that satisfy xy >= x+y: (2, 3), (2, 4), (2, 5), (3, 4), (3, 5), (4, 5)

然而,這種方法對於大型列表具有指數時間復雜度。

有哪些排序或搜索算法可以讓我實現更有效的解決方案?

這個問題有一個封閉形式的數學解決方案,但如果您更喜歡在編程語言中實現,您只需要從列表中找到所有唯一的數字對,並計算滿足您要求的數字。 itertools.combinations是你的朋友:

import itertools

A = [1,2,3,4,5]
pairs = []
for x, y in itertools.combinations(A, 2):
    if x*y >= x + y:
        pairs.append((x,y))

產量

[(2, 3), (2, 4), (2, 5), (3, 4), (3, 5), (4, 5)]

基本代數......根據另一個變量求解一個變量:

xy >= x + y
xy - y >= x
y(x-1) >= x

現在,如果你的元素都是正整數,那么你就得到了

if x == 1, no solution
if x == 2, y >= 2
else x > 2
y >= x/(x-1)

在最后一種情況下,x /(x-1)是介於1和2之間的分數; 再次,

y >= 2

解決不平等問題。

這為您提供了O(1)時間內易於訪問的解決方案; 如果你想要這些對,你就會被打印限制,這是O(n ^ 2)時間。

所以使用x*y >= x+y的事實如果兩者 (我的原始評論中的錯誤) xy都是>=2 (請參閱@ Prune的答案以獲取詳細信息),那么您也可以從列表中刪除01如果他們出現,因為他們不會做任何合適的一對。

所以,現在假設所有的數字或>=2 ,你有k他們的(如更換k通過k-1在下面的操作,如果你有n=1 ),對所有可能會滿足你的條件。 並且k元素中的對的數量是眾所周知的公式k*(k-1)/2 (如果你不知道它就谷歌它)。 計算這個數字的時間基本相同(一個乘法,一個除法),無論你有什么k值(除非你開始瘋狂的大數字),所以復雜性是O(1)。

假設你的整數是正數,如果不是,公式會稍微復雜一點,但仍然可以作為封閉形式的解決方案。

如果您想要更多數學解,請考慮xy > x+y沒有y=1 否則,您可以代數地將其解析為x > y/(y-1) 現在,如果我們有兩個連續的整數並將較大的值除以較小的值,我們或者得到正好2(如果y = 2)或得到1到2之間的一些分數。 請注意,x必須大於此y /(y-1)商,但也必須小於y。 如果y = 2,則我們的正整數列表中唯一可能的x值必須為1,在這種情況下沒有匹配,因為1不大於2/1。 所以這一切都簡化為“對於列表中的每個數字y,計算[2,y]范圍內的所有值x。” 如果你做數學運算,這應該是添加1 + 2 + 3 + ... + k,它只是k(k+1)/2 同樣,我們假設n和k是正整數; 考慮到n <= 0的情況,可以得出稍微復雜的公式。

但是假設你想要堅持使用蠻力方法,而不是做一些數學推理來找到不同的方法:我嘗試了幾種變化,這是基於以下的更快的解決方案。

  • 你說列表已經排序了,所以我放棄了排序功能。
  • 同樣,“else:continue”也沒有必要,所以為了簡單起見,我放棄了。
  • 而不是循環遍歷所有x和y值,然后檢查是否x <y,你可以讓你的第二個循環檢查y +在x + 1到y范圍內的值。 但...
  • 您可以使用itertools生成列表A中所有數字的唯一對
  • 如果你最終真的只關心pairs列表的長度而不是數字對本身,那么你可以只計算沿途的對而不是存儲它們。 否則,您可能會在高N值時耗盡內存。
  • 使用x(y-1)-y> 0的等效測試,我獲得稍快的結果。 比x(y-1)> y還要多。

所以這就是我所擁有的:

def example4(A):
    mpair_count = 0
    for pair in itertools.combinations(A, 2):
        if pair[0]*(pair[1]-1) - pair[1] > 0:
            mpair_count += 1
    return mpair_count

這是一切定時:

from timeit import default_timer as timer
import itertools

def minimum_working_example(A):
    A.sort()
    N = len(A)
    mpairs = []
    x = 0
    while x < N:
        for y in range(N):
            if x<y and (A[x]*A[y])>=(A[x]+A[y]):
                mpairs.append([A[x], A[y]])
            else:
                continue
        x+=1
    return len(mpairs)

# Cutting down the range
def example2(A):
    N = len(A)
    mpairs = []
    x = 0
    while x < N:
        for y in range(x+1,N):
            if (A[x]*A[y])>=(A[x]+A[y]):
                mpairs.append([A[x], A[y]])
        x += 1
    return len(mpairs)

# Using itertools
def example3(A):
    mpair_count = 0
    for pair in itertools.combinations(A, 2):
        if pair[0]*pair[1] > sum(pair):
            mpair_count += 1
    return mpair_count

# Using itertools and the different comparison
def example4(A):
    mpair_count = 0
    for pair in itertools.combinations(A, 2):
        if pair[0]*(pair[1]-1) - pair[1] > 0:
            mpair_count += 1
    return mpair_count

# Same as #4, but slightly different
def example5(A):
    mpair_count = 0
    for pair in itertools.combinations(A, 2):
        if pair[0]*(pair[1]-1) > pair[1]:
            mpair_count += 1
    return mpair_count

A = range(1,5000)
start = timer()
print(minimum_working_example(A))
end = timer()
print(end - start)

start = timer()
print(example2(A))
end = timer()
print(end - start)


start = timer()
print(example3(A))
end = timer()
print(end - start)

start = timer()
print(example4(A))
end = timer()
print(end - start)

start = timer()
print(example5(A))
end = timer()
print(end - start)

結果:

12487503
8.29403018155
12487503
7.81883932384
12487503
3.39669140954
12487503
2.79594281764
12487503
2.92911447083

暫無
暫無

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

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