rustで進数変換(基数変換)する関数を作る

rust

進数変換っていうのを小学生か中学生のころ習ったなーと思ってて、進数変換をするプログラムをRustで書いてみました。

今回は、10進数以下しか対応してません。16進数とか39進数とか128進数とかには対応してません。

進数変換方法は?

まずは、進数変換の方法を思い出しましょう。

10進数の13を2進数に変換するには、筆算でこんな計算をして

余りをしたから読むなんて方法をやりましたね。

結果、13(10)を2進数にすると1101(2)です。

「元となる基数で割ってその余りを拾い上げる。そして、商がゼロになるまでそれを繰り返す」

っていう方法です。

アルゴリズムを形にする

今の話

「元となる基数で割ってその余りを拾い上げる。そして、商がゼロになるまでそれを繰り返す」

このアルゴリズムを元にプログラムの形に近づけていきましょう。

まずは具体例から

まずは、具体例から考えてみます。

10進数の19を3進数に変換することを考えます。

19を3で割る>商が6、余り1>商6を3で割る>商が2,余り0>商2を3で割る>商0、余り2
商が0になったので、ここで終了

結果余り「1,0,2」これを逆から読むから「201」となる

疑似コードで表現してみる

まずは疑似コードであらわしてみます

元となる数をx、基数をbと。そして、余りを記憶してい置く場所をamariと名付けましょう

元の数x、基数bを受け取る

while xがゼロではない間続ける{
   xをbで割った余りを計算する
   計算結果をamariに記憶する
   xの商を計算してxに代入する(xの更新)
}
amariの中身を返す

こんな感じのプログラムを書けば、10進数xをb進数に変換できるはずです。

実際のコードにしてみる

疑似コードを実際コードにしていきましょう

10進数をb進数に変換するい関数をshinsu()

引数は、変換したい数x、変換したい基数(進数)をbとします。返値は、amariという数値を変換したベクタを返しましょう

fn shinsu(mut x: i64,b: i64)->Vec<i64>{
    let mut amari: Vec<i64> = Vec::new();
    while x != 0{
        amari.push(x % b);
        x /= b;
    }
    return amari;
}

これを実行すると、返り値がベクタ型なので、shinsu(19,3)とやると[2,0,1]と返ってきます。

けどこれでは、[2,0,1]という値であって、数ではないですよね。
なのでちょっと手を加えましょう

関数の改善

先ほど疑似コードをもう一度見ます。

元の数x、基数bを受け取る
while xがゼロではない間続ける{
   xをbで割った余りを計算する
   計算結果をamariに記憶する
   xの商を計算してxに代入する(xの更新)
}
amariの中身を返す

amariをさらに数値にして返せば、ちゃんと数になりそうですね。

では、amariを数に直しましょう

数列を数にする

amariはi64のベクタ型でした。中身は先ほどの例だと[2,0,1]という数列のようなもの

これを数に変換してきます。

10進数の場合右から1の位(10の0乗)、10の位(10の1乗)100の位(10の2乗)とあって100とか230とかっていう数になりますね。

b進数でも同じように考えればいいわけです。

例えば今回の3進数なら1の位(3の0乗)、10の位(3の1乗)100の位(3の2乗)とします。それを足し合わせていくと複数桁の数になります。

今回の例[2,0,1]は左から1の位10の位100の位になってます。なので、数に直すと102になります。

位の値の数列を複数桁の数に変換する方法

では、これをソースにしていきましょう。

返す数をnとします。それぞれの桁の値は、amariのベクタ型で作ってましたね。そして、ベクタの0番目が1桁目、2番目が2桁目。。。となっていました。それを使って変換してきます。

変換の疑似コードを書いてみる

まずは疑似コードを書いてみましょう

変数nを宣言して0で初期化する
for i in amariの数の長さだけ繰り返す{
    nに10のi乗の数にamariのi番目の数を掛けて足していく
}

こんな感じで、変換できそうですね。

実際のソースを書いてみる

では、疑似コードをソースにしていきましょう。

ベクタamariの長さはlen()で取得できます。

累乗は、pow()で求めることができます。10i64としたのは明示的にi64だよとしています。そうしないとエラーでますよ。pow()の引数はu32で渡さないといけないので、iもu32に変換してます。

let mut n:i64 = 0;
for i in 0..amari.len(){
    n += 10i64.pow(i as u32)*amari(i)
}

こんな感じのソースコードになります。

関数全体を作り直す

では、関数全体を作り直しましょう

fn shinsu(mut x: i64,b: i64)->i64{
    let mut amari: Vec<i64> = Vec::new();
    while x != 0{
        amari.push(x % b);
        x /= b;
    }
    let mut n:i64 = 0;
    for i in 0..amari.len(){
         n += 10i64.pow(i as u32)*amari(i)
    }
    return n;
}

これで、[2,0,1]となっていたのが102という数を返すようになりました。

まとめ

今回は10以下の基数変換をするプログラムを書いてみました。

11以上の基数になるとabcd….と10以上の数を文字であらわさなきゃいけなくなって、もう少し工夫が必要になります。

さらに少数や負の数なんかを扱うようにするにはかなり改善が必要になります。

そして、今回は10進数をb進数に変換することはできてもb進数を10進数にしたりn進数にしたりというようなことはできません。

基数変換の大まかな考え方はこんな感じなので、これを改良して工夫すれば、今回実装していないものもできるようになります。やってみたい方は是非チャレンジしてみてください。

コメント

タイトルとURLをコピーしました