繁体   English   中英

从两个列表中找到2个数字的最快方法,总和等于x

[英]The fastest way to find 2 numbers from two lists that in sum equal to x

我的代码:

n = 3
a1 = 0
b1 = 10
a2 = 2
b2 = 2

if b1>n:
    b1=n
if b2>n:
    b2=n

diap1 = [x for x in range(a1, b1+1)]
diap2 = [x for x in range(a2, b2+1)]

def pairs(d1, d2, n):
    res = 0
    same = 0
    sl1 = sorted(d1)
    sl2 = sorted(d2)
    for i in sl1:
        for j in sl2:
            if i+j==n and i!=j:
                res+=1
            elif i+j==n and i==j:
                same+=1
    return(res+same)

result = pairs(diap1, diap2, n)
print(result)

注意: n,a1,b1,a2,b2 可以改变 代码应该从2个列表中找到2个数字(每个1个),总和等于n。 例如:对(a,b)和(b,a) 不同,但(a,a)和(a,a) 是同一对 因此,我的代码输出是正确的,对于上面的代码是1(1,2),但对于大输入,它需要太多时间。 如何优化它以更快地工作?

使用set()进行快速查找...

setd2 = set(d2)

不要尝试所有可能的数字对。 一旦你修复了第一个列表中的数字,比如i,只要看看(ni)是否在第二个集合中。

for i in sl1:
    if (n-i) in setd2:
        # found match
    else:
        # no match in setd2 for i

通过以下方式,您可以最快地工作并找到总和等于n的两个数字,并将它们存储在元组列表中。

s1 = set(list1)
s2 = set(list2)
nums = []
for item in s1:
    if n-item in s2:
       nums.append((item, n-item))

接受的答案很容易理解和实现,但我只需要分享这种方法。 你可以看到你的问题是一样的,因为这一个
这个答案特别有趣,因为插入集合不需要额外的空间。 我在答案中包含了算法。

如果对数组进行排序,则可以在线性时间和常量存储中进行排序。

  • 从两个指针开始,一个指向A的最小元素,另一个指向B的最大元素。
  • 计算指向元素的总和。
  • 如果小于k,则将指针增加到A,使其指向下一个最大元素。
  • 如果它大于k,则将指针递减到B,使其指向下一个最小元素。
  • 如果它恰好是k,你就找到了一对。 移动其中一个指针,继续寻找下一对。

如果数组最初未排序,那么您可以先对它们进行排序,然后使用上述算法。

感谢您明确定义问题并提供您尝试优化的代码示例。

利用您问题中的两个关键定义和您提供的符号,我将优化尝试限制为使用列表,并添加了随机更改与n,a1,b1,a2和b2关联的值的功能。

为了显示优化结果,我创建了一个模块,其中包括使用random.randit函数创建各种列表大小和timeit.Timer函数来捕获原始pair()函数所需的时间量正如我在pairs2()函数中建议的优化。

在pairs2()函数中,您将注意到每个迭代循环都包含一个break语句。 一旦满足所需的标准,这些就消除了对每个列表的不必要的迭代。 您应该注意,随着列表大小的增加,pairs2()与pairs()时间会有所改善。

测试模块代码:

import random
from timeit import Timer

max_value = 10000
n =  random.randint(1, max_value)
a1 = random.randint(0, max_value)
b1 = random.randint(1, max_value+1)
a2 = random.randint(0, max_value)
b2 = random.randint(1, max_value+1)

if b1>n:
    b1=n
if b2>n:
    b2=n

if a1>=b1:
    a1 = random.randint(0, b1-1)
if a2>=b2:
    a2 = random.randint(0, b2-1)

diap1 = [x for x in range(a1, b1)]
diap2 = [x for x in range(a2, b2)]
print("Length diap1 =", len(diap1))
print("Length diap2 =", len(diap2))

def pairs(d1, d2, n): 
    res = 0 
    same = 0    
    sl1 = sorted(d1)
    sl2 = sorted(d2)
    for i in sl1:
        for j in sl2:
            if i+j==n and i!=j:                 
                res+=1                                          
            elif i+j==n and i==j:
                same+=1
    return(res+same)

def pairs2(d1, d2, n): 
    res = 0 
    same = 0    
    sl1 = sorted(d1)
    sl2 = sorted(d2)
    for i in sl1:
        for j in sl2:
            if i+j==n and i!=j:                 
                res+=1
                break                                      
            elif i+j==n and i==j:
                same+=1
                break
        if res+same>0:
            break
    return(res+same)

if __name__ == "__main__":
    result=0
    timer = Timer("result = pairs(diap1, diap2, n)",
                  "from __main__ import diap1, diap2, n, pairs")
    print("pairs_time = ", timer.timeit(number=1), "result =", result)

    result=0
    timer = Timer("result = pairs2(diap1, diap2, n)",
              "from __main__ import diap1, diap2, n, pairs2")
    print("pairs2_time = ", timer.timeit(number=1), "result =", result)

如果从第一个列表中提取值n,然后在第二个列表中搜索值m以使总和与搜索到的值匹配,则可以创建一些快捷方式。 例如,如果总和较小,则第二个列表中小于或等于m的所有值也不会给出正确的总和。 同样,如果总和更大。

使用此信息,我将使用以下步骤:

  • 设置两个堆,一个最小堆,一个最大堆。
  • 查看每个堆的顶部元素:
    • 如果总和与搜索到的值匹配,则表示您已完成。
    • 如果总和超过搜索值,请从最大堆中删除该值。
    • 如果总和小于搜索值,则从最小堆中删除该值。
  • 如果任何一个堆为空,则没有解决方案。

请注意,使用堆是对两个序列进行分类排序的优化。 但是,如果您经常遇到无匹配的情况,则在算法之前对数字进行排序可能是一种更快的方法。 这样做的原因是一个好的排序算法将胜过通过堆的隐式排序,而不是通过其渐近复杂性而是通过一些常数因子。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM