青の部隊 505小隊 ULZ

ローマ数字 - 例1

最終更新:

匿名ユーザー

- view
だれでも歓迎! 編集
作ってみました。ニ時間近くかかった力作。境界値チェックがないのは手抜き。
#include <stdio.h>

void dec2roman(unsigned int val, char *roman_digit)
{
        int i;
        int v;

        if(val/10)
                dec2roman(val/10, roman_digit+2);
        if(val % 10 % 5 == 4)
                printf("%c", *roman_digit);
        v = (val % 10 + 1) / 5;
        if(v)
                printf("%c", *(roman_digit+v));
        for(i = val % 10 % 5; i % 4 != 0; i--)
        {
                printf("%c", *roman_digit);
        }
}

int main(int argc, char** argv)
{
        int val;

        for(val = 1; val < 4000; val++)
        {
                printf("%d = ", val);
                dec2roman( (unsigned int)val, "IVXLCDM");
                printf("\n");
        }

        return 0;
}

以下、関数本体であるdec2romanの説明。
void dec2roman(unsigned int val, char *roman_digit)
valは変換する整数値、roman_digitはローマ数字を表す文字列。1,5,10,50...と、小さいものから順に入っているという想定。mainからの呼び出しで書いてあるように、"IVXLCDM"が入っているという想定です。

        if(val/10)
                dec2roman(val/10, roman_digit+2);
再帰呼び出しです。大きな位から表示しなきゃなんないので、再帰呼び出しにしました。後でもう一度説明します。ここから先は、valに0~9が与えられ、roman_digitに"IVX"が与えられたものと考えて読んで下さい。0なら何も表示しない、1:i, 2:ii, 3:iii, 4:iv, 5:v, 6:vi, 7:vii, 8:viii, 9:ixを表示するルーチンです。

        if(val % 10 % 5 == 4)
                printf("%c", *roman_digit);
ローマ数字の場合、4はIV, 9はIXと表現します。これらのIを表示する処理です。0~9のうち、5で割った余りが4であれば4か9なので、そのときはIを出力します。

        v = (val % 10 + 1) / 5;
        if(v)
                printf("%c", *(roman_digit+v));
次に、vまたはxを表示します。0~3であれば、vやxを表示する必要はなし、4~8ならvを表示、9ならxを表示します。変則的です。1足して考えると、1~4:表示なし、5~9:v表示, 10:x表示ということになる点に着目します。すると、5で割ったときの商が、0:表示なし、1:v表示、2:x表示ということになります。ここで、roman_dijitはIVXのIを指しているので、0なら表示せず、1または2ならroman_dijitにその値を足したアドレスの文字を表示してやります。

        for(i = val % 10 % 5; i % 4 != 0; i--)
        {
                printf("%c", *roman_digit);
        }
あとは、iを必要な数だけ表示するだけです。iを繰り返して表示するので、for文を使います。初期値は5で割った数なので、0~4になってます。繰り返しのたびにiを1ずつ減らしながら'i'を表示して行けばいいので、素直に書けば、for(i = val % 10 % 5; i != 0; i--)となります。ただし、4のときは表示しちゃまずいので、i !=0 || i!= 4が条件式になるはずです。これは、iの範囲が0~4の場合は、i % 4 != 0と等価ということになるで、上記のようになります。

以上のようにして、0~9を3個のローマ数字で表現するルーチンができました。ここで最初に戻ります。
        if(val/10)
                dec2roman(val/10, roman_digit+2);
与えられた数を10で割ると、対象とする位をひとつ上の位にすることができます。もし、上の位が存在しないのであれば、10で割った値が0になるはずなので、ifでそれをチェックしてます。逆にいえば、0でなければ、上の位が存在するということなので、先にその位を表示してやんないとダメです。そこで再帰呼び出しです。このとき、roman_digitの頭ふたつをとったものを渡してやります(roman_digit+2)。IVXLCDMから、頭ふたつをとると、XLCDMになりますね。もし、valに91が入っていたとすると、10割った値は9になります。9を、Xが1、Lが5、Cが10を表すローマ数字として表示する処理を先に実行してやるわけです。
以上の手順で、ローマ数字を正しく表示できます。3999より大きな数字は正しく表示できませんけど。表示可能かどうかチェックする機能をつけるなら、以下のコードをdec2romanの先頭に追加して、1が帰って来たら表示をせずにさらに上位に1を返すようにすればいいはず。NULLチェックもしないとダメかな。
        if(*roman_digit == '\0' ||
           val > 3 && *(roman_digit+1) == '\0' ||
           val > 8 && *(roman_digit+2) == '\0' )
                return 1;

境界値チェックを加えたコードと、その出力結果はここ

タグ:

+ タグ編集
  • タグ:
ウィキ募集バナー