3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Python & ビット演算でドドスコしてみた

Last updated at Posted at 2022-09-29

今更ドドスコ

こちらが話題になっていた2022年の夏、コロナに罹ってダウンしていたところ完全に乗り遅れてしまったので今更ながらやってみました。
リプで配列を使った例はよく見かけたので今回はPythonのビット演算で実装してみることにします。

やってみた

コードはこんな感じ。

ddsk.py
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の扱い)
  • buffer2184(2進数で「100010001000」=「ドドスコスコスコ×3」)ならば「ラブ注入♡」を標準出力してループを抜ける

実行結果

根気強く何度も実行して短い結果を取得しました。

ドドドドスコドドスコスコスコスコスコドドドドスコスコスコドドスコドドスコスコスコドドドドスコスコスコドドドドドドドドドドスコドドドドスコスコスコスコスコドドスコドドド
ドドドドドスコドドドドスコドドドドスコスコスコスコドドスコドドドドスコスコドドドドドドスコスコスコスコスコスコスコドドドドドドスコスコスコスコスコドドドドドドスコドド
ドドドドドドスコドドドドドドドドスコスコドドドドスコスコドドスコスコスコスコスコスコドドスコドドドドスコドドドドドドドドドドドドスコスコスコドドドドスコスコスコドドス
コドドスコスコドドドドドドスコドドスコスコドドドドスコドドドドドドスコドドスコドドスコドドドドスコドドスコドドスコスコドドドドドドスコドドドドドドスコスコスコスコドド
スコスコスコドドドドスコドドスコドドスコスコドドドドドドスコドドドドスコスコドドドドスコドドスコドドスコスコスコスコドドスコドドスコスコドドスコドドスコドドドドスコス
コスコスコスコドドドドドドドドスコドドドドスコドドスコスコスコスコスコスコスコドドスコドドスコドドスコスコスコスコドドドドスコスコスコドドスコスコスコドドスコスコスコ
ラブ注入♡

気になるところ

ループの1週目の処理(bufferの値が0の状態でビットシフトして桁数を制御)が若干汚い感じで気になります。(技量の問題)

あとこれくらいの軽い処理で三項演算子が適用できなかったのも若干ストレスでした。(技量の問題)

せっかくなのでコードを削ってみる

Pythonでこういう可読性低いコード書くのは好みませんが、折角なので削れるところを削ってみました。

ddsk_short.py
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進数に直しました。

ddsk.py
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がまだ絞れる

よく考えたら無限ループ使わない方が短く書けることに気づきました。

ddsk_short.py
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:バイト数は一旦忘れて行数にコミットしてみる

自分の技量的にバイト数削るのは頭打ちな気がしたので別の切り口で遊んでみます。

ddsk.py
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点)

やっちまった。

配列から取得するというのが完全に抜け落ちていたので修正したのがこちら。

ddsk.py
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の最下位ビット取得を論理積で取得する形に変更しておきます。

ddsk.py
import random
buffer=0b0
while buffer!=0b100010001000:
    buffer = buffer << 1 & 0b111111111111 | random.getrandbits(1)
    print(["ドド", "スコ"][~buffer & 0b1], end="")
print("ラブ注入♡")
3
2
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?