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
エラー一覧00F8 ↓ · ⇧F8 ↑
入力(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 %= qif 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);