简体   繁体   English

什么是计算Python中整数列表中唯一乘法和加法对的数量的有效方法?

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

Given a sorted array A = [n, n+1, n+2,... n+k] elements, I am trying to count the unique number of multiplicative and additive pairs such that the condition xy >= x+y is satisfied. 给定一个排序的数组A = [n,n + 1,n + 2,... n + k]元素,我试图计算乘法和加法对的唯一数,使得条件xy> = x + y是满意。 Where x and y are indices of the list, and y > x. 其中x和y是列表的索引,y> x。

Here is my minimum working example using a naive brute force approach: 这是我使用天真蛮力方法的最低工作示例:

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)

However this approach has an exponential time complexity for large lists. 然而,这种方法对于大型列表具有指数时间复杂度。

What sorting or searching algorithms exist that will allow me to implement a more efficient solution? 有哪些排序或搜索算法可以让我实现更有效的解决方案?

This question has a closed-form mathematical solution, but if you'd prefer to implement in a programming langauge, you just need to find all unique pairs of numbers from your list, and count the number that satisfy your requirement. 这个问题有一个封闭形式的数学解决方案,但如果您更喜欢在编程语言中实现,您只需要从列表中找到所有唯一的数字对,并计算满足您要求的数字。 itertools.combinations is your friend here: 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))

Output 产量

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

Basic algebra ... solve for one variable in terms of the other: 基本代数......根据另一个变量求解一个变量:

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

Now, if your elements are all positive integers, you get 现在,如果你的元素都是正整数,那么你就得到了

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

In this last case, x/(x-1) is a fraction between 1 and 2; 在最后一种情况下,x /(x-1)是介于1和2之间的分数; again, 再次,

y >= 2

Solves the inequality. 解决不平等问题。

This gives you a trivially accessible solution in O(1) time; 这为您提供了O(1)时间内易于访问的解决方案; if you want the pairs themselves, you're constrained by the printing, which is O(n^2) time. 如果你想要这些对,你就会被打印限制,这是O(n ^ 2)时间。

So using the fact that x*y >= x+y if both (mistake in my original comment) x and y are >=2 (see @Prune's answer for details), then you may as well remove 0 and 1 from your list if they appear, because they won't make any suitable pair. 所以使用x*y >= x+y的事实如果两者 (我的原始评论中的错误) xy都是>=2 (请参阅@ Prune的答案以获取详细信息),那么您也可以从列表中删除01如果他们出现,因为他们不会做任何合适的一对。

So now assuming all numbers or >=2 and you have k of them (eg replace k by k-1 in the following operation if you have n=1 ), all possible pairs will satisfy your condition. 所以,现在假设所有的数字或>=2 ,你有k他们的(如更换k通过k-1在下面的操作,如果你有n=1 ),对所有可能会满足你的条件。 And the number of pairs among k elements is the well known formula k*(k-1)/2 (google it if you don't know about it). 并且k元素中的对的数量是众所周知的公式k*(k-1)/2 (如果你不知道它就谷歌它)。 The time to compute this number is essentially the same (one multiplication, one division) no matter what value of k you have (unless you start going to crazy big numbers), so complexity is O(1). 计算这个数字的时间基本相同(一个乘法,一个除法),无论你有什么k值(除非你开始疯狂的大数字),所以复杂性是O(1)。

This assumes your integers are positive, if not the formula will be slightly more complicated but still possible as a closed form solution. 假设你的整数是正数,如果不是,公式会稍微复杂一点,但仍然可以作为封闭形式的解决方案。

If you want a more mathematical solution, consider that xy > x+y has no solutions for y=1 . 如果您想要更多数学解,请考虑xy > x+y没有y=1 Otherwise, you can algebraically work this out to x > y/(y-1) . 否则,您可以代数地将其解析为x > y/(y-1) Now if we have two consecutive, positive integers and divide the larger by the smaller, we either get exactly 2 (if y=2) or get some fraction between 1 and 2 exclusive. 现在,如果我们有两个连续的整数并将较大的值除以较小的值,我们或者得到正好2(如果y = 2)或得到1到2之间的一些分数。 Note that x has to be greater than this y/(y-1) quotient, but also has to be less than y. 请注意,x必须大于此y /(y-1)商,但也必须小于y。 If y=2, then the only possible x value in our list of positive integers has to be 1, in which case there are no matches because 1 is not greater than 2/1. 如果y = 2,则我们的正整数列表中唯一可能的x值必须为1,在这种情况下没有匹配,因为1不大于2/1。 So this all simplifies to "For each number y in our list, count all of the values x that are in the range of [2,y)." 所以这一切都简化为“对于列表中的每个数字y,计算[2,y]范围内的所有值x。” If you do the math, this should come out to adding 1 + 2 + 3 + ... + k, which is simply k(k+1)/2 . 如果你做数学运算,这应该是添加1 + 2 + 3 + ... + k,它只是k(k+1)/2 Again, we're assuming n and k are positive integers; 同样,我们假设n和k是正整数; you can derive a slightly more complicated formula when you take into account cases for n <= 0. 考虑到n <= 0的情况,可以得出稍微复杂的公式。

But assuming you DO want to stick with a brute force approach, and not do a little mathematical reasoning to find a different approach: I tried out several variations, and here's a faster solution based on the following. 但是假设你想要坚持使用蛮力方法,而不是做一些数学推理来找到不同的方法:我尝试了几种变化,这是基于以下的更快的解决方案。

  • You said the list is already sorted, so I dropped the sorting function. 你说列表已经排序了,所以我放弃了排序功能。
  • Likewise, the "else: continue" isn't necessary, so for simplicity I dropped that. 同样,“else:continue”也没有必要,所以为了简单起见,我放弃了。
  • Instead of looping through all x and y values, then checking if x < y, you can just make your second loop check y values in the range from x+1 to y. 而不是循环遍历所有x和y值,然后检查是否x <y,你可以让你的第二个循环检查y +在x + 1到y范围内的值。 BUT... 但...
  • You can use itertools to generate the unique pairs of all numbers in your list A 您可以使用itertools生成列表A中所有数字的唯一对
  • If you ultimately really only care about the length of the pairs list and not the number pairs themselves, then you can just count the pairs along the way instead of storing them. 如果你最终真的只关心pairs列表的长度而不是数字对本身,那么你可以只计算沿途的对而不是存储它们。 Otherwise you can run out of memory at high N values. 否则,您可能会在高N值时耗尽内存。
  • I get slightly faster results with the equivalent test of x(y-1)-y>0. 使用x(y-1)-y> 0的等效测试,我获得稍快的结果。 More so than with x(y-1)>y too. 比x(y-1)> y还要多。

So here's what I have: 所以这就是我所拥有的:

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

Here's everything timed: 这是一切定时:

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)

Result: 结果:

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