問題描述:
We are asking for a function to take a positive integer value, and return a list of all positive integer pairs whose values - when squared- sum to the given integer.
For example, given the parameter 25, the function could return two pairs of 5,0 and 3,4 because 5^2 + 0^2 = 25 and 3^2 + 4^2 = 25.
We might express that in pseudo-code like this:
allSquaredPairs(25) == [[5,0],[3,4]];
題目本身比較簡單,就是給定一個整數,求出平方和等於該整數的兩個數的序列。
解法
方法1:遍歷所有數字以得到結果
function allSquarePairs(num){ var temp = Math.sqrt(num); var result = []; for(var i=0; i<=temp; i++){ for(var j=i; j<=temp; j++) if(i*i+j*j==num) result.push([i,j]); } return result; }
然而雖然這個方法是正確的,卻無法通過測試。因為這個演算法的複雜度還是蠻大的,遇到很大的num就得花比較久的時間才能得出結果了。
那麼怎麼降低複雜度呢?我覺得問題主要出在j的遍歷上。對於每個i,j都得從頭遍歷一次,這顯然貢獻了不少的計算量。
我想了想,或許可以用二分查詢,每給定一個i,就在i與temp之間取中值,若j太大,則在前一個區間繼續遍歷;太小,則換成下一個區間。
正當我絞盡腦汁地想怎麼完美地植入二分法的時候,忽然靈光一閃,其實根本沒有必要這麼複雜的:給定了一個i之後,其實我們可以通過計算直接得出j的值,完全沒有必要遍歷的……
方法2: 直接計算j的值
function allSquaredPairs(num) { var temp = Math.sqrt(num); var result = []; var j=0; for(var i=0; i<=temp; i++){ j=Math.sqrt(num-i*i); if(isInt(j) && j>=i) result.push([i,j]); } return result; }
以上,遍歷i的時候,通過計算(num-i^2)的平方根,判斷是否為整數,如果是,則得到了一組值,否則繼續檢索。如此一來少了一個迴圈計算量就大大減少了,順理成章地通過了測試。
改進
1> isInt()的實現
上面的程式碼中isInt()表示判斷一個數字是否為整數,是則返回true,否則為false。
當然這裡沒必要使用過函式,用簡單的表示式會更方便一些,這裡只是為了佔個位。
那麼問題來了,怎樣判斷一個數字是否為整數呢?
在玩這道題之前,我知道有這些方法:
a>Math.floor(num) == num
對給定的數字取整,如果與原來的數字相等,則為整數。
b>parseInt(num) == num
和上面一樣的,只不過使用不同的方法
c>"^-?//d+$"
當然還可以使用強大的正規表示式。
做完這道題之後我才意識到還有更簡潔的形式:
d>num == num | 0
num | 0 捨棄了小數部分,和Math.floor()有些相似。不過還是不一樣的,如果num是負數的時候區別就出來了。
f>num == ~~num
和上面一樣的~
g>num == num>>0
同上~
g>num%1 == 0
與上面的幾個方法相反,這裡檢測的是小數部分。
2> 冗餘的判斷條件
在isInt()的旁邊我們還做了一個j>i的條件,以此避免出現重複序列,比如[0,5]和[5,0]。
實際上這點完全可以在設定temp的值時就過濾掉。只要讓temp=Math.sqrt(num/2)而不是temp=Math.sqrt(num),就可以去掉後面這個j>i的條件了。