きっかけ
テストケースで1郎2郎3郎...みたいなのを作ってたんですが、
名前にアラビア数字というのもいかがなものかと思いまして、変換してやるようにしました。
int2kanint
python3.7.5
で動いてます
# -*- coding: utf-8 -*-
import csv
def int2kanint(num) :
suji = ["","一","二","三","四","五","六","七","八","九"]
kugiri = ["","十","百","千"]
tani = ["","万","億","兆","京","垓","𥝱","穣","溝","澗","正","載","極","恒河沙","阿僧祇","那由他","不可思議","無量大数"]
num = list(map(int,list(str(num))))
kansuji = []
for k, v in zip(range(len(num)), reversed(num)) :
keta = []
keta.append(suji[v if v>2 else 0 if k%4 else v])
keta.append(kugiri[k%4 if v>0 else 0])
keta.append((tani[0 if k%4 else int(k/4) if any(list(reversed(num))[k:(k+4 if len(num)>=(k+4) else len(num))]) else 0]))
kansuji.append("".join(keta))
kansuji = "".join(reversed(kansuji))
return kansuji if kansuji else "零"
以下に説明を連ねていきます。
変数
説明が怪しい日本語になってますが、何となく察していただけると幸いです。
変数名 | 意味 | 型 |
---|---|---|
suji | 一から九までの漢数字です | string[] |
kugiri | 十,百,千といった区切りです | string[] |
tani | 四桁区切りの単位です | string[] |
num | 変換前のアラビア数字です | int → int[] |
keta | 四桁区切り毎の漢数字です | string[] |
kansuji | 変換後の漢数字です | string[] → string |
変換方法
suji
,kugiri
,tani
でそれぞれ分けて、最後に結合する形をとっています。
私が習った漢数字のルールを基に、それぞれ変換を行っていきます。
地域差、業界差あるとは思いますがそこは一旦置いておきましょう。
suji
それぞれの桁の値を表す一から九の漢数字には以下のルールがあります。
- 値が0でない限り、
零
は省略する- 例:4203 → 四千二百三
-
〇
とする場合がありますが今回は含めません
-
十
,百
,千
の前にくる一
は省略する- 例:11111 → 一万千百十一
よって
値が2以下
かつ一の位ではない
のであれば省略、それ以外は出力
と考えれば良いでしょう。
そのため、suji
を出す為の処理は以下のようになります。
suji[0 if v<2 and k%4 else v]
suji
はインデックスと漢数字が連動してるリストです。
上記の条件を満たしていれば空文字を、それ以外の場合は漢数字を返す形になります。
(関数でもないのに返すというのも変ですが)
kugiri
漢数字では四桁毎に数を区切りますが、その中で十,百,千とさらに桁を区切ります。
その際には以下のようなルールがあります。
- 桁に該当する値がなければ、省略する
- 例:2035070 → 二百三万五千七十
これだけですね。
kugiri[k%4 if v>0 else 0]
内容もシンプルです。
tani
漢数字では四桁毎に数を区切りる際には以下のルールがあります。
- 四桁の中身がなければ、単位を表す漢数字は省略する
- 例:123400005678 → 千二百三十四億五千六百七十八
ここだけ見ればシンプルなんですが、私のアルゴリズムではかなりややこしくなってしまいました。
というのも、与えられた値を漢数字に変換する際に、以下のようなfor文で各桁を参照しています。
for k, v in zip(range(len(num)), reversed(num)) :
k
にはインデックス、v
には各桁の値が入るわけですが、reversed(num)
としてるように一桁目から見てるわけです。
処理としては 四桁毎に単位を出力する と考えるのですが、四桁の中身がない場合、単位を省略しなければなりません。
そうすると必然的に四桁先を見ればいいだけなんですが、値が四桁もない場合を考慮しなければならないわけです。
解決策としては以下のとおりです。
tani[0 if k%4 or not any(list(reversed(num))[k:(k+4 if len(num)>=(k+4) else len(num))]) else int(k/4)]
値が十,百,千の桁である
または四桁の中身が1つもない
のであれば省略、それ以外は出力という形に落ち着きました。
any
で四桁分の値を参照しに行くのですが、四桁先がリストの末尾を超えた場合、末尾まで参照する、といった感じです。
終わりに
競技プログラミングとも言えなくもない、アルゴリズムを考える課題は大好きなので、ついつい熱中して作ってしまいました。
数字to漢数字はいくらでもやりようがあると思うのですが、リストを使ったやり方は今回初めて思いついたので、とても楽しかったです。
高速化・簡易化できる書き方がありましたら、ぜひともご教授ください!