繁体   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