簡體   English   中英

如何將這個 O(n^2) 算法轉換為 O(n)?

[英]How to convert this O(n^2) algorithm to O(n)?

https://www.codewars.com/kata/is-my-friend-cheating/train/javascript

我的目標是 devise a function 找到滿足方程a * b == sum(1, 2, 3..., n-2, n-1, n) - a - b數對(a, b) .

以下代碼找到所有對,但速度太慢且超時。 我在這個挑戰的評論中看到算法需要具有 O(n) 復雜度才能通過。 這是怎么做到的?

function removeNb (n) {
  if(n===1) return null;

  let sum = (n * (n+1))/2;

  let retArr = [];
  let a = n;
  while( a !== 0){
    let b = n;
    while( b !== 0){
       if(b != a && a*b == ((sum - b) - a) ){
         retArr.push([a,b]);
       }
       b--;
    }
    a--;
  }
  retArr.sort( (a,b) => a[0] - b[0]);
  return retArr;
} 

感謝大家的幫助:這是我的最終解決方案:

function removeNb (n) {
  let retArr = [];
  let a = 1;
  let b = 0;
  let sumN = (n * (n+1))/2; 

  while( a <= n){
    b = parseInt((sumN - a) / (a + 1));
    if( b < n && a*b == ((sumN - b) - a) ) 
      retArr.push([a,b]);
    a++;
  }
  return retArr;
} 

我認為我的主要問題是當我試圖求解b時我的代數出現了一個(令人尷尬的)錯誤。 以下是任何想知道的人的正確步驟:

a*b = sum(1 to n) - a - b
ab + b = sumN - a
b(a + 1) = sumN - a
b = (sumN - a) / (a + 1)

您可以求解 b 並得到: b = (sum - a)/(a + 1) (給定 a != -1)

現在迭代a -> O(n)

這是一個實現:

function removeNb(n){
    var sum = (1 + n) * n / 2;
    var candidates = [];

    // O(n)
    for(var y = n; y >= 1; y--){
        x = (-y + sum) / (y + 1);

        /*
         * Since there are infinite real solutions,
         * we only record the integer solutions that
         * are 1 <= x <= n.
         */
        if(x % 1 == 0 && 1 <= x && x <= n)
            // Assuming .push is O(1)
            candidates.push([x, y]);
    }

    // Output is guaranteed to be sorted because
    // y is iterated from large to small.
    return candidates;
}

console.log(removeNb(26));
console.log(removeNb(100));

https://jsfiddle.net/DerekL/anx2ox49/

根據您的問題,它還指出

在該序列中,他選擇了兩個數字 a 和 b。

但是它沒有提到ab是唯一的數字,因此代碼中不包含檢查。

正如其他答案中所解釋的,可以使用 O(n) 算法求解 b。 此外,考慮到解決方案的對稱性——如果 (a,b) 是一個解決方案,那么 (b,a) 也是——還可以節省一些迭代,一次添加幾個解決方案。 要知道需要多少次迭代,讓我們注意 b > a 當且僅當 a < -1+sqrt(1+sum)。 為了證明這一點:

(sum-a)/(a+1) > a ; sum-a > a^2+a ; sum > a^2+2a ; a^2+2a-sum < 0 ; a_1 < a < a_2 

其中 a_1 和 a_2 來自 2 度方程解:

a_1 = -1-sqrt(1+sum) ; a_2 = -1+sqrt(1+sum)

由於 a_1 < 0 且 a > 0,最終我們證明了 b > a 當且僅當 a < a_2。

因此我們可以避免在 -1+sqrt(1+sum) 之后進行迭代。

一個工作示例:

function removeNb (n) {
  if(n===1) return null;
  let sum = (n * (n+1))/2;
  let retArr = [];
  for(let a=1;a<Math.round(Math.sqrt(1+sum));++a) {
      if((sum-a)%(a+1)===0) {
          let b=(sum-a)/(a+1);
          if(a!==b && b<=n) retArr.push([a,b],[b,a]);
      } 
  }
  retArr.sort( (a,b) => a[0] - b[0]);
  return retArr;
}

然而,對於這個實現,我們仍然需要最后的排序。 為了避免它,我們可以注意到 b=(sum-a)/(a+1) 是 a 的遞減 function(推導證明)。 因此我們可以構建 retArr 連接兩個 arrays,一個在末尾添加元素(push),一個在開頭添加元素(unshift)。 一個工作示例如下:

function removeNb (n) {
  if(n===1) return null;
  let sum = (n * (n+1))/2;
  let retArr = [];
  let retArr2 = [];
  for(let a=1;a<Math.round(Math.sqrt(1+sum));++a) {
      if((sum-a)%(a+1)===0) {
          let b=(sum-a)/(a+1);
          if(a!==b && b<=n) {
              retArr.push([a,b]);
              retArr2.unshift([b,a]); // b>a and b decreases with a
          }
      } 
  }
  retArr=retArr.concat(retArr2); // the final array is ordered in the 1st component
  return retArr;
}

作為非母語人士,我會說參考文獻“所有(a,b)是序列 1 到 n 中可能刪除的數字”中的短語暗示 a,= b。 所以我添加了這個約束。

let n = 100;
let sumOfNum = n => {
  return (n * (n + 1)) / 2;
};
let sum = sumOfNum(n);
let response = [];
for (let i = 1; i <= 26; i++) {
  let s = (sum - i) / (i + 1);
  if (s % 1 == 0 && s * i == sum - s - i && s <= n) {
    response.push([s, i]);
  }
}

// 這是 O(N) 時間復雜度

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM