有没有更快的方法来查找“幸运三元组”的数量?

时间:2016-09-27 03:20:55

标签: algorithm

我正在研究代码挑战问题 - “找到幸运的三元组”。 “幸运三元组”定义为“在列表lst中,适用于(lst[i], lst[j], lst[k]) where i < j < k三元组合的任意组合,其中lst[i] divides lst[j]lst[j] divides lst[k]

我的任务是找到给定列表中的幸运三元组数。蛮力方式是使用三个循环,但解决问题需要太多时间。我写了这个,系统响应“时间超过”。问题看起来很愚蠢,但数组是未排序的,因此二进制搜索等常规方法不起作用。我对这个问题感到震惊了一天,希望有人能给我一个提示。我正在寻找一种更快解决问题的方法,至少时间复杂度应该低于O(N ^ 3)。

5 个答案:

答案 0 :(得分:4)

一个简单的动态编程算法将在二次时间和线性空间中执行此操作。您只需为列表中的每个项目维护一个计数器c[i],该计数器代表除L[i]之前的整数。

然后,当您浏览列表并使用之前的所有项L[k]测试每个整数L[j]时,如果L[j]除以L[k],则只需添加c[j] (可能是0)到你的三元组的全局计数器,因为这也意味着确实存在c[j]L[i]L[i]L[j]和{{1} }}

i < j

可能有一些更好的算法使用花哨的离散数学来更快地完成它,但如果O(n ^ 2)没问题,这样就可以了。

关于你的评论:

  • 为什么选择DP?我们有一些东西可以清楚地建模为具有从左到右的顺序(DP橙色标志),感觉就像重复使用先前计算的值可能很有趣,因为强力算法会进行很多次完全相同的计算。

  • 如何从中获得解决方案?运行一个简单的例子(提示:最好是从左到右处理输入)。在步骤int c[] = {0} int nbTriples = 0 for k=0 to n-1 for j=0 to k-1 if (L[k] % L[j] == 0) c[k]++ nbTriples += c[j] return nbTriples ,计算您可以从此特定点计算的内容(忽略i右侧的所有内容),并尝试查明您为不同i反复计算的内容:这就是您想要缓存。在这里,当您在步骤i'sk)看到潜在的三元组时,您必须考虑L[k] % L[j] == 0上发生的情况:“左侧是否有一些除数?这些中的每一个都会给我们一个新的三元组。让我们看看......但是等等!我们已经在步骤L[j]上计算了它!让我们缓存这个值!“这就是你跳上座位的时候。

答案 1 :(得分:2)

python中的完整工作解决方案: c = [0] * len(l) print c 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] print j
print c print count '

答案 2 :(得分:1)

阅读Sieve of Eratosthenes,这是一种寻找素数的常用技巧,可以用来找到你的幸运三元组#39。基本上,您需要按递增的值顺序迭代列表,并且对于每个值,将其乘以增加因子,直到它大于最大的列表元素,并且每次这些倍数中的一个等于列表中的另一个值时,倍数可以被基数整除。如果列表在给你时被分类,那么i&lt; j&lt; k要求也将得到满足。

e.g。给出列表[3, 4, 8, 15, 16, 20, 40]

3开始,其在列表范围内具有倍数[6, 9, 12, 15, 18 ... 39]。在这些倍数中,列表中只包含15,因此在15下记录它具有因子3

继续4,其中有[8, 12, 16, 20, 24, 28, 32, 36, 40]倍数。将那些标记为因子4。

继续浏览列表。当您到达具有现有已知因子的元素时,如果您在列表中找到该数字的任何倍数,那么您将获得三倍。在这种情况下,对于16,它在列表中有多个32。现在您知道32可以被16整除,可以被4整除。鉴于15,列表中没有倍数,因此没有值可以与315形成三元组。

答案 3 :(得分:1)

问题的precomputation步骤可以帮助减少时间复杂性。

预计算步骤

对于每个元素(i),迭代数组以找到哪些元素(j),使lst[j]%lst[i]==0

for(i=0;i<n;i++)
{
   for(j=i+1;j<n;j++)
   {
      if(a[j]%a[i] == 0)
         // mark those j's. You decide how to store this data
   }
}

预计算步骤将花费O(n^2)时间。

终极步骤中,使用预计算步骤的详细信息,以帮助查找三元组..

答案 4 :(得分:0)

形成一个图形 - 一个索引数组,它是当前索引之前的倍数。然后计算这些指数的倍数的集体总和,从图中引用。它的复杂度为 O(n^2)

例如,对于列表 {1,2,3,4,5,6} 将有一个倍数数组。图表看起来像 { 0:[1,2,3,4,5], 1:[3,5], 2: [5], 3:[],4:[], 5:[]}

因此,三胞胎总数将为 {0->1 ->3/5} 和 {0->2 ->5},即 3

package com.welldyne.mx.dao.core;

import java.util.LinkedList;
import java.util.List;

public class LuckyTriplets {

  public static void main(String[] args) {

    int[] integers = new int[2000];

    for (int i = 1; i < 2001; i++) {

        integers[i - 1] = i;
    }
    long start = System.currentTimeMillis();
    int n = findLuckyTriplets(integers);
    long end = System.currentTimeMillis();
    System.out.println((end - start) + " ms");
    System.out.println(n);

  }

  private static int findLuckyTriplets(int[] integers) {

    List<Integer>[] indexMultiples = new LinkedList[integers.length];

    for (int i = 0; i < integers.length; i++) {

        indexMultiples[i] = getMultiples(integers, i);
    }

    int luckyTriplets = 0;
    for (int i = 0; i < integers.length - 1; i++) {

        luckyTriplets += getLuckyTripletsFromMultiplesMap(indexMultiples, i);
    }
    return luckyTriplets;
  }

  private static int getLuckyTripletsFromMultiplesMap(List<Integer>[] indexMultiples, int n) {

    int sum = 0;

    for (int i = 0; i < indexMultiples[n].size(); i++) {

        sum += indexMultiples[(indexMultiples[n].get(i))].size();
    }

    return sum;
  }

  private static List<Integer> getMultiples(int[] integers, int n) {

    List<Integer> multiples = new LinkedList<>();
    for (int i = n + 1; i < integers.length; i++) {

        if (isMultiple(integers[n], integers[i])) {

            multiples.add(i);
        }
    }
    return multiples;
 }

 /*
  * if b is the multiple of a
  */
 private static boolean isMultiple(int a, int b) {

    return b % a == 0;
 }
}