在列表中查找因子的最有效方法是什么?

时间:2016-10-08 20:40:44

标签: python algorithm

我正在做什么:

我需要创建一个函数,给定一个正整数列表(可能有重复的整数),计算所有三元组(在列表中),其中第三个数字是第二个的倍数,第二个是多个第一个:

(相同的数字不能在一个三元组中使用两次,但可以被所有其他三元组使用)

例如,[3, 6, 18]就是一个因为18均匀地进入6,并且均匀地进入3

所以给定[1, 2, 3, 4, 5, 6]它应该找到:

[1, 2, 4] [1, 2, 6] [1, 3, 6]

并返回3(它找到的三元组数)

我尝试了什么:

我做了一些有效但功效不够的功能。是否有一些我不知道的数学概念可以帮助我更快地找到这些三元组?具有更好功能的模块?我不知道该搜索什么...

def foo(q):
    l = sorted(q)
    ln = range(len(l))
    for x in ln:
        if len(l[x:]) > 1:
            for y in ln[x + 1:]:
                if (len(l[y:]) > 0) and (l[y] % l[x] == 0):
                    for z in ln[y + 1:]:
                        if l[z] % l[y] == 0:
                            ans += 1
    return ans

这个更快一点:

def bar(q):
    l = sorted(q)
    ans = 0
    for x2, x in enumerate(l):
        pool = l[x2 + 1:]
        if len(pool) > 1:
            for y2, y in enumerate(pool):
                pool2 = pool[y2 + 1:]
                if pool2 and (y % x == 0):
                    for z in pool2:
                        if z % y == 0:
                            ans += 1
    return ans

这是我从你们所有人那里得到的帮助,但我必须做错事,因为它得到了错误的答案(尽管它真的很快):

def function4(numbers):
    ans = 0
    num_dict = {}
    index = 0
    for x in numbers:
        index += 1
        num_dict[x] = [y for y in numbers[index:] if y % x == 0]

    for x in numbers:
        for y in num_dict[x]:
            for z in num_dict[y]:
                print(x, y, z)
                ans += 1

    return ans

39889而不是40888) - 哦,我不小心让索引var从1开始而不是0.它现在有效。

最终编辑

通过重新评估我需要它做什么,我找到了找到三元组数量的最佳方法。这种方法实际上并没有找到三元组,它只计算它们。

def foo(l):
    llen = len(l)
    total = 0
    cache = {}
    for i in range(llen):
        cache[i] = 0
    for x in range(llen):
        for y in range(x + 1, llen):
            if l[y] % l[x] == 0:
                cache[y] += 1
                total += cache[x]
    return total

这是一个功能版本,可以解释思考过程(虽然因为垃圾邮件打印而不适合大型列表):

def bar(l):
    list_length = len(l)
    total_triples = 0
    cache = {}
    for i in range(list_length):
        cache[i] = 0
    for x in range(list_length):
        print("\n\nfor index[{}]: {}".format(x, l[x]))
        for y in range(x + 1, list_length):
            print("\n\ttry index[{}]: {}".format(y, l[y]))
            if l[y] % l[x] == 0:
                print("\n\t\t{} can be evenly diveded by {}".format(l[y], l[x]))
                cache[y] += 1
                total_triples += cache[x]
                print("\t\tcache[{0}] is now {1}".format(y, cache[y]))
                print("\t\tcount is now {}".format(total_triples))
                print("\t\t(+{} from cache[{}])".format(cache[x], x))
            else:
                print("\n\t\tfalse")
    print("\ntotal number of triples:", total_triples)

3 个答案:

答案 0 :(得分:4)

现在你的算法有O(N ^ 3)运行时间,这意味着每次你加倍初始列表的长度时,运行时间会增加8倍。

在最坏的情况下,你无法改善这一点。例如,如果你的数字都是2的连续幂,意味着每个数字除以每个数字都比它更重要,那么每三个数字都是一个有效的解决方案,所以打印出来的所有解决方案都会像你现在在做什么。

如果你有一个较低的“密度”数字来划分其他数字,你可以做的一件事就是搜索成对的数字而不是三元组。这将花费时间仅为O(N ^ 2),这意味着当您将输入列表的长度加倍时,运行时间会增加4倍。一旦你有一对数字列表,你可以用它来建立一个三元组列表。

# For simplicity, I assume that a number can't occur more than once in the list.
# You will need to tweak this algorithm to be able to deal with duplicates.

# this dictionary will map each number `n` to the list of other numbers
# that appear on the list that are multiples of `n`.
multiples = {}
for n in numbers:
   multiples[n] = []

# Going through each combination takes time O(N^2)
for x in numbers:
   for y in numbers:
     if x != y and y % x == 0:
         multiples[x].append(y)

# The speed on this last step will depend on how many numbers
# are multiples of other numbers. In the worst case this will
# be just as slow as your current algoritm. In the fastest case
# (when no numbers divide other numbers) then it will be just a
# O(N) scan for the outermost loop.
for x in numbers:
    for y in multiples[x]:
        for z in multiples[y]:
            print(x,y,z)

可能有更快的算法,也可以利用除法的代数属性,但在你的情况下,我认为O(N ^ 2)可能足够快。

答案 1 :(得分:4)

关键见解是:

如果 a 划分 b ,则表示 a &#34;适合 b &#34; 。 如果 a 没有划分 c ,则表示&#34; a 不适合 c &#34 ;. 如果 a 无法适应 c ,则 b 无法适应 c (想象如果< strong> b 符合 c ,因为 a 适合 b ,然后 a 适合所有适合 c b ,所以 a 也必须符合 c ..(想想主要因子化等))

这意味着我们可以优化。如果我们将数字从最小到最大排序,首先从较小的数字开始。第一次迭代,从最小的数字开始为 a 如果我们将数字分为两组,分组 1 a 分组, 2 分组 a < / strong>没有划分,然后我们知道 1 组中的数字不能划分 2 组中的数字,因为 2 组中没有数字 a 作为一个因素。

所以,如果我们有[2,3,4,5,6,7],我们将从2开始并得到: [2,4,6]和[3,5,7] 我们可以在每个组上重复这个过程,分成更小的组。这表明一种算法可以更有效地计算三元组。这些组的速度非常快,这意味着它的效率应该非常接近输出的大小。

答案 2 :(得分:0)

这是迄今为止我能够提出的最佳答案。它速度快,但速度不够快。我还在发帖,因为我可能会放弃这个问题,并且不想忽略我已经取得的任何进展。

def answer(l):
    num_dict = {}
    ans_set = set()

    for a2, a in enumerate(l):
        num_dict[(a, a2)] = []

    for x2, x in enumerate(l):
        for y2, y in enumerate(l):
            if (y, y2) != (x, x2) and y % x == 0:
                pair = (y, y2)
                num_dict[(x, x2)].append(pair)

    for x in num_dict:
        for y in num_dict[x]:
            for z in num_dict[y]:
                ans_set.add((x[0], y[0], z[0]))

    return len(ans_set)