【问题标题】:Total number of possible triangles from n numbers来自 n 个数字的可能三角形的总数
【发布时间】:2011-11-13 08:40:33
【问题描述】:

如果给出n 数字,我将如何找到可能的三角形的总数?有没有什么方法可以在O(n^3) 时间内做到这一点?

我正在考虑 a+b>cb+c>aa+c>b 成为三角形的条件。

【问题讨论】:

  • 提示:对数字进行排序,并为每一对 (a,b) (b≥a) 找到所有满足 b≤c≤a+bcs。
  • 但在最坏的情况下,即使在平均情况下,我们也会得到 O(n^3) 复杂度?或者可能是我被误解了。你能详细说明一下吗?

标签: c complexity-theory


【解决方案1】:

假设给定的 n 中没有相等的数字,并且允许多次使用一个数字。例如,我们给出了一个数字 {1,2,3},所以我们可以创建 7 个三角形:

  1. 1 1 1
  2. 1 2 2
  3. 1 3 3
  4. 2 2 2
  5. 2 2 3
  6. 2 3 3
  7. 3 3 3

如果这些假设中的任何一个不正确,那么修改算法很容易。

这里我提出在最坏情况下需要 O(n^2) 时间的算法:

  1. 对数字进行排序(升序)。 我们将采用三元组 ai
  2. 对于每个 i、j,您需要找到满足 ak = aj >= ai 我们只能违反 ak

考虑两对 (i, j1) 和 (i, j2) j1 = k1(在 (i, j1) 的第 2 步中找到)。这意味着如果你对 j 进行迭代,你只需要检查从前一个 k 开始的数字。因此它为每个特定 i 提供了 O(n) 时间复杂度,这意味着整个算法的时间复杂度 O(n^2)

C++源代码:

int Solve(int* a, int n)
{
    int answer = 0;
    std::sort(a, a + n);

    for (int i = 0; i < n; ++i)
    {
        int k = i;

        for (int j = i; j < n; ++j)
        {
            while (n > k && a[i] + a[j] > a[k])
                ++k;

            answer += k - j;
        }
    }

    return answer;
}

反对者更新:

这绝对是 O(n^2)!请仔细阅读 Thomas H. Cormen 关于摊销分析的“算法介绍”一章(第二版中的 17.2)。 有时通过计算嵌套循环来发现复杂性是完全错误的。 在这里,我尝试尽可能简单地解释它。让我们修复 i 变量。然后对于那个 i 我们必须将 ji 迭代到 n (这意味着 O(n) 操作)和内部 while 循环将 ki 迭代到 n (这也意味着 O(n) 操作)。 注意:对于每个 j,我不会从头开始 while 循环。我们还需要对从 0n 的每个 i 执行此操作。所以它给了我们 n * (O(n) + O(n)) = O(n^2)

【讨论】:

  • 所以你的while循环是免费运行的?你计算过while循环的复杂度吗,因为只有2个for会给出O(n^2)
  • 伙计们,请阅读我的更新并仔细观察。它是 O(n^2)。
  • 我很抱歉..我已经纠正了我的错误,也赞成并接受了这个作为答案:-)
  • 我们不能用二分查找来找到'k'的值吗?这会将时间复杂度降低到 nlogn?
  • @Wisdom'sWind 您的解决方案是一件艺术品。投票者试图理解 k 的移动方式。它不是每次都在while循环中重新初始化,这就是算法实现O(n^2)复杂度的方式
【解决方案2】:

O(n^2*logn)中有一个简单的算法。

  • 假设您希望所有三角形都为三元组(a, b, c) 其中a &lt;= b &lt;= c
  • 有 3 个三角形不等式,但只有 a + b &gt; c 就足够了(其他则微不足道)。

现在:

  • O(n * logn)中的序列进行排序,例如通过合并排序。
  • 对于每一对 (a, b), a &lt;= b,剩余值 c 必须至少为 b 且小于 a + b
  • 所以你需要统计区间[b, a+b)的项目数。

这可以简单地通过对 a+b (O(logn)) 进行二进制搜索并计算 [b,a+b) 中的项目数来获得 b-a 的每种可能性。

一起O(n * logn + n^2 * logn)O(n^2 * logn)。希望这会有所帮助。

【讨论】:

  • 是的,但复杂性就在这里。 BTW,@cartesius00,你知道b的索引,不用二分查找。
【解决方案3】:

如果你使用二进制排序,那就是 O(n-log(n)),对吧?把你的二叉树放在手边,对于每一对 (a,b),其中 a b 和 c

【讨论】:

  • 这将始终评估为假,请参阅我的答案
  • @artistoex - 如果您指的是关于不平等和对称性的段落,那有一些严重的缺陷。这是什么玩笑?
  • @david 实际上不,不是开玩笑 :-) 是的,这有点轻率。
  • @david:对不起,但这不是答案,O(n-log(n)) 的东西不正确。
  • @James 我错误地理解它是 n-long(n) 并且应该是 O(n logn),因为没有婴儿车算法可以排序小于这个。此外,这让我有足够的想法将复杂性从 O(n^3) 降低到 O(n^2 logn)。所以我把这个标记为正确的。否定答案的人应撤消此操作。
【解决方案4】:

设a、b、c为三个边。三角形必须满足以下条件(两条边之和大于第三条边)

i) a + b > c
ii) b + c > a
iii) a + c > b

以下是计算三角形的步骤。

  1. 按非降序对数组进行排序。

  2. 将两个指针‘i’和‘j’分别初始化为第一个和第二个元素,并将三角形的计数初始化为0。

  3. 修复 'i' 和 'j' 并找到最右边的索引 'k'(或最大的 'arr[k]')使得 'arr[i] + arr[j] > arr[k]' .以'arr[i]'和'arr[j]'作为两侧可以形成的三角形的数量是'k - j'。将“k - j”添加到三角形计数中。

让我们将“arr[i]”视为“a”,将“arr[j]”视为b,将“arr[j+1]”和“arr[k]”之间的所有元素视为“c”。由于‘arr[i]

4.增加'j'以再次修复第二个元素。

请注意,在第 3 步中,我们可以使用之前的“k”值。原因很简单,如果我们知道'arr[i] + arr[j-1]'的值大于'arr[k]',那么我们可以说'arr[i] + arr[j]'也将大于 'arr[k]',因为数组是按升序排序的。

5.如果‘j’已经结束,则增加‘i’。将‘j’初始化为‘i+1’,‘k’初始化为‘i+2’,重复步骤3和4。

时间复杂度: O(n^2)。 由于 3 个嵌套循环,时间复杂度看起来更高。如果我们仔细观察算法,我们会发现 k 在最外层循环中只被初始化一次。对于最外层循环的每次迭代,最内层循环最多执行 O(n) 时间,因为对于 j 的所有值,k 从 i+2 开始并上升到 n。因此,时间复杂度为O(n^2)。

【讨论】:

    【解决方案5】:

    我已经制定了一个在 O(n^2 lgn) 时间内运行的算法。我认为它是正确的... 代码是用 C++ 编写的...

    int Search_Closest(A,p,q,n)  /*Returns the index of the element closest to n in array 
                                      A[p..q]*/
    
    {
       if(p<q)
       {
          int r = (p+q)/2;
          if(n==A[r])
             return r;
          if(p==r)
             return r;
          if(n<A[r])
             Search_Closest(A,p,r,n);
          else 
             Search_Closest(A,r,q,n);
       }
       else
          return p;
     }
    
    
    
       int no_of_triangles(A,p,q) /*Returns the no of triangles possible in A[p..q]*/
       {
          int sum = 0;
          Quicksort(A,p,q);  //Sorts the array A[p..q] in O(nlgn) expected case time
          for(int i=p;i<=q;i++)
              for(int j =i+1;j<=q;j++)
               {
                   int c = A[i]+A[j];
                   int k = Search_Closest(A,j,q,c);
                   /* no of triangles formed with A[i] and A[j] as two sides is (k+1)-2 if A[k] is small or equal to c else its (k+1)-3. As index starts from zero we need to add 1 to the value*/
                   if(A[k]>c)
                        sum+=k-2;
                   else 
                        sum+=k-1;
                }
           return sum;
       }
    

    希望对你有帮助......

    【讨论】:

    • 我有一个类似的解决方案,除了我在搜索最近的一对边,而不是最近的单边。这些解决方案的问题在于它们多次生成/计数相同的三角形(例如,三元组 3nn) 复杂度。
    • 这就是我们在检查条件之前删除重复项的原因。就像一旦我们有 3,4,5;我们永远不会回到 4,5,3。像 i=0; i
    • @Peeyush:我按原样运行了上面的代码,并抑制了重复。上面的代码显然产生了比实际更多的解决方案。
    【解决方案6】:

    possible answer

    虽然我们可以使用二分搜索来找到 'k' 的值,从而提高时间复杂度!

    【讨论】:

    • 这并没有提供问题的答案。要批评或要求作者澄清,请在其帖子下方发表评论。
    • @Shai 这是从数组中计算三角形数量的链接!
    • @shivi 他们也给出了这个 SO 链接作为参考......现在再循环一个;)
    【解决方案7】:
    N0,N1,N2,...Nn-1
    sort
    X0,X1,X2,...Xn-1 as X0>=X1>=X2>=...>=Xn-1
    choice X0(to Xn-3) and choice form rest two item x1...
    choice case of (X0,X1,X2)
    check(X0<X1+X2)
    OK is find and continue
    NG is skip choice rest
    

    【讨论】:

    • 复杂性如何?选择函数选择 X0 , X1 和 X2,因此,它仍然是 O(n^3),尽管在某些情况下由于跳过我们可以节省时间,但并非总是如此。
    • 如果所有组合都OK(最坏情况?),所有组合都必须检查(不可跳过),如您所想,但是(X0,X1,X2)好像在NG时跳过,(X0, Xn-2, Xn -1) 可以跳过检查过程。所以你可以跳过任何情况。
    • 那么您认为跳过几个案例会降低复杂性吗?
    • 嗯,我觉得效果丢失了,程序太复杂了。
    【解决方案8】:

    似乎没有比 O(n^3) 更好的算法了。在最坏的情况下,结果集本身有 O(n^3) 个元素。

    例如,如果给定 n 个相等的数字,则算法必须返回 n*(n-1)*(n-2) 个结果。

    【讨论】:

    • 有趣,本来可以更有趣的,c>d 和 c>a-b 并不意味着 d>a-b :-)。
    • @peeyush 现在,这是一个有效的结果(所以我希望):-)
    • 我删除了答案的前一部分,以免引起混淆。
    • @peeyush n^logn 远不止 any 多项式
    • 已编辑:我认为 David 的解释给出了 (n^2 logn) 算法,因为即使在 n 等于 no 的情况下它也会起作用,并且可以在每个 a 和 b 的 logn 时间内找到 c。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-01-29
    • 1970-01-01
    • 2013-05-20
    • 2010-10-08
    • 1970-01-01
    相关资源
    最近更新 更多