也許你我素未謀面,但很可能相見恨晚,我是前端胖頭魚
前言
金三銀四大潮如期到來,在這個裁員
、降薪
、倒閉
無處不在的寒冷季節裡,我們們都沒有太多選擇的餘地。唯有努力提高自身能力,才能在寒風凜冽中站住腳跟,抵禦隨時到來的風險。
最近有個朋友在面位元組時遇到了一道有意思的題,按照他的話說,差點就因為這個題寄了,不過好在他在最後幾分鐘,充分調動體內的洪荒之力,解出了答案,朝位元組又邁進了一步。
要不要一起來做做這道題!!!
1.# 62進位制的大數相加
// 實現兩個62進位制數的大數加法
// 輸入:兩個 62 進位制數,String 型別,僅考慮整數
// 輸出:兩數之和,String 型別
// 62 進位制數:按照 1-9,a-z,A-Z 遞增
function sum (a, b) {}
不知道你們看到這道題時是什麼感受!!!
我當時的想法是:“好像只聽過2進位制、8進位制、16進位制、32進位制,62進位制是什麼鬼? ”
終於在看到這段資訊後恍然大悟:62 進位制數:按照 1-9,a-z,A-Z 遞增,
我掰著手加腳指頭
數了數...
- 0 ~ 9 是10個數
- a ~ z 是26個字母
- A ~ Z 是26個字母
加起來就是62個數,剛好湊齊62進位制。
1.1 10進位制大數相加(整數)
突然看到求62進位制的兩數之和,我內心多少是有點懵逼的,懵逼的點在哪?
就是字母咋相加啊?比如 1a(10進位制的72)
+ 2 = 1c(10進位制的74)
不過作為接受過9年義務教育的社會主義接班人
,一年級我們們就學會了加法(也就是所謂的10進位制加法)
。
回顧一下小學知識: 19 + 23?
1. 個位數相加:9 + 3 = 12。寫下 2,進位 1。
19
+ 23
-----
2
2. 十位數相加並加上進位:1 + 2 + 1(進位)= 4。
19
+ 23
-----
42
非常簡單是不是?我們們嘗試一下用程式來計算兩個10進位制數的加法。
別擔心,朋友,這段程式碼雖然看起來長了點,但它完全模擬的小學加法,很容易看懂。
敲黑板:10進位制的大數相加也是面試的熱點
const addBigNumbers = (a, b) => {
// 從尾部往前計算
let i = a.length - 1
let j = b.length - 1
// 進位標誌,1或者0
let carry = 0
let result = ''
while (i >= 0 || j >= 0 || carry) {
const n1 = +(a[ i ] || 0)
const n2 = +(b[ j ] || 0)
// 當前位相加(包含進位部分)
let sum = n1 + n2 + carry
// 當前位相加如果>=10,說明要往前進1,否則要重置進位標誌
if (sum >= 10) {
sum -= 10
carry = 1
} else {
carry = 0
}
// 遞減往前算
i--
j--
// 當前位 + 前面低位的結果
result = sum + '' + result
}
return result
}
console.log(addBigNumbers('19', '23')) // 42
console.log(addBigNumbers('100', '1024')) // 1124
1.2# 62進位制與10進位制之間如何轉換?
其實我們們知道如何實現10進位制大數相加
之後,62進位制的大數相加
也就完成了一大半,只要解決這2個問題,面試官就該給你過了。
- 62進位制轉化為10進位制
- 10進位制轉化為62進位制
想想只要能將62進位制轉化為10進位制進行相加,再把結果轉化為62進位制,問題不就迎刃而解了嗎?
1a (10進位制的72) + 2 = 1c (10進位制的74)
1.3# 62進位制轉化為10進位制
讓我們一起來回顧一下計算機基礎知識:62進位制轉化為10進位制的規則
設 62 進位制數為 d[n]d[n-1]...d[1]d[0],其中 d[i] 表示第 i 位的數字(從右向左,最低位為0)。
則該數的十進位制值為:
d[0] * 62^0 + d[1] * 62^1 + ... + d[n] * 62^n
舉個例子
1a = 1 * 62^1 + 10 * 62^0 // 62進位制中的a代表10進位制中的10,b代表的是11以此類推
= 1 * 62 + 10 * 1
= 62 + 10
= 72
有了這個規律用程式碼實現就很方便了.
const base62ToDecimal = (str) => {
const base = 62
const digits = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
let len = str.length
let sum = 0
let n =0 // 最後一位是0,倒數第二位是1...
while (len--) {
let digit = digits.indexOf(str[ len ]) // 獲取當前位的10進位制數,例如a表示10進位制的10,b表示11,c表示12...
sum += digit * Math.pow(base, n)
n++
}
return sum
}
// 示例
console.log(base62ToDecimal('1a')) // 72
console.log(base62ToDecimal('1c')) // 74
1.4# 10進位制轉化為62進位制
同樣 10進位制轉化為62進位制也有規律可言:
- 用輸入的十進位制數除以 62,得到商和餘數。
- 將餘數對應的 62 進位制字元放在結果的最低位。
- 將商作為新的輸入,重複步驟 1 和 2,直到商為 0。
- 將得到的所有餘數按照逆序排列,就是最終的 62 進製表示。
舉個例子: 將10進位制的72轉化為64進位制是啥?
第一步,將 72 不斷除以 62,得到的餘數就是 62 進位制數的低位數字。依次計算如下:
1. 72 ÷ 62 = 1 ... 10 (餘數為 10,10對應的62進位制字元為 'a')
2. 1 ÷ 62 = 0 ... 1 (餘數為 1,1對應的62進位制字元為 '1')
第二步,將每次得到的餘數按照逆序排列,就是最終的62進製表示:
72(10進位制)= '1a'(62進位制)
根據規律聰明的你也能很快用程式來實現10進位制到62進位制的轉換
const decimalToBase62 = (decimal) => {
const base = 62
const digits = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
let result = []
while (decimal > 0) {
const remainder = decimal % base // 取餘
result.push(digits[ remainder ]) // 將餘數真正對應62進位制字元存入陣列
decimal = Math.floor(decimal / base) // 計算商,用於下次計算
}
return result.reverse().join('') || '0' // 反轉結果
}
console.log(decimalToBase62('72')) // 1a
console.log(decimalToBase62('74')) // 1c
1.5 解題啦!!!
囉嗦了這麼多,大家有沒有發現解決62進位制大數相加所需的知識點實在是太基礎了
- 會小學加法
- 會進位制轉換
最後請丟出這份程式碼,亮瞎面試官的眼吧!!!
// 62進位制轉10進位制
const base62ToDecimal = (str) => {
const base = 62
const digits = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
let len = str.length
let sum = 0
let n =0 // 最後一位是0,倒數第二位是1...
while (len--) {
let digit = digits.indexOf(str[ len ]) // 獲取當前位的10進位制數,例如a表示10進位制的10,b表示11,c表示12...
sum += digit * Math.pow(base, n)
n++
}
return sum
}
// 10進位制轉62進位制
const decimalToBase62 = (decimal) => {
const base = 62
const digits = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
let result = []
while (decimal > 0) {
const remainder = decimal % base // 取餘
result.push(digits[ remainder ]) // 將餘數真正對應62進位制字元存入陣列
decimal = Math.floor(decimal / base) // 計算商,用於下次計算
}
return result.reverse().join('') || '0' // 反轉結果
}
const addBigBase62Numbers = (a, b) => {
// 轉化為10進位制的字串
a = '' + base62ToDecimal(a)
b = '' + base62ToDecimal(b)
// 從尾部往前計算
let i = a.length - 1
let j = b.length - 1
// 進位標誌,1或者0
let carry = 0
let result = ''
while (i >= 0 || j >= 0 || carry) {
const n1 = +(a[ i ] || 0)
const n2 = +(b[ j ] || 0)
// 當前位相加(包含進位部分)
let sum = n1 + n2 + carry
// 當前位相加如果>=10,說明要往前進1,否則要重置進位標誌
if (sum >= 10) {
sum -= 10
carry = 1
} else {
carry = 0
}
// 遞減往前算
i--
j--
// 當前位 + 前面的
result = sum + '' + result
}
return decimalToBase62(result) // 將10進位制再轉化為62進位制
}
console.log(addBigBase62Numbers('1a', '2')) // 1c
console.log(addBigBase62Numbers('Z', '1')) // 10
2.# 另一種解法
我們們廢了老大勁,寫了一大坨的程式碼才實現這個功能,有沒有其他更簡便一點的解法呢?
其實62進位制的數學性質和10進位制類似,只是基數(62)不同而已,在10進位制中是逢十進一
,62進位制中就是逢62進一
了。所以在我們知道10進位制的大數相加如何解之後,僅僅需要做少量的修改就可以變成62進位制的大數相加。
搞起!!!
const addBigBase62Numbers = (a, b) => {
const digits = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
let i = a.length - 1
let j = b.length - 1
let carry = 0
let result = ''
while (i >= 0 || j >= 0 || carry) {
const n1 = digits.indexOf(a[i] || 0) // 獲取當前位的10進製表示,如a表示10,b表示11
const n2 = digits.indexOf(b[j] || 0) // 獲取當前位的10進製表示,如a表示10,b表示11
let sum = n1 + n2 + carry
// 62進1
if (sum >= 62) {
sum -= 62
carry = 1
} else {
carry = 0
}
// digits[sum] 將當前位轉換回62進位制
result = digits[sum] + result
i--
j--
}
return result
}
console.log(addBigBase62Numbers('1a', '2')) // 1c
console.log(addBigBase62Numbers('Z', '1')) // 10
好舒暢、程式碼一下子少了一大坨,又可以和麵試官吹牛逼了...
3.# 舉一反三
文中我們只處理了整數的62進位制大數相加,聰明你可以嘗試一下這幾種場景
- 帶小數的62進位制大數相加?
- n進位制的大數相加?
原理都是類似的,期待你在評論區寫下你的答案,一起交流。