こんにちは。エムスリーエンジニアリンググループの藤原です。
今回の記事ではパズルのようなプログラミングクイズを出題してみます。
自分で考えたい方は問題部分から記事を読み進めずに考えてみてください。問題の後にヒント、答えと続いています。
問題
では、問題です。
![]() |
巳年(Python) '25年 なクイズということで作成してみました。 (元日あたりに作ったので [1:-1] 部分も実は"かかって"います!)
難読クイズとは少し違った趣向のクイズで、エンジニアリングとしては何の役にも立たないかもしれないですが、たまにはこういうのも面白いのではないかと思います。
ヒント
解答
ここから解答です。
想定解のコードはこちら。
sorted(oct(ord("*")))[1:-1]
そして、「 _ 」部分をつなげてできる言葉は「 doctor 」でした!
考え方
解答に至るための考え方としては、Pythonの組み込み関数を「型」や「文字数」で絞って考えるのが一番論理的な解き方かと思われます。
以下では考え方の一例を示します。
問題に示されている長さ1の文字列から始まる状態の遷移を次の4つに分けて推理を進めることとしましょう。
- 3文字の関数①を適用して状態①に
- 3文字の関数②を適用して状態②に
- 6文字の関数③を適用して状態③に
- スライス
[1:-1]を適用して['2', '5']に
まず、状態③をスライスすることで ['2', '5'] が得られることから、状態③は長さ4の List であることがわかります。
また、関数③は6文字の組み込み関数です。
候補は以下の通り6つ。
>>> import builtins >>> [name for name in dir(builtins) if callable(getattr(builtins, name)) and len(name) == 6] ['divmod', 'filter', 'format', 'locals', 'object', 'sorted']
これらのうち List を返すのは sorted のみのため、関数③は sorted に決まり、状態②は長さ4の Iterable[str] であることもわかります。
ここで3文字の組み込み関数も列挙しておきましょう。
>>> [name for name in dir(builtins) if callable(getattr(builtins, name)) and len(name) == 3] ['abs', 'all', 'any', 'bin', 'chr', 'dir', 'hex', 'int', 'len', 'map', 'max', 'min', 'oct', 'ord', 'pow', 'set', 'str', 'sum', 'zip']
関数②の候補は、長さ4の Iterble[str] を返し得るものなので bin, chr, hex, oct, set, str です。
また、関数①の候補は、長さ1の文字列を引数に取り得るものなので all, any, dir, int, len, ord, set, str です。
ここからは関数①と関数②の候補を組み合わせて考えていきます。
関数①の候補が返す型は bool, List[str], int, Set[str], str のいずれかですが、関数②の候補いずれかを適用して '2' と '5' が要素に含まれる長さ4の Iterable[str] を得るためには int を返すものしか条件を満たせません。
int を返す関数①の候補は3つありますが、長さ1の文字列を引数とした場合に int と len が返す値は高々一桁の数値となり、これらも関数②を適用して '2' と '5' が要素に含まれる長さ4の Iterable[str] を得るという条件を満たせないため、関数①は ord であることがわかります。
関数②の決定には、問題の「 _ 」部分を繋ぐと言葉になるという前提を使うことにしましょう。候補の bin, chr, hex, oct のうち、d***or で言葉ができるもを探すと oct の doctor が見つかります。関数②は oct で良さそうです。
最後に、出発点となっている長さ1の文字列を探します。
ord と oct を適用して "0o25" または "0o52" になるものが条件を満たしますが、長さ1の文字列となるのは "0o52" の場合で、その文字列は伏字の "*" そのままであることがわかります。
>>> chr(int("25", 8))
'\x15'
>>> chr(int("52", 8))
'*'
全ての謎が解けました。 "*" から始まる状態の遷移を順番に追ってみましょう。
- 3文字の関数①を適用
ord('*')->42 - 3文字の関数②を適用
oct(42)->'0o52' - 6文字の関数③を適用
sorted('0o52')->['0', '2', '5', 'o'] - スライスを適用
['0', '2', '5', 'o'][1:-1]->['2', '5']
解説は以上です。
ここでは論理的な考え方の例を示してみましたが、もしかするとひらめきで答えに辿り着いてしまった人もいるかもしれません。
別解
実はこの問題には別解があります。
社内Slackでも出題してみたところ、なんと3パターンの別解が発見されました。
「 _ 」部分を繋いでも言葉にならないものではありますが、演算結果が ['2', '5'] に合致するものたちです。
別解1. oct を str に
sorted(str(ord("┰")))[1:-1]
oct を str にしても型的には問題なく、演算結果が条件を満たす値を探すと ord("┰") = 9520 が見つかるということのようです。
別解2. oct を hex に
sorted(hex(ord("%")))[1:-1]
別解1と同様、 oct を hex にした場合に条件を満たす ord("%") = 37 (37は16進数で25)が見つかります。
別解3. ( を文字列に含ませる
sorted("25("+(("a")))[1:-1]
コード中の ( を文字列に含めてしまうパターンも発見されました。このパターンだと 25a の3文字は位置の入れ替え可能という自由度もあります。
We are hiring !!
いかがだったでしょうか。ここまで読んでいただいたあなたは、問題を解く喜びを知っている方でしょう。
エムスリーでは医療業界の複雑な問題に共に挑戦するエンジニアを募集しています。
下記のリンクからご応募お待ちしています。
