[英]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
的事實如果兩者 (我的原始評論中的錯誤) x
和y
都是>=2
(請參閱@ Prune的答案以獲取詳細信息),那么您也可以從列表中刪除0
和1
如果他們出現,因為他們不會做任何合適的一對。
所以,現在假設所有的數字或>=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的情況,可以得出稍微復雜的公式。
但是假設你想要堅持使用蠻力方法,而不是做一些數學推理來找到不同的方法:我嘗試了幾種變化,這是基於以下的更快的解決方案。
itertools
生成列表A
中所有數字的唯一對 pairs
列表的長度而不是數字對本身,那么你可以只計算沿途的對而不是存儲它們。 否則,您可能會在高N值時耗盡內存。 所以這就是我所擁有的:
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.