查找包含200000+个元素的2个数组元素的乘积最小的最快方法

时间:2020-01-12 06:52:10

标签: c++ algorithm optimization minimum

我有一个数组a[n]。我们输入了数字n。如果满足以下条件,我需要找到a[i]a[j]的最小乘积:

1)abs(i - j) > k

2)a[i] * a[j]最小化

这是我的解决方案(非常幼稚):

#include <iostream>
using namespace std;
#define ll long long
int main() {
    ll n,k; cin >> n >> k;

    ll a[n]; for(ll i=0;i<n;i++) cin >> a[i];

    ll mn; bool first = true;

    for(ll i=0;i<n;i++) {
        for(ll j=0;j<n;j++) {
            if(i!=j)
            if(abs(i-j) > k) {
                if(first) {
                    mn = a[i]*a[j];
                    first = false;
                } else if(a[i]*a[j] < mn) mn = a[i]*a[j];
            }
        }
    }
    cout << mn << endl;
}

但是我想知道是否有更快的方法来找到具有距离的最小产品?

3 个答案:

答案 0 :(得分:12)

假设至少有一对满足条件的元素,并且其中两个元素没有相乘,那么可以在curl -X POST https://url.com -H 'authorization: Token YOUR_Session_TOKEN' -H 'content-type: application/json' -d '{"app_ids":["com.exmaple.app"], "data" : {"title":"Title", "content":"Content"}} 时间和Theta(n-k)空间的最坏情况和最佳情况下完成此操作,像这样:

Theta(1)

就时间和空间的渐近最坏情况复杂度而言,这是最佳的,因为最佳乘积可能是auto back_max = a[0]; auto back_min = a[0]; auto best = a[0]*a[k+1]; for(std::size_t i=1; i<n-(k+1); ++i) { back_max = std::max(back_max, a[i]); back_min = std::min(back_min, a[i]); best = std::min(best, std::min(a[i+k+1]*back_max, a[i+k+1]*back_min)); } return best; ,且任何a[0]元素的距离至少为n-(k+1),因此解决该问题的任何算法都至少需要读取k+1个整数。


该算法的思想如下:

最优产品使用n-(k+1)的两个元素,假设它们是aa[r]。在不失一般性的前提下,我们可以假设a[s]是乘积,是可交换的。

由于限制s > r,这意味着abs(s-r) > k。现在s >= k+1可能是满足此条件的每个索引,因此我们遍历这些索引。那是所示代码中s上的迭代,但是为了方便起见,它被i移了(没关系)。对于每次迭代,我们需要找到以k+1作为最大索引的最优乘积,并将其与先前的最佳猜测进行比较。

i+k+1配对的可能索引由于距离要求而小于或等于i+k+1。我们也需要对所有这些进行迭代,但是这是不必要的,因为固定的ia[i+k+1]*a[j]超过j的最小值等于i产品(在min(a[i+k+1]*max(a[j]), a[i+k+1]*min(a[j]))的最小值和最大值中取最小值,说明a[j]的两个可能的符号,或者等效地是两个单调性的两个可能的方向。)

由于我们在此处对其进行优化的a[i+k+1]值集仅为a[j],在{a[0], ..., a[i]}的每次迭代中仅增加了一个元素(a[i]),因此我们如果i大于或小于先前的最佳值,则可以通过更新单个变量来简单地跟踪max(a[j])min(a[j])。这是在代码示例中使用a[i]back_max完成的。

迭代的第一步(back_min)在循环中被跳过,而是作为变量的初始化执行。

答案 1 :(得分:6)

不确定最快

对于没有 i 的简单问题,最小乘积是来自两个最小和最大元素的对的乘积。

因此,(以下内容太复杂了,请参见walnut's answer
(•如果k≤n
•将minProduct初始化为a [0] * a [k + 1])

  • 保留两个dynamic minmax data structures upToI beyondIplusK
    以{}和{a [ j ]开头| k j }
  • 从0到 n - k -1的每个 i
    • 向[em> upToI
    • 添加一个[ i ]
    • beyondIplusK
    • 中删除一个[ i + k ]
    • 在其中检查新的最小产品
      min( upToI )×min( beyondIplusK ),min( upToI )×max( beyondIplusK ),
      max( upToI )×min( beyondIplusK )和max( upToI )×max( beyondIplusK

答案 2 :(得分:4)

“最小幅度”

找到2个“最小幅度”元素,然后(找到两个零或搜索整个数组之后),将它们相乘。

不包含abs(i - j) > k部分的“最低价值”

有3种可能性:

  • 两个最高(最小的)负数

  • 两个最低(最小数量级)的非负数

  • 最低(最大)负数和最高(最大)非负数

您可以搜索所有6个值并找出产品,最后一个是最好的。

但是;一旦您看到零,就知道您不需要再了解前两种可能性。并且一旦看到一个负数和一个非负数,便知道您只在乎第三个可能性。

这将导致具有3个状态的有限状态机-“关心所有3种可能性”,“除非看到负数,否则答案为零”和“仅关心最后一种可能性”。这可以实现为一组3个循环,其中(有限状态机的)状态改变时,其中2个循环跳到另一个循环的中间(goto

具体来说,它看起来可能有点模糊(未经测试):

   // It could be any possibility

   for(ll i=0;i<n;i++) {
       if(a[i] >= 0) {
            if(a[i] < lowestNonNegative1) {
                lowestNonNegative2 = lowestNonNegative1;
                lowestNonNegative1 = a[i];
            }
            if(lowestNonNegative2 == 0) {
                goto state2;
            }
       } else {
            if(a[i] > highestNegative1) {
                highestNegative2 = highestNegative1;
                highestNegative1= a[i];
            }
            if(lowestNonNegative1 < LONG_MAX) {
                goto state3;
            }
       }
   }
   if(lowestNonNegative2 * lowestNonNegative1 < highestNegative2 * highestNegative1) {
       cout << lowestNonNegative2 * lowestNonNegative1;
   } else {
       cout << highestNegative2 * highestNegative1;
   }
   return;

   // It will be zero, or a negative and a non-negative

   for(ll i=0;i<n;i++) {
state2:
       if(a[i] < 0) {
           goto state3;
       }
   }
   cout << "0";
   return;

   // It will be a negative and a non-negative

   for(ll i=0;i<n;i++) {
state3:
       if(a[i] < lowestNegative) {
           lowestNegative = a[i];
       } else if(a[i] > highestNonNegative) {
           highestNonNegative = a[i];
       }
    }
    cout << lowestNegative * highestNonNegative;
    return;

使用abs(i - j) > k部分的“最低价值”

在这种情况下,您仍然有3种可能性;并可以使其与“具有有限状态机的3个循环”相同的方法工作,但是它太凌乱/难看。对于这种情况,一个更好的选择可能是对阵列进行预扫描,以确定是否有零,以及它们是负数还是正数。这样,在进行预扫描后,您可以知道答案为零,也可以选择仅针对特定可能性设计的循环。