高时间复杂度问题的优化(4Sum)

Posted

技术标签:

【中文标题】高时间复杂度问题的优化(4Sum)【英文标题】:Optimization for a high time complexity problem (4Sum) 【发布时间】:2020-08-22 02:16:29 【问题描述】:

这是来自 Leetcode 的问题。我根据 3Sum 问题的思想推导出了这个解决方案。

问题是:

给定一个由 n 个整数组成的数组 nums 和一个整数目标,在 nums 中是否存在元素 a、b、c 和 d 使得 a + b + c + d = target?找出数组中所有唯一的四元组,给出目标的总和。

例如

Given array nums = [1, 0, -1, 0, -2, 2], and target = 0.

A solution set is:
[
  [-1,  0, 0, 1],
  [-2, -1, 1, 2],
  [-2,  0, 0, 2]
]

我的解决方案。

class Solution(object):
    def fourSum(self, nums, target):
        answer = []
        nums.sort()
        for i in range(len(nums)-3):
            # print('node 1')
            for j in range(i+1,len(nums)-2):
                # print('node 2')
                k,w = j+1,len(nums)-1
                while k < w:
                    sum = nums[i]+nums[j]+nums[k]+nums[w]
                    if  sum == target and [nums[i],nums[j],nums[k],nums[w]] not in answer:
                        answer.append([nums[i],nums[j],nums[k],nums[w]])
                    if  sum > target:
                        w-=1
                    else:
                        k+=1
        return answer   

但是算法的速度并不理想。

可以做一些小的改变来加速算法吗?

这段代码有冗余吗? 谢谢

【问题讨论】:

你需要的最小时间复杂度是多少? 还有一件事,我想让你发布一些测试用例。谢谢! @Ava 我目前正在做一个 O(n3) 算法。这是我能想到的最好的了。你是对的,我放了一个测试样本 @Leo 不,你目前是 O(n^6)。请参阅我的更新答案。 【参考方案1】:

由于我要使用python,所以我宁愿尝试以pythonic的方式来做。

from itertools import combinations

def fourSum(nums, target):
    return list(
        num for num in combinations(nums, 4)
        if sum(num) == target
    )

print(fourSum([1, 0, -1, 0, -2, 2], 0))

为了比较我和你的代码的执行时间,我什至写了下面的脚本。

from itertools import combinations
import timeit, random

def myFourSum(nums, target):
    return list(
        num for num in combinations(nums, 4)
        if sum(num) == target
    )
    
def yourFourSum(nums, target):
    answer = []
    nums.sort()
    for i in range(len(nums)-3):
        # print('node 1')
        for j in range(i+1,len(nums)-2):
            # print('node 2')
            k,w = j+1,len(nums)-1
            while k < w:
                sum = nums[i]+nums[j]+nums[k]+nums[w]
                if  sum == target and [nums[i],nums[j],nums[k],nums[w]] not in answer:
                    answer.append([nums[i],nums[j],nums[k],nums[w]])
                if  sum > target:
                    w-=1
                else:
                    k+=1
    return answer    
    

_list, target = random.sample(range(10000), 10000), random.randint(100, 5000)

setup = "from __main__ import myFourSum"
stmt = f"myFourSum(_list, target)"
    
print(myFourSum(_list, target))
print(f"Exec. Time: timeit.timeit(setup = setup, stmt = stmt, number = 10000)")

print('-' * 50)

setup = "from __main__ import yourFourSum"
stmt = f"yourFourSum(_list, target)"
print(yourFourSum(_list, target))
print(f"Exec. Time: timeit.timeit(setup = setup, stmt = stmt, number = 10000)")

我把这个留给你,你应该选择哪一个。

【讨论】:

这看起来像是一个糟糕的笑话。你失败了nums = [0, 0, 0, 0, 0]; target = 0,四次返回同一个四胞胎,而不是 unique 四胞胎。你失败了nums = [100, 100, 100, 100]; target = 400,返回 no 四胞胎。你的复杂度是 O(n^4)。而这样一个小案例是一个无关紧要和可怕的基准。并且更好地为测试参数使用常量,这样更改它们就不需要更改它们两次(麻烦且容易出错) @superb rain 是的,我同意。我修复了你提到的所有错误。关于时间复杂度,或许我们可以采用分治法? 您仍然无法通过 target=400 案例,并且您仍在对无关紧要的小案例进行基准测试。 @superbrain 好的,现在它已修复。我忘记把is 换成== 了。无论如何,我还通过 10000 个列表项改进了基准测试。我希望这对现在有所帮助。 现在它给了我NameError: name '_list' is not defined【参考方案2】:

可以做一些小的改变来加速算法。

这是一个问题。

但是是的。您的错误只是 not in answer 测试,因为 answer 是一个列表(列表)。改为将其设为一组(元组)。如果需要,最后转换为列表列表。 (如果你这样做on LeetCode,法官可能会接受不正确的类型。)

这会将您当前的 O(n^6) 解决方案变成 O(n^3) 解决方案。例如:

nums = list(range(50))
target = 98
With list:  0.697 seconds
With set:   0.062 seconds

nums = list(range(100))
target = 198
With list: 48.921 seconds
With set:   0.304 seconds

【讨论】:

您介意解释一下为什么将列表设为元组可以降低时间复杂度吗? @Leo 你从哪里得到的?我不是这么说的。

以上是关于高时间复杂度问题的优化(4Sum)的主要内容,如果未能解决你的问题,请参考以下文章

4sum, 4sum closest

4Sum - LeetCode

LeetCode27-简单-原地移除元素

LeetCode27-简单-原地移除元素

LeetCode27-简单-原地移除元素

时间复杂度你还在担心时间复杂度太高吗?