Google Foobar Challenge 3 - 查找访问代码

时间:2016-10-04 07:38:44

标签: java python combinations

查找访问代码

写一个函数答案(l),它取一个正整数列表l并计算&#34;幸运三元组的数量&#34; (lst [i],lst [j],lst [k])其中i < j&lt; ķ。 l的长度在2到2000之间。 l的元素介于1和999999之间。答案符合有符号的32位整数。有些列表是故意生成的,没有任何访问代码来甩掉间谍,所以如果没有找到三元组,则返回0.

例如,[1,2,3,4,5,6]具有三元组:[1,2,4],[1,2,6],[1,3,6],作出答案总共3个。

测试用例

输入:     (int list)l = [1,1,1] 输出:     (int)1

输入:     (int list)l = [1,2,3,4,5,6] 输出:     (int)3

我的尝试

from itertools import combinations

def answer(l):
    if len(l) < 3:
        return 0
    found = 0
    for val in combinations(l,3):
        # Ordering Check
        if (val[0] <= val[1] <= val[2]) != True:
            continue
        # Answer Size Check against size of signed integer 32 bit
        if int(val[0].__str__() + val[1].__str__() + val[2].__str__()) > 2147483647:
            continue
        # Division Check
        if (val[1] % val[1] != 0) or (val[2] % val[1] != 0):
            continue
        # Increment 'found' variable by one
        found += 1
    return found

6 个答案:

答案 0 :(得分:2)

这是我头顶的解决方案,它具有O(n ^ 2)时间和O(n)空间复杂度。我认为有一个更好的解决方案(可能使用动态编程),但这会产生所有组合。

public static int foobar( int[] arr)
{
    int noOfCombinations = 0;
    int[] noOfDoubles = new int[arr.length];

    // Count lucky doubles for each item in the array, except the first and last items
    for( int i = 1; i < arr.length-1; ++i)
    {
        for( int j = 0; j < i; ++j)
        {
            if( arr[i] % arr[j] == 0)
                ++noOfDoubles[i];
        }
    }

    // Count lucky triples
    for( int i = 2; i < arr.length; i++)
    {
        for( int j = 1; j < i; ++j)
        {
            if( arr[i] % arr[j] == 0)
                noOfCombinations += noOfDoubles[j];
        }
    }

    return noOfCombinations;
}

答案 1 :(得分:1)

事情是:你让图书馆方法组合做所有&#34;真实&#34;为你工作。

当然:通常情况就是这样。当有现有的库函数可以为您提供所需的内容时,想要重新发明轮子。您当前的代码非常简洁,并且易于阅读(除非您打电话给您的列表,好吧,&#34;列表&#34;,但不是&#34; l&#34;)。

但是这种情况有所不同:很明显,这个程序的大部分执行时间都会在该调用中发生。谷歌似乎认为这个电话正在做什么......可以做得更快更快

所以,答案就是:你真的想通过以比现在更好更好的方式重写你的代码来重新发明轮子!第一个出发点可能是查看组合的源代码,以了解该调用是否/如何执行您在上下文中不需要的操作。

猜测:该通话会产生很多不理想的排列。所有这些都是浪费时间。你想退后一步,考虑如何从你的输入中构建那些幸运的三元组,而不是创造了大量不那么幸运的三元组!

答案 2 :(得分:1)

我尝试在python中实现它。它的速度还不够快,无法通过测试,但它比uoyilmaz解决方案移植到python的速度快50倍。代码如下:

#!/usr/bin/env python2.7

from bisect import insort_left
from itertools import combinations


def answer_1(l):
    """My own solution."""
    indices = {}
    setdefault_ = indices.setdefault
    for i, x in enumerate(l):
        setdefault_(x, []).append(i)

    out = 0
    highest_value = max(l)
    for i, x in enumerate(l):
        multiples = []
        for m in xrange(1, int(highest_value / x) + 1):
            if x * m in indices:
                for j in indices[x * m]:
                    if i < j:
                        insort_left(multiples, (j, x * m))

        if multiples:
            multiples = [m[1] for m in multiples]
            for pair in combinations(multiples, 2):
                out += pair[1] % pair[0] == 0

    return out


def answer_2(l):
    """@uoyilmaz's solution ported from Java."""
    out = 0
    pair_counts = [0] * len(l)
    for i in xrange(1, len(l) - 1):
        for j in xrange(i):
            if l[i] % l[j] == 0:
                pair_counts[i] += 1

    for i in xrange(2, len(l)):
        for j in xrange(1, i):
            if l[i] % l[j] == 0:
                out += pair_counts[j]

    return out


answer = answer_1

# -----------------------------------------------------------------------------

_SEED = 1.23


def benchmark(sample_count):
    from random import seed, randint
    import timeit
    clock = timeit.default_timer

    seed(_SEED)
    samples = [[randint(1, 999999) for _ in xrange(randint(2, 2000))]
                for _ in xrange(sample_count)]

    start = clock()
    for sample in samples:
        answer(sample)

    end = clock()
    print("%.4f s elapsed for %d samples." % (end - start, sample_count))


def test():
    # Provided test cases.
    assert(answer([1, 1, 1]) == 1)
    assert(answer([1, 2, 3, 4, 5, 6]) == 3)

    # Custom test cases.
    assert(answer([1]) == 0)
    assert(answer([1, 2]) == 0)
    assert(answer([2, 4]) == 0)
    assert(answer([1, 1, 1, 1]) == 4)
    assert(answer([1, 1, 1, 1, 1]) == 10)
    assert(answer([1, 1, 1, 1, 1, 1]) == 20)
    assert(answer([1, 1, 1, 1, 1, 1, 1]) == 35)
    assert(answer([1, 1, 2]) == 1)
    assert(answer([1, 1, 2, 2]) == 4)
    assert(answer([1, 1, 2, 2, 2]) == 10)
    assert(answer([1, 1, 2, 2, 2, 3]) == 11)
    assert(answer([1, 2, 4, 8, 16]) == 10)
    assert(answer([2, 4, 5, 9, 12, 34, 45]) == 1)
    assert(answer([2, 2, 2, 2, 4, 4, 5, 6, 8, 8, 8]) == 90)
    assert(answer([2, 4, 8]) == 1)
    assert(answer([2, 4, 8, 16]) == 4)
    assert(answer([3, 4, 2, 7]) == 0)
    assert(answer([6, 5, 4, 3, 2, 1]) == 0)
    assert(answer([4, 7, 14]) == 0)
    assert(answer([4, 21, 7, 14, 8, 56, 56, 42]) == 9)
    assert(answer([4, 21, 7, 14, 56, 8, 56, 4, 42]) == 7)
    assert(answer([4, 7, 14, 8, 21, 56, 42]) == 4)
    assert(answer([4, 8, 4, 16]) == 2)


def main():
    test()
    benchmark(100)


if __name__ == '__main__':
    main()

现在,如果有人知道如何进一步提高速度,我可以提出建议。

答案 3 :(得分:1)

我实际上刚刚在foo.bar收到此问题,因此我会深入了解我的O(n^2)解决方案。

我选择将输入建模为有向图,其中图中的每个node都是输入数组的索引i,而uv的边缘如果u可以除以v,则存在。

一旦我构建了有向图,只需要为每个节点总结每个邻居的所有传出边缘。正确性在于,当构造的图中存在长度为3的路径时,triplet存在。

Node -> Neighbor -> # of ougoing edges = # of triplets starting from Node

注意:我在这里使用输入数组的索引作为图形节点值,但输入数组值也足够了,因为我们只是计算边缘。

public static int answer(int[] l) {
    if (l.length < 3) {
        return 0;
    }

    Map<Integer, Set<Integer>> graph = new HashMap<>();
    graph.put(0, Collections.emptySet());

    for (int i = 1; i < l.length; i++) {
        graph.put(i, new HashSet<>());
        for (int j = 0; j < i; j++) {
            if (l[i] % l[j] == 0) {
                graph.get(i).add(j);
            }
        }
    }

    int count = 0;
    for (int node : graph.keySet()) {
        for (int neighbor : graph.get(node)) {
            count += graph.get(neighbor).size();
        }
    }

    return count;
}

答案 4 :(得分:1)

def solution(l):
    c = [0] * len(l)
    count = 0
    for i in range(0,len(l)):
        j=0
        for j in range(0, i):
            if l[i] % l[j] == 0:
                c[i] = c[i] + 1
                count = count + c[j]
    return count

这将花费O(n ^ 2)的时间复杂度

要尝试一下,只需将这两行添加为驱动程序代码:

ar = [1, 2, 3, 4, 5, 6]
print(solution(ar))

答案 5 :(得分:0)

我今天在 foobar 上也遇到了这个问题。 @saiifeemustafaq 给出了一个聪明的解决方案。我只想在答案中添加一些解释。希望它可以帮助像我这样的人。

   def solution(l):
        c = [0] * len(l)
        count = 0
        for i in range(len(l)):
            for j in range(i):
                if l[i] % l[j] == 0:
                    c[i] += 1
                    count += c[j]
        return count

该算法使用嵌套的 for 循环结构。外循环处理列表中的每个整数,即 l[i]。内部函数找出 l[i] 左侧有多少个整数可以整除 l[i]。结果将存储在预先创建的列表中。

最棘手的部分来了。由于j总是小于i,所以每次看l[i]时,能整除l[j]的整数个数就已经确定了。现在,如果 l[j] 除以 l[i],就找到了幸运三元组! l[j]l[i]l[j] 左侧的一些其他整数除 l[j]。由l[j]l[i]组成的幸运三元组的个数正是存储列表中可以整除l[j]的整数个数。这就是我们在代码中有 count += c[j] 的原因。循环结束后,我们就会得到答案。