今更ドドスコ
こちらが話題になっていた2022年の夏、コロナに罹ってダウンしていたところ完全に乗り遅れてしまったので今更ながらやってみました。
リプで配列を使った例はよく見かけたので今回はPythonのビット演算で実装してみることにします。
やってみた
コードはこんな感じ。
import random
buffer = 0
while True:
buffer = buffer << 1 & 4095
if random.getrandbits(1):
buffer |= 1
print("ドド", end="")
else:
print("スコ", end="")
if buffer == 2184:
print("ラブ注入♡")
break
言わずもがなな内容ですが、以下を無限ループで処理してます。
-
buffer
をビットシフトし、12桁で固定するため論理演算を行う -
random.getrandbits(1)
で0
または1
を乱数生成してそのまま条件分岐にかける- 判定結果が真(つまり
1
)だったらbuffer
の最下位ビットに1
をセットして「ドド」を標準出力 - 判定結果が偽(つまり
0
)だったら「スコ」を標準出力(buffer
の最下位ビットは暗黙で0
の扱い)
- 判定結果が真(つまり
-
buffer
が2184
(2進数で「100010001000」=「ドドスコスコスコ×3」)ならば「ラブ注入♡」を標準出力してループを抜ける
実行結果
根気強く何度も実行して短い結果を取得しました。
ドドドドスコドドスコスコスコスコスコドドドドスコスコスコドドスコドドスコスコスコドドドドスコスコスコドドドドドドドドドドスコドドドドスコスコスコスコスコドドスコドドド
ドドドドドスコドドドドスコドドドドスコスコスコスコドドスコドドドドスコスコドドドドドドスコスコスコスコスコスコスコドドドドドドスコスコスコスコスコドドドドドドスコドド
ドドドドドドスコドドドドドドドドスコスコドドドドスコスコドドスコスコスコスコスコスコドドスコドドドドスコドドドドドドドドドドドドスコスコスコドドドドスコスコスコドドス
コドドスコスコドドドドドドスコドドスコスコドドドドスコドドドドドドスコドドスコドドスコドドドドスコドドスコドドスコスコドドドドドドスコドドドドドドスコスコスコスコドド
スコスコスコドドドドスコドドスコドドスコスコドドドドドドスコドドドドスコスコドドドドスコドドスコドドスコスコスコスコドドスコドドスコスコドドスコドドスコドドドドスコス
コスコスコスコドドドドドドドドスコドドドドスコドドスコスコスコスコスコスコスコドドスコドドスコドドスコスコスコスコドドドドスコスコスコドドスコスコスコドドスコスコスコ
ラブ注入♡
気になるところ
ループの1週目の処理(buffer
の値が0
の状態でビットシフトして桁数を制御)が若干汚い感じで気になります。(技量の問題)
あとこれくらいの軽い処理で三項演算子が適用できなかったのも若干ストレスでした。(技量の問題)
せっかくなのでコードを削ってみる
Pythonでこういう可読性低いコード書くのは好みませんが、折角なので削れるところを削ってみました。
import random
b=0
while 1:
b=b<<1&4095
if random.getrandbits(1):
b|=1
print("ドド")
else:
print("スコ")
if b==2184:
print("ラブ注入♡")
break
なおファイルサイズはこんな感じ。
$ wc -c ddsk_short.py
164 ddsk_short.py
仮にimport
文でas
節を使ったとしても文字数が変わらないところが歯痒いですね。
print
時の行末の改行も削っているので実行結果は割愛します。(長くなるので)
感想
もっと前に同じようなお題でズンドコキヨシがありましたが、3セット繰り返す分ズンドコキヨシよりも実装幅が広かったのでプログラマーの個性が出るお題だなと感じました。
配列との実行速度の比較とかしてみると面白そうです。(結果が乱数に左右される手前、同条件での比較ができないですが)
あと「そもそもPythonのビット演算って早いのか?」という疑問が沸いたので今度cpythonのソースコード見て調べてみようと思います。たぶん。やれたらやる。
追記
追記その1:ちゃんと2進数に直した
コードを見返してみたところビット演算なのに数値を10進数で扱ってるのは何か解せなかったので2進数に直しました。
import random
buffer = 0b0
while True:
buffer = buffer << 1 & 0b111111111111
if random.getrandbits(1):
buffer |= 0b1
print("ドド", end="")
else:
print("スコ", end="")
if buffer == 0b100010001000:
print("ラブ注入♡")
break
うん、この方がビット演算してるプログラムっぽくて好きだ。
追記その2:無限ループとbreak
がまだ絞れる
よく考えたら無限ループ使わない方が短く書けることに気づきました。
import random
b=0
while b!=2184:
b=b<<1&4095
if random.getrandbits(1):
b|=1
print("ドド")
else:
print("スコ")
print("ラブ注入♡")
これで20バイト近く削減です。
$ wc -c ddsk_short.py
147 ddsk_short.py
追記その3:バイト数は一旦忘れて行数にコミットしてみる
自分の技量的にバイト数削るのは頭打ちな気がしたので別の切り口で遊んでみます。
import random
buffer=0b0
while buffer!=0b100010001000:
bit = random.getrandbits(1)
buffer = buffer << 1 & 0b111111111111 | bit
print("ドド" if bit else "スコ", end="")
print("ラブ注入♡")
print
を三項演算子使って書いたらすっきりしました。
追記その4:そもそもちゃんと元ツイの通りに組めてなかった件
【問題】配列{"ドド","スコ"}からランダムに要素を標準出力し続け、『その並びが「ドドスコスコスコ」を3回繰り返したもの』に一致したときに「ラブ注入♡」と標準出力して終了するプログラムを作成せよ(配点:5点)
やっちまった。
配列から取得するというのが完全に抜け落ちていたので修正したのがこちら。
import random
ddsk = ["ドド", "スコ"]
buffer=0b0
while buffer!=0b011101110111:
bit = random.getrandbits(1)
print(ddsk[0 if not bit else 1], end="")
buffer = buffer << 1 & 0b111111111111 | bit
print("ラブ注入♡")
「まず標準出力したうえで並びを検知する」というお題の流れに沿って、バッファの更新をprint
の後ろに動かしました。(ここに来てなぜかちょっとだけ可読性を意識)
追記その5:追記その4がバグってたので修正
while buffer!=0b011101110111:
は先頭が0
のため実質2進11桁の判定条件となり、11ループ目の時点で「スコスコスコドドスコスコスコドドスコスコスコドド」が成立するとループを抜けてしまいます。
コメントで指摘くださった @shiracamus さん、ありがとうございました!
こちら先頭のビットを1
で指定するよう条件のビット列を反転し、print
時の配列要素の指定で帳尻を合わせました。
そしてコメントで教えていただいたコードを参考に、buffer
の最下位ビット取得を論理積で取得する形に変更しておきます。
import random
buffer=0b0
while buffer!=0b100010001000:
buffer = buffer << 1 & 0b111111111111 | random.getrandbits(1)
print(["ドド", "スコ"][~buffer & 0b1], end="")
print("ラブ注入♡")