語法書 / AA 競程語法書 下冊 / 第十六單元 / 整數轉成進制字串

16.4 整數轉成進制字串

反方向:手上有一個整數,要印出它的 K 進制長相

這次從個位下手。「滿 K 進位」代表個位就是「湊不滿一個 K 的零頭」——也就是 x % K2.6 的取餘數);而 x / K 是整數除法,恰好把個位丟掉、留下其餘所有位。十進制直覺一秒懂:3072 % 10 是個位 23072 / 10307。所以反覆「取個位、去個位」,就能把每一位依序剝下來。拿 26 轉二進制走一遍:

回合 x 取出 x % 2 剩下 x / 2
1 26 0 13
2 13 1 6
3 6 0 3
4 3 1 1
5 1 1 0

x 歸零,結束。取出的順序是 01011——由低位到高位,但人類寫數字是高位在前:答案 (11010)_2 得把取出順序整個顛倒才對。

把取出的位轉回字元是 8.2 加法方向的活:'0' + d10 以上換字母,'A' + (d - 10)

char digitToChar(int d) {
    if (d < 10) return '0' + d;
    return 'A' + (d - 10);    // 10 -> 'A'、11 -> 'B'……
}

「顛倒」則交給一個新朋友——<algorithm> 函式庫的 reverse

reverse(ans.begin(), ans.end());   // 把整段內容前後顛倒

它跟 11.4sort 是同鄉,吃一對「範圍」參數(10.5 的 begin/end),把範圍內的東西整個前後翻面,string 與 vector 都能用。不想用它,倒著印也完全可以:

for (int i = (int)ans.size() - 1; i >= 0; i--) cout << ans[i];   // 倒著印

方向一:由低位至高位(推薦)

把上面的材料組起來,就是本節的主做法——輸入進制 K 與十進制整數 x,輸出 x 的 K 進制長相:

#include <iostream>
#include <string>
#include <algorithm>
using namespace std;

// 數值 -> 進制位字元:0~9 用 8.2 的加法,10 以上換字母
char digitToChar(int d) {
    if (d < 10) return '0' + d;
    return 'A' + (d - 10);
}

int main() {
    int K;
    long long x;
    cin >> K >> x;                     // 把十進制的 x 轉成 K 進制輸出

    if (x == 0) {                      // 特判:x 是 0 時迴圈一次都不會跑
        cout << 0 << '\n';
        return 0;
    }

    string ans;
    while (x > 0) {
        ans += digitToChar(x % K);     // 取出個位,接到字串尾端
        x /= K;                        // 去掉個位
    }
    reverse(ans.begin(), ans.end());   // 低位先取出來:整串顛倒回正
    cout << ans << '\n';
    return 0;
}

執行結果(輸入 163072):

C00

換輸入 23072 會得到 110000000000——跟 16.2 開頭寫的一致。

方向二:由高位至低位

跟上一節一樣,這個方向也有「從高位開始」的版本:先找出最高位的位值 now,再由高位往低位一位一位「數」——x / now 是這一位的數字、x %= now 留下零頭給更低的位:

#include <iostream>
#include <string>
using namespace std;

char digitToChar(int d) {
    if (d < 10) return '0' + d;
    return 'A' + (d - 10);
}

int main() {
    int K;
    long long x;
    cin >> K >> x;

    if (x == 0) {                        // 一樣要特判 0
        cout << 0 << '\n';
        return 0;
    }

    long long now = 1;                   // now = 最高位的位值
    while (now <= x / K) now *= K;       // 想寫 now * K <= x,改比 x / K 防止乘法溢位(2.9)
    string ans;
    while (now > 0) {
        ans += digitToChar(x / now);     // 這一位的數字:x 裡裝了幾個 now
        x %= now;                        // 拿掉這一位,剩下的留給更低位
        now /= K;                        // 位值降一級
    }
    cout << ans << '\n';
    return 0;
}

輸出順序天生就是高位在前,不用反轉;代價是得先用一個迴圈找 now,而且找的過程要小心溢位——註解裡把 now * K <= x 改寫成 now <= x / K,就是為了讓比較式裡永遠不出現可能爆範圍的乘法。兩版都會用最好,日常推薦方向一:短、直覺、坑少。

動手試試看:解掉進制轉換——給你 A 進制的字串,要求輸出 B 進制。完整路線:先用 16.3 把字串轉成 long long,再用本節把它印成 B 進制。注意 2 \le A, B \le 36(字母會用到 ZcharToDigitdigitToChar 的字母分支本來就管到 35),且數值上看 10^{18}——累積變數務必 long long。