【问题标题】:Any faster way to find the number of "lucky triples"?有没有更快的方法来找到“幸运三元组”的数量?
【发布时间】:2021-06-17 04:14:37
【问题描述】:

我正在处理一个代码挑战问题——“找到幸运三元组”。 “幸运三元组”被定义为“在lst 列表中,对于任何三元组组合,如(lst[i], lst[j], lst[k]) where i < j < k,其中lst[i] divides lst[j]lst[j] divides lst[k]

我的任务是找出给定列表中幸运三元组的数量。蛮力的方法是使用三个循环,但解决问题需要太多时间。我写了这个,系统响应“时间超过”。这些问题看起来很傻很容易,但是数组是未排序的,所以像二进制搜索这样的一般方法不起作用。我被这个问题惊呆了一天,希望有人能给我一个提示。我正在寻找一种更快解决问题的方法,至少时间复杂度应该低于O(N^3)。

【问题讨论】:

  • " 有什么方法可以更快地找到幸运三元组的总数吗?"如果这已被分配为家庭作业,那么答案大概是“是”。你试过什么?
  • @JohnColeman 我写了蛮力一。我还没有想出任何其他方法,所以我正在寻求帮助。为什么你们不赞成这个?请让我知道我应该改进什么。
  • 这个问题的措辞使它看起来有点像一个家庭作业问题的陈述,没有证据表明学生为解决它付出了努力。每天都会发布许多此类问题,并且往往被严重否决。您的问题可能被误认为是这样的问题。如果您说您已经实施了蛮力解决方案并想要一些有关寻找更有效解决方案的提示,您会得到更好的响应。如果您稍微修改您的问题,我很乐意将我的反对票改为赞成票。
  • @JohnColeman,嗨,约翰,我已经修改了描述。希望它工作正常。任何有关该问题的想法表示赞赏。谢谢。
  • 如果a%b = 0,b%c = 0,那么a%c呢?

标签: algorithm


【解决方案1】:

一个简单的类似动态规划的算法将在二次时间和线性空间中执行此操作。您只需为列表中的每个项目维护一个计数器 c[i],它表示除以 L[i] 的先前整数的数量。

然后,当您遍历列表并测试每个整数 L[k] 和所有先前的项目 L[j] 时,如果 L[j] 除以 L[k],您只需将 c[j](可能为 0)添加到您的全局三元组的计数器,因为这也意味着恰好存在 c[j] 项目 L[i] 使得 L[i] 划分 L[j]i < j

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

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

关于您的评论:

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

  • 如何从中得到解决方案? 运行一个简单的示例(提示:最好从左到右处理输入)。在步骤i,计算您可以从该特定点计算的内容(忽略i 右侧的所有内容),并尝试针对不同的i's 反复确定您计算的内容:这就是您要缓存的内容。在这里,当您在步骤 k (L[k] % L[j] == 0) 看到潜在的三元组时,您必须考虑在 L[j] 上会发生什么:“它的左边是否也有一些除数?每个都可以给我们一个新的三元组。让我们看看...但是等等!我们已经在步骤j 计算了这个值!让我们缓存这个值!“这就是你跳到座位上的时候。

【讨论】:

  • 非常简洁的解决方案。我尝试使用动态编程来解决它,但没有找到规则。能分享一下你的思考过程吗?比如你如何想出使用 dp 的想法以及如何定义转换公式。
  • 请根据您的算法告诉我以下数组的答案:a = [3, 6, 5, 30, 18]
  • 上面有 2 个有效的三元组,[3,6,18], [3,6,30]。您的代码给出了不同的答案...
  • @User_Targaryen 对不起,我没有验证,很可能是这样。但我看不出它是如何失败的:它会在您的样本上返回 2,不是吗?不管怎样,重要的是DP在这里适用而且很简单。
  • 不,它不返回 2.. 再次检查...即使我编写了与您类似的解决方案,但发现了缺陷..
【解决方案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

【讨论】:

  • 具有多重性。
【解决方案3】:

阅读Sieve of Eratosthenes,这是一种查找素数的常用技术,可用于查找您的“幸运三元组”。本质上,您需要以递增的值顺序迭代列表,并且对于每个值,将其乘以递增因子,直到它大于最大的列表元素,并且每次这些倍数中的一个等于列表中的另一个值时,倍数可以被基数整除。如果列表在给您时已排序,那么 i

例如给定列表[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 整除,16 可以被4 整除。而对于15,列表中没有倍数,因此没有可以与315 形成三元组的值。

【讨论】:

  • 这是一个很好的解决方案,但列表未排序。你还有别的想法吗?在处理未排序的数组时,常用的方法可能不起作用。我尝试预处理数据,但我不工作
  • Sieve of Eratosthenes 是一个很好的参考,它为解决方案增加了另一个视角。 Codility 上有课程和更多代码挑战,可能对其他人有用。
【解决方案4】:

解决问题的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) 时间。

Ultimate Step中,使用Precomputation Step的细节,帮助找到三元组..

【讨论】:

  • 看来这种方式和蛮力方式是一样的。经过预计算,我得到了 lst[j] 的合格数。如果我使用标记的数字 lst[j],整个过程就像将三个循环拆分为两个循环的两个部分,但计算数量是相同的。
  • @Anderson:如果你必须枚举所有不同的幸运三元组,那么最坏的情况必须是 O(N^3)。如果您只需要计算这种幸运三元组的数量,那么您可以在 O(N^2) 时间和 O(N) 空间中找到它。你想让我给你提供只返回这种三元组计数的代码吗??
  • @User_Targaryen 关于最坏情况(例如 2,4,8,...,2^N)的好点,但看起来如果数字是随机的,您应该能够达到平均值- 时间复杂度是二次的。也许涉及在扫描列表时构建一棵树。
  • @User_Targaryen,谢谢你的帮助,我只需要三倍数。只需实现 O((N ^ 2 )* lgN) 解决方案,它仍然会超过时间
【解决方案5】:

形成一个图 - 一个比当前索引多倍的索引数组。然后计算从图中引用的这些指数的倍数的总和。它的复杂度为 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;
 }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-06-24
    • 1970-01-01
    • 2022-01-27
    • 2021-06-15
    • 2014-09-23
    • 2021-03-16
    • 2010-12-07
    相关资源
    最近更新 更多