FizzBuzz問題(独自解法) [Z80]
今回は Twitter(X) のタイムラインで見かけた Fizz Buzz 問題の解法について書いてみたいと思います。ウィキペディアにも書いてあるように Fizz Buzz 問題とは1から100までを言い合う遊びで3の倍数の場合は数字の代わりに Fizz と言い、5の倍数の場合は Buzz、両者の倍数の場合は Fizz Buzz と言うゲームです。
FizzBuzz 問題を解くプログラムを作ること自体は容易なのですが、面白くするために高速化という観点から剰余算を使わないという制約を設けます。また、FizzBuzz の間にはスペースを入れず、それぞれの数字や 単語の後にはスペースを1個入れることとします。
素数を求める時によく使われる「エラトステネスの篩(ふるい)」でのフラグ設定のように3周期のカウンタと5周期のカウンタを持つことでそれぞれの倍数であることを判断できます。
最初に考えたC言語のソースは次のようなものでした。
ネット上のコンパイラ環境サービスで実行結果を確かめることもできます(便利な世の中になったものですね)。
上述のように3と5のカウンタを持っているので全体は15周期の繰り返しになり、4ビットでカウントできます。ならば15周期のカウンタだけを使って処理できないものでしょうか?
ここで15以内の3と5の倍数について考えてみます。両者の倍数である15を除外して列挙すると
更に面白いことに相互を XOR してみると他のパターンに推移するので、これらの3の倍数と5の倍数は2進数の観点で見ると密接な関係があるかのように思えてきます。
この特徴を利用して Z80 のアセンブリ言語で FizzBuzz 問題を解くプログラムを作ってみることにしました。上記は独自の解析なので恐らくこの手法による FizzBuzz プログラムは世界初だと思いますw
1のビットの数が2個だった場合、上述のように3か5の倍数ということになるのですが、どちらに属するかの判断方法は簡単そうに見えて中々思いつきませんでした。
最も単純なのは
CP 5
JR Z,Buzz
CP 10
JR Z,Buzz
のような8バイトものコードになってしまいます。漸く思い付いたのが次の方法です。
SUB 5
OR A
JP PE,Buzz
如何でしょう。引き算後のビット1の総数(パリティ)で判断できました(ほかに良い方法があればコメント下さい)。
★追記 2023/09/23 {
もっと判り易く、簡潔な判定方法を思い付きました^^
AND 5
JP PE,Buzz
}
★追記 2023/09/23 {
ビット数の判定をループ処理方式
L1: LD BC,4 * 256 + 0
XOR A ; clear bit counter
LD L,D
L2: RRC L
ADC A,C
DJNZ L2
CP 2
JR NZ,DspNum
から下記のループ処理無しの方式に改善してみました。
LD A,D
OR A
JP PO,DspNum
CP 0FH
JR Z,Both
}
できたソースを下記に示します。CP/M用に作成していますがDOSコールしているのは1文字出力サービスのみですので他の環境にも移植は容易だと思います。メインループ処理の簡潔さを堪能してください。
上のプログラムの出力結果は次のようになります。
1 2 Fizz 4 Buzz Fizz 7 8 Fizz Buzz 11 Fizz 13 14 FizzBuzz 16 17 Fizz 19 Buzz Fizz 22 23 Fizz Buzz 26 Fizz 28 29 FizzBuzz 31 32 Fizz 34 Buzz Fizz 37 38 Fizz Buzz 41 Fizz 43 44 FizzBuzz 46 47 Fizz 49 Buzz Fizz 52 53 Fizz Buzz 56 Fizz 58 59 FizzBuzz 61 62 Fizz 64 Buzz Fizz 67 68 Fizz Buzz 71 Fizz 73 74 FizzBuzz 76 77 Fizz 79 Buzz Fizz 82 83 Fizz Buzz 86 Fizz 88 89 FizzBuzz 91 92 Fizz 94 Buzz Fizz 97 98 Fizz Buzz
上記のソースよりは少し前のバージョンですが、ネット上のサービスとして公開されている MSX のエミュ環境である MSXPenでも実行できました(重ねて便利な世の中に・・)。
FizzBuzz 問題を解くプログラムを作ること自体は容易なのですが、面白くするために高速化という観点から剰余算を使わないという制約を設けます。また、FizzBuzz の間にはスペースを入れず、それぞれの数字や 単語の後にはスペースを1個入れることとします。
素数を求める時によく使われる「エラトステネスの篩(ふるい)」でのフラグ設定のように3周期のカウンタと5周期のカウンタを持つことでそれぞれの倍数であることを判断できます。
最初に考えたC言語のソースは次のようなものでした。
FizzBuzz 問題の解答プログラム(C言語) |
|
ネット上のコンパイラ環境サービスで実行結果を確かめることもできます(便利な世の中になったものですね)。
上述のように3と5のカウンタを持っているので全体は15周期の繰り返しになり、4ビットでカウントできます。ならば15周期のカウンタだけを使って処理できないものでしょうか?
ここで15以内の3と5の倍数について考えてみます。両者の倍数である15を除外して列挙すると
- 3の倍数
0011,0110,1100,1001
- 5の倍数
0101,1010
更に面白いことに相互を XOR してみると他のパターンに推移するので、これらの3の倍数と5の倍数は2進数の観点で見ると密接な関係があるかのように思えてきます。
この特徴を利用して Z80 のアセンブリ言語で FizzBuzz 問題を解くプログラムを作ってみることにしました。上記は独自の解析なので恐らくこの手法による FizzBuzz プログラムは世界初だと思いますw
1のビットの数が2個だった場合、上述のように3か5の倍数ということになるのですが、どちらに属するかの判断方法は簡単そうに見えて中々思いつきませんでした。
最も単純なのは
CP 5
JR Z,Buzz
CP 10
JR Z,Buzz
のような8バイトものコードになってしまいます。漸く思い付いたのが次の方法です。
SUB 5
OR A
JP PE,Buzz
如何でしょう。引き算後のビット1の総数(パリティ)で判断できました(ほかに良い方法があればコメント下さい)。
★追記 2023/09/23 {
もっと判り易く、簡潔な判定方法を思い付きました^^
AND 5
JP PE,Buzz
}
★追記 2023/09/23 {
ビット数の判定をループ処理方式
L1: LD BC,4 * 256 + 0
XOR A ; clear bit counter
LD L,D
L2: RRC L
ADC A,C
DJNZ L2
CP 2
JR NZ,DspNum
から下記のループ処理無しの方式に改善してみました。
LD A,D
OR A
JP PO,DspNum
CP 0FH
JR Z,Both
}
できたソースを下記に示します。CP/M用に作成していますがDOSコールしているのは1文字出力サービスのみですので他の環境にも移植は容易だと思います。メインループ処理の簡潔さを堪能してください。
FizzBuzz 問題の解答プログラム(Z80アセンブリ) |
|
上のプログラムの出力結果は次のようになります。
1 2 Fizz 4 Buzz Fizz 7 8 Fizz Buzz 11 Fizz 13 14 FizzBuzz 16 17 Fizz 19 Buzz Fizz 22 23 Fizz Buzz 26 Fizz 28 29 FizzBuzz 31 32 Fizz 34 Buzz Fizz 37 38 Fizz Buzz 41 Fizz 43 44 FizzBuzz 46 47 Fizz 49 Buzz Fizz 52 53 Fizz Buzz 56 Fizz 58 59 FizzBuzz 61 62 Fizz 64 Buzz Fizz 67 68 Fizz Buzz 71 Fizz 73 74 FizzBuzz 76 77 Fizz 79 Buzz Fizz 82 83 Fizz Buzz 86 Fizz 88 89 FizzBuzz 91 92 Fizz 94 Buzz Fizz 97 98 Fizz Buzz
上記のソースよりは少し前のバージョンですが、ネット上のサービスとして公開されている MSX のエミュ環境である MSXPenでも実行できました(重ねて便利な世の中に・・)。
コメント 0