查找排列时减少运行时间

时间:2016-11-02 18:45:00

标签: python permutation

我正在处理Google的一个FooBar挑战,以便在Google的运行时环境中执行时遇到时间限制超出错误。我的解决方案运行良好 - 在我的本地开发环境中很快就能实现。

这是我的代码:

from itertools import permutations

def answer(l):
    options = []
    for r in range(1, len(l) + 1):
        for p in permutations(l, r=r):
            n = int(''.join(map(str, p)))
            if n % 3 == 0:
                options.append(n)
    numbers = sorted(options, reverse=True)
    return numbers[0]

l = [3, 1, 4, 1, 5, 9]
#l = [3, 1, 4, 1]
print(answer(l))

目标是从传入的数字列表中找到可被3整除的最大数字。

两个例子的输出应该是:

[3, 1, 4, 1, 5, 9] => 94311
[3, 1, 4, 1] => 4311

根据注释从最大到最小(而不是从最小到最大)生成排列,然后突破,我修改了代码。反对,它适用于本地环境,但谷歌运行时表示超出了时间限制:

def answer(l):
    options = []
    l = sorted(l, reverse=True)
    for r in range(len(l) + 1, 1, -1):
        for p in permutations(l, r=r):
            n = int(''.join(map(str, p)))
            if n % 3 == 0:
                return n
    return 0

我正在根据permutations docs对输入列表进行排序,如果输入已排序,则表示将对元组进行排序。然后,因为它应该排序,第一次找到一个可被3整除的值,这将是最高值

正如我所说,我的代码(两个版本)都有效。但是,我的运行时间似乎比谷歌预期的要长。如何减少上述代码的运行时间?

1 个答案:

答案 0 :(得分:2)

  1. 最高的数字将包含最多的数字。所以对于 大小为n的列表,搜索应该从n开始并继续到n-1, N-2 ...

  2. 可被3整除的数字将始终位于解决方案中。对于 示例2514可以被3整除,因此是32514或35314.因此,您可以减少 搜索不能被3整除的数字。

  3. 对于不能被3整除的n位数字列表(n> = 3),您可以 通过删除最多 2位数,得到一个可被3整除的数字。这是因为求和将具有余数1或2.如果它是1,在最坏的情况下你可以删除2位数,余数为2.如果它是2,再次在最坏的情况下你可以删除2位数余数为1。

  4. 现在算法:

    您有一个数字列表:

    divisible = [i for i in numbers if i % 3 == 0]
    candidate_list = [i for i in numbers if i % 3 != 0]
    

    如果candidate_list的总和可以被3整除,那么你就得到了答案。如果没有,请查看其余部分:

    remainder = sum(candidate_list) % 3
    

    如果余数为1,我们将在候选列表中搜索1,4或7。如果它是2,则数字将是2,5和8.如果我们找到一个数字,我们将从列表中删除它,剩余数字的总和将被3整除。

    if remainder!=0:
        for i in range(3):
            if (remainder + i*3) in candidate_list:
                candidate_list.remove(remainder + i*3)
                return candidate_list
    

    这将从最小的数字开始搜索,并在找到数字时突破循环。如果没有,我们将搜索两位数而不是1.

    counter = 0
    for candidate in candidate_list:
        if candidate % 3 + remainder == 3:
            candidate_list.remove(candidate)
            counter += 1
            if counter > 1:
                return candidate_list
    

    总的来说,你会有这样的事情:

    numbers = [3, 1, 4, 1, 5, 9, 0, 2, 4, 7, 9, 1, 3]
    divisible = [i for i in numbers if i % 3 == 0]
    
    def search(numbers):
        candidate_list = sorted([i for i in numbers if i % 3 != 0])
        remainder = sum(candidate_list) % 3
        if remainder!=0:
            for i in range(3):
                if (remainder + i*3) in candidate_list:
                    candidate_list.remove(remainder + i*3)
                    return candidate_list
            counter = 0
            for candidate in candidate_list:
                if candidate % 3 + remainder == 3:
                    candidate_list.remove(candidate)
                    counter += 1
                    if counter > 1:
                        return candidate_list
        else:
            return candidate_list
    
    candidate_list = search(numbers)
    fin = int(''.join(map(str, sorted(divisible + candidate_list, reverse=True))))