2017-04-22 80 views
10

我試圖this Codewars challenge問題涉及找到一個數字的除數,然後計算這些除數的平方和。我發現了兩個解決這個問題的方法。Javascript性能:reduce()vs for-loop

第一種方法是基於對finding the sum of all divisors另一個#1的問題,似乎巧妙起初:

function divisorsSquared(n) { 
    // create a numeric sequence and then reduce it 
    return [...Array(n+1).keys()].slice(1) 
    .reduce((sum, num)=>sum+(!(n % (num)) && Math.pow(num,2)), 0); 
} 

我曾經用一個簡單的for循環第二種方法:

function divisorsSquared(n) { 
    var sum = 0; 
    for(var i = 1; i<= n; i++){ 
    if(n % i === 0) sum += Math.pow(i,2); 
    } 
    return sum; 
} 

現在我注意到第一種方法比第二種方法明顯慢,並且快速地確認了這一點。

我的問題是:爲什麼第一種方法慢得多,在生產代碼中哪種方法更可取?

關於Codewars我注意到,對於許多挑戰,使用類似數組方法的巧妙的單行解決方案。作爲初學者,即使性能更差,這種解決方案可能會被認爲比循環更好嗎?

+5

如果你在看代碼,第一個使用一個構造函數,然後循環傳播,然後遍歷拿到鑰匙,然後遍歷切片,然後遍歷減少...第二個迭代...一次。 – adeneo

+0

我沒有想到,但現在看起來很明顯。謝謝! –

回答

2

功能或命令式方法在JS中有所不同。勢在必行永遠贏。然而,真正的事情是大多數時候更好的算法是贏家。你的代碼是一個天真的方法。只要檢查整數直到目標值的平方根,並且每次檢查將得到兩個答案,您就可以調整它以更好地工作。如果目標是100,如果2是紅利,那麼100/2也必須是紅利。因此,最多檢查Math.sqrt(100) - 1並小心處理10以便不考慮兩次。

相應地,現在減少功能的解決方案擊敗了當務之急的解決方案。

function divisorsSquared(n) { 
 
    var sn = Math.sqrt(n); 
 
    return Array.from({length:~~sn-1},(_,i) => i+1) 
 
       .reduce((s,m) => n%m ? s : s + m*m + (n/m)*(n/m), 0) + (n%sn ? 0 : sn*sn); 
 
} 
 
var result = 0; 
 
console.time("functional and tuned"); 
 
result = divisorsSquared(1000000); 
 
console.timeEnd("functional and tuned"); 
 
console.log("for input: 1000000 the result is:",result); 
 

 

 
function dvssqr(n) { 
 
    var sum = 0; 
 
    for(var i = 1; i<= n; i++){ 
 
    if(n % i === 0) sum += Math.pow(i,2); 
 
    } 
 
    return sum; 
 
} 
 

 
console.time("imperative and naive"); 
 
result = dvssqr(1000000); 
 
console.timeEnd("imperative and naive"); 
 
console.log("for input: 1000000 the result is:",result);

4
  • Array(n+1)分配與n + 1個元素的數組,Array(n+1).keys()返回在創建數組的索引的迭代器,但蔓延運營商[...Iterator]幫助「解包」這個迭代到另一個陣列,然後終於slice(1)走來複制第二個創建的數組從索引1開始,該數組分配另一個數組(第三個),丟棄數字0。所以那3個分配,但是2個被分配。您的for-loop不分配任何陣列,它是在O(n)的一個簡單穿越只有2分配用於isum,所以它是更有效的
  • sum+(!(n % (num)) && Math.pow(num,2))是基本上相同if(n % i === 0) sum += Math.pow(i,2);if方法是方式更具有可讀性。如果我是法官,我會選擇第二種方法,因爲它更有效率,但它有利於可讀性。
2

縱觀代碼,for循環顯然不那麼複雜且更具可讀性。

考慮到你在一個團隊中工作,你的團隊成員的最大數量將知道代碼正在做什麼。 有些人不得不查看reduce()方法是什麼,但他們也會知道發生了什麼。 所以在這裏,for循環更容易讓其他人閱讀和理解。

另一方面,本地數組函數(filter(), map(), reduce())將使您無法編寫一些額外的代碼 ,並且性能也較慢。

對於初學者,我認爲for-loops應該比本地數組函數更好。