{ }codelper
進数変換剰余64ビット整数P0403

循環小数を別の進法に

a 進法で書いた数を一つ b 進法に変えて出力せよ。
数は 0 以上で、小数点以下が限りなく繰り返されることもある。
繰り返す部分は一度だけ書いて括弧で囲む:10 進法の 0.1666… は 0.1(6)、0.142857142857… は 0.(142857) である。
桁の値が 10 以上なら英大文字を使う:A = 10、B = 11、…、Z = 35。
入力の数は I、I.F、I.F(R) の三つの形のどれかである。
I は整数部分で一桁以上あり、0 一文字でなければ 0 で始まらない。
F は小数点のすぐ後ろの繰り返さない部分で空のこともあり、R は繰り返す部分で一桁以上ある。
小数点があれば F と R が両方とも空になることはない。
入力の小数部分は最も短く整理されているとは限らない:0.50、0.1(0)、0.12(32)、0.(9) のような表記も出る。
出力は同じ値を b 進法で書いたただ一つの表記である。
整数部分の先頭に余計な 0 を付けない(整数部分が 0 なら 0 一文字)。
値が整数なら小数点を書かない。
小数点以下が有限なら括弧なしで書き、最後の桁は 0 ではない。
小数点以下が限りなく続くなら、繰り返す部分ができるだけ前から始まるようにし、その中で最も短いものを括弧で囲む。
(b−1) が限りなく続く表記は使わない:10 進法の 0.(9) は 1 と同じ値なので 1 と書く。
一行目に a と b が空白一つ区切りで、二行目に a 進法で書いた数が空白なしで与えられる。

入力
2 ≤ a, b ≤ 36。
整数部分 I の値は 10 進法で 10⁹ 未満。
小数点以下に書かれた桁数(括弧の中を含む)を d とすると a^d ≤ 10,000。
各桁は 0〜9 または A〜Z で、値は a より小さい。
出力
b 進法で書いた数を一行(大文字、上の規則に合うただ一つの表記)
例 1 · stdin
10 2
0.1
stdout
0.0(0011)
例 2 · stdin
10 6
12.4(9)
stdout
20.3
main.py
入力(stdin)

実行すると、これらの行が順に input()・標準入力に渡されます(実行中に入力を求めません)。

$ 準備完了

コードを入力して解くにはパソコンで開いてください。モバイルでは問題と解答を見られます。

ほかの言語では?言語別の解答を見る · 8 言語の解答

分数に直してから筆算の割り算をします。
a 進法の I.F(R) で F が k 桁、R が m 桁なら値は p / q で、q = a^k × (a^m − 1)、p = I × q + F × (a^m − 1) + R です(R がなければ q = a^k、p = I × q + F)。
p を q で割った商を b 進法で書き、余り r については r × b を q で割った商が次の桁、その余りが新しい r です。
r が 0 になれば終わり、前に見た r がまた出たら、その r で作った桁から繰り返します。
r は q より小さいので、q 回以内にどちらかが起きます。
こう割れば、最も前から始まる最も短い循環節が自然に得られ、(b−1) が限りなく続く表記も出ません。
どの r をいつ見たかは、Python は辞書、ほかは q 個の配列に記録します(q ≤ 10,000。
Craykon は 新リスト(q, -1))。
桁の読み方は言語ごとに違います:Python int(t, a)、Java Long.parseLong(t, a)、JavaScript・TypeScript parseInt(t, a) は 2〜36 進法をそのまま読めますが、空の F を渡すと Python・Java は例外、JavaScript は NaN なので、空文字列は 0 として別に扱います。
C# の Convert.ToInt64(t, base) は 2・8・10・16 進法しか受けないので Digits.IndexOf(ch) で、C は strchr(DIGITS, c) - DIGITS、C++ は DIGITS.find(ch)、Craykon は DIGITS.位置検索(ch) で桁の値を求めます。
罠その一:整数部分と小数部分を別々に変えると、0.(9) のように小数部分がちょうど 1 の数で壊れます。
9 / 9 を割ると最初の桁が 10 になり、10 進法で 0.A が出ます。
p 全体を q で割れば 1 が整数部分に繰り上がります。
罠その二:p は 10⁹ × 10⁴ = 10¹³ 近くまで大きくなり、Java・C#・C・C++ の int ではあふれます — long/long long を使います(Python の整数に上限はなく、JavaScript・Craykon は 2^53 まで正確なので大丈夫です)。
罠その三:r の位置は、その r で桁を作る前に記録します。
桁を入れた後に記録すると、10 進法の 0.1(6) が 0.16() になります。
罠その四:r が 0 になったらすぐ止めます。
0 をもう一度割ると末尾に 0 が付き、10 進法の 0.5 が 2 進法で 0.10 になります。
罠その五:整数部分が 0 だと while (v > 0) が一度も回らず、0.1 が .1 になります(C・C++・C# の解答は do-while で防いでいます)。
罠その六:JavaScript・TypeScript の v.toString(b) と Java の Long.toString(v, b) は小文字を出すので toUpperCase() が必要です。
罠その七:C・C++・Java・C# で桁の値を c - '0' で求めると、'A' が 10 ではなく 17 になります。

Pythonmain.py · 49 行
DIGITS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" a, b = map(int, input().split())s = input().strip() # 정수 부분 / 끝나는 부분 F / 되풀이 부분 R 로 나눈다int_str, _, frac = s.partition(".")fixed, _, rep = frac.partition("(")rep = rep.rstrip(")") # 값을 분수 p / q 로 만든다: q = a^k × (a^m − 1)  (R가 없으면 a^k)k, m = len(fixed), len(rep)f_val = int(fixed, a) if fixed else 0if m == 0:    q = a ** k    num = f_valelse:    r_val = int(rep, a)    q = a ** k * (a ** m - 1)    num = f_val * (a ** m - 1) + r_valp = int(int_str, a) * q + num  # 소수 부분이 1이 될 수도 있으니(0.(9)) 한꺼번에 나눈다 whole, r = divmod(p, q) # 정수 부분을 b진법으로head = []while True:    head.append(DIGITS[whole % b])    whole //= b    if whole == 0:        breakout = "".join(reversed(head)) # 나머지를 b배 하며 긴 나눗셈. 같은 나머지가 다시 나오면 거기서부터 되풀이된다seen = {}  # 나머지 → 그 나머지로 만든 자리의 위치digits = []while r != 0 and r not in seen:    seen[r] = len(digits)    r *= b    digits.append(DIGITS[r // q])    r %= q if digits:    if r == 0:        out += "." + "".join(digits)    else:        start = seen[r]        out += "." + "".join(digits[:start]) + "(" + "".join(digits[start:]) + ")"print(out)
JavaScriptmain.js · 43 行
const DIGITS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";const [line1, line2] = require("fs").readFileSync(0, "utf8").trim().split(/\r?\n/);const [a, b] = line1.split(" ").map(Number);const s = line2.trim(); // a진법 글자들을 값으로(빈 문자열이면 0). 값은 10^13쯤이라 2^53 안이다const read = (t) => (t === "" ? 0 : parseInt(t, a)); // 정수 부분 / 끝나는 부분 / 되풀이 부분으로 나눈다const m = s.match(/^([0-9A-Z]+)(?:\.([0-9A-Z]*)(?:\(([0-9A-Z]+)\))?)?$/);const intStr = m[1], fixed = m[2] ?? "", rep = m[3] ?? ""; // 값을 분수 p / q 로: q = a^k × (a^m − 1), 되풀이 부분이 없으면 a^klet q = a ** fixed.length;let num = read(fixed);if (rep !== "") {  const am1 = a ** rep.length - 1;  q *= am1;  num = num * am1 + read(rep);}const p = read(intStr) * q + num; // 소수 부분이 1일 수도 있으니(0.(9)) 한꺼번에 나눈다 let out = Math.floor(p / q).toString(b).toUpperCase(); // toString은 소문자를 낸다let r = p % q; // 긴 나눗셈: 같은 나머지가 다시 나오면 거기서부터 되풀이된다const seen = new Array(q).fill(-1); // 나머지 → 그 나머지로 만든 자리의 위치const digits = [];while (r !== 0 && seen[r] === -1) {  seen[r] = digits.length;  r *= b;  digits.push(DIGITS[Math.floor(r / q)]);  r %= q;} if (digits.length > 0) {  if (r === 0) out += "." + digits.join("");  else {    const start = seen[r];    out += "." + digits.slice(0, start).join("") + "(" + digits.slice(start).join("") + ")";  }}console.log(out);
TypeScriptmain.ts · 46 行
import { readFileSync } from "node:fs";const DIGITS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";const [line1, line2] = readFileSync(0, "utf8").trim().split(/\r?\n/);const [a, b]: number[] = line1.split(" ").map(Number);const s: string = line2.trim(); // a진법 글자들을 값으로(빈 문자열이면 0). 값은 10^13쯤이라 2^53 안이다const read = (t: string): number => (t === "" ? 0 : parseInt(t, a)); // 정수 부분 / 끝나는 부분 / 되풀이 부분으로 나눈다const m = s.match(/^([0-9A-Z]+)(?:\.([0-9A-Z]*)(?:\(([0-9A-Z]+)\))?)?$/)!;const intStr: string = m[1];const fixed: string = m[2] ?? "";const rep: string = m[3] ?? ""; // 값을 분수 p / q 로: q = a^k × (a^m − 1), 되풀이 부분이 없으면 a^klet q: number = a ** fixed.length;let num: number = read(fixed);if (rep !== "") {  const am1 = a ** rep.length - 1;  q *= am1;  num = num * am1 + read(rep);}const p: number = read(intStr) * q + num; // 소수 부분이 1일 수도 있으니(0.(9)) 한꺼번에 나눈다 let out: string = Math.floor(p / q).toString(b).toUpperCase(); // toString은 소문자를 낸다let r: number = p % q; // 긴 나눗셈: 같은 나머지가 다시 나오면 거기서부터 되풀이된다const seen: number[] = new Array<number>(q).fill(-1); // 나머지 → 그 나머지로 만든 자리의 위치const digits: string[] = [];while (r !== 0 && seen[r] === -1) {  seen[r] = digits.length;  r *= b;  digits.push(DIGITS[Math.floor(r / q)]);  r %= q;} if (digits.length > 0) {  if (r === 0) out += "." + digits.join("");  else {    const start = seen[r];    out += "." + digits.slice(0, start).join("") + "(" + digits.slice(start).join("") + ")";  }}console.log(out);
JavaMain.java · 71 行
import java.io.BufferedReader;import java.io.InputStreamReader;import java.util.Arrays; public class Main {    static final String DIGITS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";     // a진법 글자들을 값으로(빈 문자열이면 0)    static long read(String t, int a) {        return t.isEmpty() ? 0 : Long.parseLong(t, a);    }     public static void main(String[] args) throws Exception {        BufferedReader in = new BufferedReader(new InputStreamReader(System.in));        String[] ab = in.readLine().trim().split(" ");        int a = Integer.parseInt(ab[0]), b = Integer.parseInt(ab[1]);        String s = in.readLine().trim();         // 정수 부분 / 끝나는 부분 / 되풀이 부분으로 나눈다        String intStr = s, fixed = "", rep = "";        int dot = s.indexOf('.');        if (dot >= 0) {            intStr = s.substring(0, dot);            String frac = s.substring(dot + 1);            int open = frac.indexOf('(');            if (open >= 0) {                fixed = frac.substring(0, open);                rep = frac.substring(open + 1, frac.length() - 1);            } else {                fixed = frac;            }        }         // 값을 분수 p / q 로: q = a^k × (a^m − 1), 되풀이 부분이 없으면 a^k        long q = 1;        for (int i = 0; i < fixed.length(); i++) q *= a;        long num = read(fixed, a);        if (!rep.isEmpty()) {            long am1 = 1;            for (int i = 0; i < rep.length(); i++) am1 *= a;            am1 -= 1;            q *= am1;            num = num * am1 + read(rep, a);        }        long p = read(intStr, a) * q + num; // 10^13까지 커지니 long. 소수 부분이 1일 수도 있어 한꺼번에 나눈다         StringBuilder out = new StringBuilder(Long.toString(p / q, b).toUpperCase()); // toString은 소문자를 낸다        int r = (int) (p % q); // q < 10000이라 나머지는 int에 들어간다         // 긴 나눗셈: 같은 나머지가 다시 나오면 거기서부터 되풀이된다        int[] seen = new int[(int) q]; // 나머지 → 그 나머지로 만든 자리의 위치        Arrays.fill(seen, -1);        StringBuilder digits = new StringBuilder();        while (r != 0 && seen[r] == -1) {            seen[r] = digits.length();            r *= b;            digits.append(DIGITS.charAt((int) (r / q)));            r %= q;        }         if (digits.length() > 0) {            out.append('.');            if (r == 0) out.append(digits);            else {                int start = seen[r];                out.append(digits, 0, start).append('(').append(digits, start, digits.length()).append(')');            }        }        System.out.println(out);    }}
C#Main.cs · 86 行
using System;using System.Text; class Program{    const string Digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";     // a진법 글자들을 값으로(빈 문자열이면 0). Convert.ToInt64(s, base)는 2·8·10·16진법만 받는다    static long Read(string t, int a)    {        long v = 0;        foreach (char ch in t) v = v * a + Digits.IndexOf(ch);        return v;    }     static void Main()    {        string[] ab = Console.ReadLine().Split(' ', StringSplitOptions.RemoveEmptyEntries);        int a = int.Parse(ab[0]), b = int.Parse(ab[1]);        string s = Console.ReadLine().Trim();         // 정수 부분 / 끝나는 부분 / 되풀이 부분으로 나눈다        string intStr = s, fixedPart = "", rep = "";        int dot = s.IndexOf('.');        if (dot >= 0)        {            intStr = s[..dot];            string frac = s[(dot + 1)..];            int open = frac.IndexOf('(');            if (open >= 0)            {                fixedPart = frac[..open];                rep = frac[(open + 1)..^1];            }            else fixedPart = frac;        }         // 값을 분수 p / q 로: q = a^k × (a^m − 1), 되풀이 부분이 없으면 a^k        long q = 1;        for (int i = 0; i < fixedPart.Length; i++) q *= a;        long num = Read(fixedPart, a);        if (rep.Length > 0)        {            long am1 = 1;            for (int i = 0; i < rep.Length; i++) am1 *= a;            am1 -= 1;            q *= am1;            num = num * am1 + Read(rep, a);        }        long p = Read(intStr, a) * q + num; // 10^13까지 커지니 long. 소수 부분이 1일 수도 있어 한꺼번에 나눈다         long whole = p / q;        var head = new StringBuilder();        do        {            head.Insert(0, Digits[(int)(whole % b)]); // 낮은 자리부터 나오니 앞에 끼운다            whole /= b;        } while (whole > 0);         long r = p % q;        // 긴 나눗셈: 같은 나머지가 다시 나오면 거기서부터 되풀이된다        int[] seen = new int[q]; // 나머지 → 그 나머지로 만든 자리의 위치        Array.Fill(seen, -1);        var digits = new StringBuilder();        while (r != 0 && seen[r] == -1)        {            seen[r] = digits.Length;            r *= b;            digits.Append(Digits[(int)(r / q)]);            r %= q;        }         var outSb = new StringBuilder(head.ToString());        if (digits.Length > 0)        {            string d = digits.ToString();            if (r == 0) outSb.Append('.').Append(d);            else            {                int start = seen[r];                outSb.Append('.').Append(d[..start]).Append('(').Append(d[start..]).Append(')');            }        }        Console.WriteLine(outSb);    }}
C++main.cpp · 79 行
#include <iostream>#include <string>#include <vector>#include <algorithm> const std::string DIGITS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; // a진법 글자들을 값으로(빈 문자열이면 0)long long readPart(const std::string &t, int a) {    long long v = 0;    for (char ch : t) v = v * a + (long long)DIGITS.find(ch);    return v;} long long power(int a, std::size_t k) {    long long v = 1;    for (std::size_t i = 0; i < k; i++) v *= a;    return v;} int main() {    int a, b;    std::string s;    std::cin >> a >> b >> s;     // 정수 부분 / 끝나는 부분 / 되풀이 부분으로 나눈다    std::string intStr = s, fixed, rep;    auto dot = s.find('.');    if (dot != std::string::npos) {        intStr = s.substr(0, dot);        std::string frac = s.substr(dot + 1);        auto open = frac.find('(');        if (open != std::string::npos) {            fixed = frac.substr(0, open);            rep = frac.substr(open + 1, frac.size() - open - 2);        } else {            fixed = frac;        }    }     // 값을 분수 p / q 로: q = a^k × (a^m − 1), 되풀이 부분이 없으면 a^k    long long q = power(a, fixed.size());    long long num = readPart(fixed, a);    if (!rep.empty()) {        long long am1 = power(a, rep.size()) - 1;        q *= am1;        num = num * am1 + readPart(rep, a);    }    long long p = readPart(intStr, a) * q + num; // 10^13까지 커지니 long long     long long whole = p / q; // 소수 부분이 1일 수도 있으니(0.(9)) 한꺼번에 나눈다    std::string out;    do {        out += DIGITS[whole % b];        whole /= b;    } while (whole > 0);    std::reverse(out.begin(), out.end());     // 긴 나눗셈: 같은 나머지가 다시 나오면 거기서부터 되풀이된다    std::vector<int> seen(q, -1); // 나머지 → 그 나머지로 만든 자리의 위치    std::string digits;    long long r = p % q;    while (r != 0 && seen[r] == -1) {        seen[r] = (int)digits.size();        r *= b;        digits += DIGITS[r / q];        r %= q;    }     if (!digits.empty()) {        out += '.';        if (r == 0) out += digits;        else {            int start = seen[r];            out += digits.substr(0, start) + "(" + digits.substr(start) + ")";        }    }    std::cout << out << '\n';}
Cmain.c · 76 行
#include <stdio.h>#include <string.h> static const char *DIGITS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; /* s[from..to) 의 a진법 글자들을 값으로(비어 있으면 0) */static long long read_part(const char *s, int from, int to, int a) {    long long v = 0;    for (int i = from; i < to; i++)        v = v * a + (long long)(strchr(DIGITS, s[i]) - DIGITS); /* 'A'는 10: c - '0'이 아니다 */    return v;} static long long power(int a, int k) {    long long v = 1;    while (k-- > 0) v *= a;    return v;} int main(void) {    int a, b;    char s[128];    if (scanf("%d %d %127s", &a, &b, s) != 3) return 0;    int len = (int)strlen(s);     /* 정수 부분 [0, dot) / 끝나는 부분 [dot+1, open) / 되풀이 부분 [open+1, len-1) */    char *pdot = strchr(s, '.');    int dot = pdot ? (int)(pdot - s) : len;    char *popen = strchr(s, '(');    int open = popen ? (int)(popen - s) : len;    int fixed_from = dot < len ? dot + 1 : len;     /* 값을 분수 p / q 로: q = a^k × (a^m − 1), 되풀이 부분이 없으면 a^k */    long long q = power(a, open - fixed_from);    long long num = read_part(s, fixed_from, open, a);    if (open < len) {        long long am1 = power(a, len - 1 - (open + 1)) - 1;        q *= am1;        num = num * am1 + read_part(s, open + 1, len - 1, a);    }    long long p = read_part(s, 0, dot, a) * q + num; /* 10^13까지 커지니 long long */     long long whole = p / q; /* 소수 부분이 1일 수도 있으니(0.(9)) 한꺼번에 나눈다 */    char head[64];    int hl = 0;    do {        head[hl++] = DIGITS[whole % b];        whole /= b;    } while (whole > 0);    for (int i = hl - 1; i >= 0; i--) putchar(head[i]);     /* 긴 나눗셈: 같은 나머지가 다시 나오면 거기서부터 되풀이된다 */    static int seen[10000];  /* 나머지 → 그 나머지로 만든 자리의 위치 */    static char digits[10001];    for (int i = 0; i < q; i++) seen[i] = -1;    int n = 0;    long long r = p % q;    while (r != 0 && seen[r] == -1) {        seen[r] = n;        r *= b;        digits[n++] = DIGITS[r / q];        r %= q;    }    digits[n] = '\0';     if (n > 0) {        putchar('.');        if (r == 0) fputs(digits, stdout);        else {            int start = seen[r];            printf("%.*s(%s)", start, digits, digits + start);        }    }    putchar('\n');    return 0;}
Craykonmain.cray · 99 行
DIGITS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; // a진법 글자들을 앞에서부터 읽어 값으로 만든다(빈 문자열이면 0)읽기(s, a){    v = 0;    それぞれ (ch の中 s)    {        v = v * a + DIGITS.位置検索(ch);    }    返す v;} 거듭(a, k){    v = 1;    繰り返す (i = 0; i < k; i += 1)    {        v = v * a;    }    返す v;} ab = コンソール.数読み();a = ab[0];b = ab[1];s = コンソール.行読み().空白除去(); // 정수 부분 / 끝나는 부분 / 되풀이 부분으로 나눈다intStr = s;fixed = "";rep = "";dot = s.位置検索(".");もし (dot >= 0){    intStr = s.切り出す(0, dot);    frac = s.切り出す(dot + 1);    open = frac.位置検索("(");    もし (open >= 0)    {        fixed = frac.切り出す(0, open);        rep = frac.切り出す(open + 1, frac.長さ - open - 2);    }    でなければ    {        fixed = frac;    }} // 값을 분수 p / q 로: q = a^k × (a^m − 1), 되풀이 부분이 없으면 a^kq = 거듭(a, fixed.長さ);num = 읽기(fixed, a);もし (rep.長さ > 0){    am1 = 거듭(a, rep.長さ) - 1;    q = q * am1;    num = num * am1 + 읽기(rep, a);}p = 읽기(intStr, a) * q + num; // 소수 부분이 1일 수도 있으니(0.(9)) 한꺼번에 나눈다 whole = 数学.商(p, q);r = p % q; // 정수 부분을 b진법으로out = "";間 (真){    out = DIGITS[whole % b] + out;    whole = 数学.商(whole, b);    もし (whole == 0)    {        抜ける;    }} // 긴 나눗셈: 같은 나머지가 다시 나오면 거기서부터 되풀이된다seen = 新リスト(q, -1); // 나머지 → 그 나머지로 만든 자리의 위치digits = [];間 (r != 0 && seen[r] == -1){    seen[r] = digits.個数;    r = r * b;    digits.追加(DIGITS[数学.商(r, q)]);    r = r % q;} もし (digits.個数 > 0){    もし (r == 0)    {        out = out + "." + digits.つなぐ("");    }    でなければ    {        start = seen[r];        out = out + "." + digits.部分リスト(0, start).つなぐ("") + "(" + digits.部分リスト(start, digits.個数 - start).つなぐ("") + ")";    }}コンソール.書く(out);