背景
競プロのコンテスト用に必要になったので、作りました。
また N 進 → 10 進変換としても使えると思います。
なお負の入力値は想定していません。ご了承ください。
できること
数値(0〜9)のみで表現された N 進数の値を 10 進数に変換する。(N ≦ 10)
使用例
cout << conv_ary("10110011", 2); //出力値:179
$\scriptsize 1\times2^7+0\times2^6+1\times2^5+1\times2^4+0\times2^3+0\times2^2+1\times2^1+1\times2^0 = 179$
cout << conv_ary("0123456", 7); //出力値:22875
$\scriptsize 0\times7^6+1\times7^5+2\times7^4+3\times7^3+4\times7^2+5\times7^1+6\times7^0 = 22875$
cout << conv_ary("256", 1024); //出力値:2102278
$\scriptsize 2\times1024^2+5\times1024^1+6\times1024^0 = 2102278$
cout << conv_ary("12345678", 8); //出力値:-1
8 進数では 0〜7 のみが使用できるため、8 は不正入力となり -1 が return されます。
cout << conv_ary("0ff0", 16); //出力値:-1
入力文字列に数値以外の文字(f)が含まれるため、不正入力となり -1 がリターンされます。
ソースコード
/* 関数名 conv_ary(string IN, unsigned long long ary)
説明 stringで数字の文字列を受け取り、ary進数と見なしたときの値を10進数(long long)で返す
使用ライブラリ string
使用関数 pow_ll, ctoi
*/
long long conv_ary(string IN, long long ary){
// cout << __func__ << ", " << __LINE__ << ": IN= " << IN << ", ary=" << ary << endl; // debug用
/* エラーチェック */
short max=0;
for(int i=0; i<IN.length(); i++){
// cout << __func__ << ", " << __LINE__ << ": IN[" << i << "]= " << IN[i] << endl; // debug用
/* 入力文字列に0〜9以外の文字が含まれていた場合はエラーとして -1 を return する */
if(isdigit(IN[i])==false) return -1;
/* 入力文字列に指定の進数では使えない文字が含まれていた場合、エラーとして -1 を return する
例えば IN = "124", ary = 3 のとき、4 は 3 進数で扱えないためエラーとなる */
max = ctoi(IN[i]);
if(max>=ary) return -1;
}
/* エラーチェックここまで */
long long ret=0;
for(int i=0; i<IN.length(); i++){
ret += (long long) ctoi(IN[IN.length()-1-i]) * pow_ll(ary , i);
}
// cout << __func__ << ", " << __LINE__ << ": return=" << ret << endl; //debug用
return ret;
}
解説
関数のメイン処理
long long conv_ary(string IN, long long array)- 文字列 (IN) と「何進数と見なすか」の情報 (ary) を受け取ります。
- IN の中に含まれる文字は 0〜9 の数値である必要があります。
- ary は long long 型の範囲内で対応しています。つまり 9,223,372,036,854,775,807 進数も可能。
- return はとりあえず long long 型としていますが、必要に応じて変更してください。
- エラーチェック
- 必要ない場合は削除するかコメントアウト、またはディレクティブ
#ifdefなどで外してください。 - 何をしているかはコメントに書いてあります。
- ary としてありえない数字(1 進数、0 進数、または負の値)を受け取った場合もここでエラーとなります。
for(int i=0; i<IN.length(); i++){ ... }- IN に含まれる全文字について、1つずつ参照していくためのループです。
各桁について 10 進変換し、1 桁ずつ結果を足し合わせていく方法を取りました。
ret += (long long) ctoi(IN[IN.length()-1-i]) * pow_ll(ary , i);-
ctoi(IN[IN.length()-1-i])
入力文字列 IN の右から i 番目の文字を取り出し、関数ctoi(char)を用いて数値に変換しています。 -
pow_ll(ary , i)
ary の i 乗を計算しています。
cmath ライブラリの pow 関数は double 型なので、より大きい値を計算できるように long long 型のべき乗計算関数を用意し使用しています。 -
ret += ...
各桁の 10 進変換した値を足し合わせていく処理です。
サブルーチンその1: ctoi(const char)
こちらの記事のコードを使用しています。
char が表す数字を int へ変換したい @EqualL2
https://qiita.com/EqualL2/items/b4683db7ab4e90545bb2
サブルーチンその2: pow_ll(long long, long long)
/* 関数名 pow_ll(long long x, long long n)
説明 x^n を計算する。long long 対応
使用ライブラリ なし
*/
long long pow_ll(long long x, long long n){
long long ret = x;
// cout << __func__ << ", " << __LINE__ << ": x= " << x << ", n=" << n << endl; // debug用
if(n==0) return 1;
for(long long i=1; i<n; i++){
ret *= x;
}
// cout << __func__ << ", " << __LINE__ << ": return=" << ret << endl; // debug用
return ret;
}
とりあえず long long にしていますが、必要に応じてlong なり int に変更してください。
今回のコードに関して言えば、n は int で受け取るべきでした。反省。
あとがき(課題とか)
今回は 10 進数で書かれた数値(文字列) を N 進数に読み替えたらどうなるか? というコンセプトで作りました。
その場で雑に作ったものなので、処理高速化の余地はあると思います。
完全な N 進 → M 進変換とか作っておきたいですね。
→できました。https://qiita.com/Shiro-san/items/9376c8898a0882482a6a