【问题标题】:Minimum sub-vector size k with the divide and conquer technique使用分治技术的最小子向量大小 k
【发布时间】:2020-07-20 16:15:45
【问题描述】:

早上好,我有问题要解决:

你有一个大小为 n 的向量,你想找到一个大小为 m 且其元素之和最小的子向量

这是如何工作的一个例子是: see example of operation 其中最小子向量为:{1,3,1},总和为 5

我需要通过蛮力(下面解释的滑动窗口)和分而治之的技术来分析这个问题。然后我会写一个比较报告,并解释滑动窗口效果更好。本文是针对算法比较的大学项目。但我需要用 D&C 明确地构建它。

我已经按如下方式完成了,但是我遇到了基本情况和返回最小和子向量的问题。


// Function to find the minimum between two numbers
int min(int a, int b) { return (a < b)? a : b; }

// Function to find the minimum between three numbers
int min(int a, int b, int c) { return min(min(a, b), c); }

// Function to find the minimum sum that passes through the center of the vector
int minSumCenter(int v[], int l, int center, int h)
{

    // Elements to the left of the center
    int sum = 0;
    int left_sum = INT_MAX;
    for (int i = center; i >= l; i--)
    {
        sum = sum + v[i];
        if (sum < left_sum)
          left_sum = sum;
    }

    // Elements to the right of centre
    sum = 0;
    int right_sum = INT_MAX;
    for (int i = center+1; i <= h; i++)
    {
        sum = sum + v[i];
        if (sum < right_sum)
          right_sum = sum;
    }

    // Return de los elementos que están tanto a la izquierda como a la derecha
    return left_sum + right_sum;
}

// Minimum sum sub-vector size m, size v is h-l
int subvectorMinDyV(int v[], int l, int h, int m){
   // Base Case 1
   if ((h-l) <= m) {
       int sum = 0;
       for(int i=0; i<m; i++)
        sum += v[i];
       return sum;
  // Base Case 2
}else if(m*2-1 <= (h-l)){
       int sum=0;
       int sumMin = INT_MAX;
       for(int i=0; i<(l+h)-m;i++){
           sum=0;
           for(int j=i; j<m; j++)
            sum += v[j];

           if(sum < sumMin)
            sumMin = sum;
       }
       return sumMin;
   }


   int center = (l + h)/2;
   /* Possible cases
      a) minimum sum sub-vector is on the left
      b) minimum sum sub-vector is on the right
      c) minimum sum sub-vector is a in the middle */
   return min(subvectorMinDyV(v, l, center, m),
              subvectorMinDyV(v, center+1, h, m),
              minSumCenter(v, l, center, h));
}

int main(){
   int v[] = {6,10,4,2,14,1};
   int n = sizeof(v)/sizeof(v[0]);
   int sumMin = subvectorMinDyV(v, 0, n-1, 3);
   cout << "The minimum amount with DyV is: " << sumMin << endl;

   return 0;
}

非常感谢。

【问题讨论】:

    标签: c++ algorithm recursion divide-and-conquer


    【解决方案1】:

    我不确定您所说的 divide-and-conquer 到底是什么意思。正如其他人指出的那样,滑动窗口方法是O(n)。 (你不能做得更好,因为你需要至少查看每个元素一次。)

    您的解决方案很接近,除了您不必要地重新计算总和。这应该可以完成工作

    void subvectorMin(int* v, int n, int m)
    {
      if (n < m)
      {            
        std::cout << "Cannot calculate sub-vector m. (m<n)";
        return; // return early
      }
    
      // compute the sum of first m elements
      int sum = 0;  
      for(int i = 0; i < m; ++i) 
        sum += v[i];
    
      // assume answer is at position 0
      int pos = 0;
      int min_sum = sum;
    
      // check if there is a minimum sum somewhere else
      for(int i = m; i < n; ++i)
      {
        sum = sum + v[i] - v[i - m]; // THIS is the sliding window that 
                                     // avoids the sum being recomputed
    
        // if smaller sum is found, update the position
        if(sum < min_sum)
        { 
            min_sum = sum; 
            pos = i - m + 1;
        }
      }    
    
      std::cout << "The minimum component sum is: " << min_sum
                << " , subvector: {";
      for(int i = pos; i < pos + m; ++i)
          std::cout << " " << v[i];
      std::cout << " }" <<std::endl;
    }
    

    【讨论】:

    • 感谢您的回答,我需要使用分而治之的技术来将大问题分解为较小的问题: - 向量的前半部分:从 0 到中心 - 后半部分向量的:从中心+1 到 n - 检查最小量是否通过中心我已经完成了 here my codehere an example,但我无法很好地定义基本情况。
    • 我不知道你为什么有这个要求。当您的数据以这种方式重叠时,您最终将不得不在分区点上做额外的工作。你能解释一下为什么你必须这样做吗?
    • 感谢您的回复,我需要通过蛮力(上面解释的窗口滑动)和分而治之的技术来分析这个问题。然后我会写一份比较报告,并解释说使用窗户滑动效果会更好。这项工作是针对算法比较的大学项目。但我需要用 D&C 明确地构建它。
    • 好的。但是您的问题的代码与评论中的链接完全不同。编辑您的问题以反映什么不适合您。
    • 你能帮我@cigien 解决这个问题吗?我已经修改了问题。谢谢
    【解决方案2】:

    如果你观察你的例子,你会发现数组是如何分区的。

    for n = 6, m = 3
    
    v = 6|10|4|2|14|1
        ---------------
    p = 0|1 |2|3|4 |5
    
    g1: 0 1 2
    g2: 1 2 3 (1 2 sum already calculated)
    g3: 2 3 4 (2 3 sum already calculated)
    g4: 3 4 5 (3 4 sum already calculated)
    

    当你从左到右收集 m 元素后你需要做减去 c-mth position element. wherec` 是当前位置

        void subvectorMin(int* v, int n, int m, int p, int sum){
    
        if (p >= n) {
        return sum;
        }
    int tmp = sum - v[p-m]+ v[p];
        return Min(sum, Min(tmp, subvector(v, n, m, p+1, tmp)));
        }
    
    main() {
    for (i = 0; i < m; i++) 
    sum += v[i];
        subvectorMin(v, n, m, m,sum);
    

    【讨论】:

    • 我的想法是将我的算法转换为分而治之,这就是我在这张图片中展示的方式:URL 我认为我们应该做一些类似 mergeSort 的事情。感谢您的回复。
    • 它的滑动窗口问题。从左到右分开。为什么你总是分中间
    • Windows 滑动的效率为 O(n^2),我想通过分而治之的效率达到 O(n*log(n))。我能够构建的方法如下URL,但我对基本案例有疑问。谢谢
    • @Michael 滑动窗口是 O(n),因为在窗口的每张幻灯片上,我们通过添加窗口的新最后一个元素并减去 O(1) 中窗口的前第一个元素来更新最多 O(n) 次迭代。
    • 我得到 o(n^2) 因为我返回了和旁边的子向量。我需要分而治之,到目前为止我的方法是program,但它在基本情况下给了我一个错误。
    猜你喜欢
    • 2021-09-19
    • 1970-01-01
    • 2018-08-02
    • 1970-01-01
    • 2022-01-24
    • 1970-01-01
    • 1970-01-01
    • 2014-04-20
    • 2016-06-15
    相关资源
    最近更新 更多