判斷等比數列

來自 LIOJ 上的題目,也是蠻有趣的一題。

常犯的錯誤

如果看完題目直接開始做,通常我們會這樣子寫:

1
2
3
4
5
6
7
8
function isSequence(arr) {
for(let i=1; i<arr.length; i++) {
// 判斷第二個元素 / 第一個元素 !== 第三個元素 / 第二個元素
if(arr[i]/arr[i-1] !== arr[i+1]/arr[i]) return false
}
return true
}
console.log(isSequence([1, 2, 4, 8, 16])) // false

為什麼最後是 false?

因為當迴圈跑到 i=4 的時候,裡面的 array[i+1] 會有問題 => array[5] 沒有元素。

所以這個時候迴圈的條件就得改成 for(let i=1; i<array.length-1; i++),雖然這樣就能得出正確結果,但同時也是很不直覺的寫法。

寫迴圈時都該注意最大與最小範圍

當 i 是從 1 ~ n-1 的時候,如果出現 i+1 ,那就意味著會超出迴圈的「最大範圍」;或反過來說,如果 i 是從 0 ~ n-1,那 i-1 就會超出迴圈的「最小範圍」。只要其中一項發生,那你的執行結果就一定會有問題。

更好的做法-預設值

我們可以先假設一個預設的「公比值」,接著在利用迴圈來做判斷:

1
2
3
4
5
6
7
8
function isSequence(arr) {
let n = arr[1] / arr[0] // 預設公比值
for(let i=1; i<arr.length; i++) {
if(arr[i]/arr[i-1] !== n) return false
}
return true
}
console.log(isSequence([1, 2, 4, 8, 16])) // true

如果一個數列真的是「等比數列」,那一定會符合我們預設的公比,所以能做出正確的判斷。

另外,迴圈區塊中的 arr[i] 都有乖乖待在迴圈的最大與最小範圍,所以不會有超出的問題。

所以這樣子寫起來就相對直覺,也能解出正確的解答。這個做法就跟 求次大值 的概念雷同,都是先假設一個「預設值」來做判斷。

養成好習慣,適當處理邊角案例

這一題的邊角案例會是 [] 或是 [1],就是「空陣列」或是「只有一個元素」。如果假設這兩個 case 都視為等比數列,那我們應該會這樣寫:

1
2
3
4
5
6
7
8
9
10
function isSequence(arr) {
if(arr.length <= 1) return true // 邊角案例
let n = arr[1] / arr[0]
for(let i=1; i<arr.length; i++) {
if(arr[i]/arr[i-1] !== n) return false
}
return true
}
console.log(isSequence([])) // true
console.log(isSequence([1])) // true

但其實你仔細觀察會發現,就算不加上這個判斷,最後的結果也會是 true

1
2
3
4
5
6
7
8
9
function isSequence(arr) {
let n = arr[1] / arr[0]
for(let i=1; i<arr.length; i++) {
if(arr[i]/arr[i-1] !== n) return false
}
return true
}
console.log(isSequence([])) // 依然是 true
console.log(isSequence([1])) // 依然是 true

這是因為當 arr.length 為 0 或 1 的時候,不會通過迴圈的判斷條件 i < arr.length,也就是說不會進到迴圈裡面,直接跳到 return true 的部分。

儘管以這個例子來說確實可以不加上額外判斷,可是還是會建議你加上去,這是一個比較好的習慣。因為如果換成其他的程式語言,在執行到 arr[1]arr[0] 卻發現存取不到元素的時候,是有機會直接噴出一個 error,然後你的程式就執行不下去,最後就 GG 了。

mentor-program-day16 JavaScript 中的科學記號表示法
Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×