蠻有挑戰性的一題
簡述
這題是來自程式導師計畫 week2 的超級超級挑戰題。
解題的邏輯能用「直式算數」來思考,也就是把大問題拆成小問題的一種思考模式。
大數相加
建議先把大數相加弄懂再去解相乘會比較好理解,因為在做相乘時也需要用到相加的概念:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
| function add(a, b){ let result = ''; let hasCarryBit = false; const lengthA = a.length; const lengthB = b.length; let totalDigits = undefined;
if (lengthA > lengthB) { totalDigits = lengthA; for (let i=0; i<lengthA - lengthB; i++) { b = '0' + b; } } else if (lengthA < lengthB) { totalDigits = lengthB; for (let i=0; i<lengthB - lengthA; i++) { a = '0' + a; } } else { totalDigits = lengthA; }
for (let i=totalDigits-1; i>=0; i--) { let value = Number(a[i]) + Number(b[i]); if (hasCarryBit) { value += 1; hasCarryBit = false; } if (value >= 10) { hasCarryBit = true; }
result = value%10 + result;
if (i===0 && hasCarryBit) { result = '1' + result; } } return result; }
const TESTNUMBER = '124902814902890825902840917490127902791247902479027210970941724092174091274902749012740921759037590347438758957283947234273942304239403274093275902375902374092410937290371093719023729103790123'; const ANSWER = BigInt(add(TESTNUMBER, TESTNUMBER)); const BIGINT = BigInt(TESTNUMBER) + BigInt(TESTNUMBER); console.log(ANSWER === BIGINT);
|
解釋一下為什麼一定要加這段:
1 2 3 4
| if (i===0 && hasCarryBit) { result = '1' + result; }
|
假設數字是 95 + 40,算到 9 + 4 = 13
的時候,前面的 1
理應要在下圈加上去,但要注意迴圈只會跑到數字長度,所以如果沒有加上這段答案會是 35
不是 135
。
所以該怎麼辦?沒有怎麼辦,既然迴圈不會跑下一圈,那你只能在最後一圈的時候判斷有沒有進位數,有的話就要自己補 1 到前面,這就是為什麼這段很重要。
大數相乘
相乘的邏輯大致上跟相加差不多,但有幾點要注意:
- 每一層要根據位數在尾巴補上 0(就跟做加法的時候一樣)
- 進位值要用除以 10 的方式來算(範圍變成 0 ~ 9)
- 要用累加器來紀錄上一次的值(算直式的時候要一層一層加總,所以得紀錄上一層的值)
這邊我都寫註解了,仔細看的話應該可以理解:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119
| function multiply(a, b) { let result = ''; let currentDigit = 0; let accumulator = '0'; for (let i=b.length-1; i>=0; i--) { let sum = ''; let carryBit = 0; let hasCarryBit = false; for(let j=a.length-1; j>=0; j--) { let value = Number(b[i]) * Number(a[j]); if (hasCarryBit) { value += carryBit; carryBit = 0; hasCarryBit = false; } if (value >= 10) { hasCarryBit = true; carryBit = Math.floor(value/10); }
sum = value%10 + sum;
if (j===0 && hasCarryBit) { sum = carryBit + sum; } }
for (let i=0; i<currentDigit; i++) { sum = sum + '0'; } result = add(sum, accumulator); accumulator = result; currentDigit++; } return result; }
function add(a, b){ let result = ''; let hasCarryBit = false; const lengthA = a.length; const lengthB = b.length; let totalDigits = undefined;
if (lengthA > lengthB) { totalDigits = lengthA; for (let i=0; i<lengthA - lengthB; i++) { b = '0' + b; } } else if (lengthA < lengthB) { totalDigits = lengthB; for (let i=0; i<lengthB - lengthA; i++) { a = '0' + a; } } else { totalDigits = lengthA; }
for (let i=totalDigits-1; i>=0; i--) { let value = Number(a[i]) + Number(b[i]); if (hasCarryBit) { value += 1; hasCarryBit = false; } if (value >= 10) { hasCarryBit = true; }
result = value%10 + result;
if (i===0 && hasCarryBit) { result = '1' + result; } } return result; }
const TESTNUMBER = '124902814902890825902840917490127902791247902479027210970941724092174091274902749012740921759037590347438758957283947234273942304239403274093275902375902374092410937290371093719023729103790123'; const ANSWER = BigInt(multiply(TESTNUMBER, TESTNUMBER)); const BIGINT = BigInt(TESTNUMBER) * BigInt(TESTNUMBER); console.log(ANSWER === BIGINT);
|