找到产品大于总和的对

时间:2013-11-07 15:43:20

标签: algorithm optimization

作为输入,一个有序浮点数组,我需要找到每个(i,j)的{​​{1}}对A[i]*A[j]>=A[i]+A[j]的总数。 我已经知道了天真的解决方案,在其他循环中使用循环,这将给我O(n ^ 2)算法,但我想知道是否有更优化的解决方案。

5 个答案:

答案 0 :(得分:8)

这是一个O(n)算法。

让我们看一下A * B >= A + B

  • A, B <= 0时,它始终是真的。

  • A, B >= 2时,它始终是真的。

  • A >= 1, B <= 1(或B >= 1, A <= 1)时,它始终为假。

  • 0 < A < 1, B < 0(或0 < B < 1, A < 0)时,它可以是true或false。

  • 1 < A < 2, B > 0(或1 < B < 2, A > 0)时,它可以是true或false。

这是一个可视化,由Wolfram AlphaGeobits提供:

现在,进入算法。

*要查找一个数字介于0和1或1和2之间的对,我会执行与the 3SUM problem相似的操作。

*“选择2”这里指的是combinations

  • 计算两者都为负数的所有对

    进行二元搜索以找到第一个正数(&gt; 0)数字的索引 - O(log n)

    由于我们有索引,我们知道有多少数字是负数/零,我们只需要选择其中的2个,这样就是amountNonPositive * (amountNonPositive-1) / 2 - O(1)

  • 查找一个介于0和1之间的所有对

    进行二元搜索以找到最后一个数字的索引&lt; 1 - O(log n)

    从该索引开始作为右索引,最左边的元素作为左索引。

    重复此操作,直到右侧索引&lt; = 0 :(在O(n)中运行)

    • 虽然产品小于总和,但减少左侧索引

    • 计算大于左侧索引的所有元素

    • 减少正确的指数

  • 查找1到2之间的所有对

    进行二元搜索以查找第一个数字的索引&gt; 1 - O(log n)

    从该索引开始,作为左索引,最右边的元素作为右索引。

    重复此操作,直到左侧索引&gt; = 2 :(在O(n)中运行)

    • 虽然产品大于总和,但减少正确的指数

    • 计算大于正确索引的所有元素

    • 增加左侧索引

  • 使用两个数字&gt; = 2

    计算所有对

    在最后一步结束时,我们处于第一个索引&gt; = 2。

    现在,从那里,我们只需要选择所有剩余数字中的2个,
    所以它再次amountGreaterEqual2 * (amountGreaterEqual2-1) / 2 - O(1)

答案 1 :(得分:1)

您可以在O(n log n)中找到并打印对(以简写形式)。

对于每个A[i],满足条件(1)的最小数量k。 所有大于k的值也将满足条件。

使用二分搜索找到最低j A [j]&gt; = k为O(log n)

所以你可以找到并打印出这样的结果:

(i, j)
(1, no match)
(2, no match)
(3, >=25)
(4, >=20)
(5, >=12)
(6, >6)
(7, >7)
...
(n-1, n)       

如果要打印所有组合,则为O(n ^ 2),因为组合数为O(n ^ 2)。

(*)要处理负数,它实际上需要更复杂一些,因为满足等式的数字可能超过一个范围。 我并不完全确定它对于小的负数如何表现,但如果范围的数量不是绝对有限,那么我的解决方案不再优于O(n ^ 2)。

答案 2 :(得分:1)

这是二分搜索,O(n log n):

A*B = A+B处的每个号码都有一个断点。您可以将其减少到B = A / (A - 1)。一侧或另一侧的所有数字都适合它。如果有负数等没关系

  • 如果A < 1,那么所有数字<= B都适合。

  • 如果A > 1,那么所有数字>= B都适合。

  • 如果A == 1,则没有匹配(除以零)。

Wolfram Alpha link


所以有些伪代码:

loop through i
    a = A[i]
    if(a == 1)
        continue
    if(a >= 2)
        count += A.length - i 
        continue

    j = binsearch(a / (a-1))

    if(j <= i)
        continue

    if(a < 1)
        count += j-i
    if(a > 1)
        count += A.length - j

答案 3 :(得分:0)

这是一个O(n)算法,可以在数组元素为正时解决问题。

当元素为正数时,我们可以这样说:

  • A[i]*A[j] >= A[i]+A[j]j>i A[k]*A[j] >= A[k]+A[j]k k>i满足A[i]*A[j] < A[i]+A[j](因为数组已排序)。

  • 如果j>i A[i]*A[k] < A[i]+A[k] kk<j int findNumOfPairs(float A[]) { start = 0; end = A.length - 1; numOfPairs = 0; while (start != end) { if (A[start]*A[end] >= A[start]+A[end]) { numOfPairs += end - start; end--; } else { start++; } } return numOfPairs; } 满足{{1}}。

(当两个数字都是分数时,这些事实不成立,但无论如何都不会满足条件)

因此我们可以执行以下算法:

{{1}}

答案 4 :(得分:-1)

如果排除所有小于1.0的浮点数,因为任何数字倍数小于1,x * 0.3 = A [i] + A [j],每个i <1。 j,所以我们只需要计算数组的数量来计算对的数量(i,j),我们可以使用关于排列和组合的公式来计算它。公式应为n(n-1)/ 2。