※過去記事はこちら。AlteryxユーザーのためのAdvent of Codeの始め方、1日目、2日目、3日目、4日目、5日目、6日目、7日目、8日目、9日目、10日目、11日目、12日目、13日目、14日目、15日目、16日目、17日目、18日目、19日目。

第弐拾話「Pulse Propagation」
タイトルは「パルスの伝搬」。パルスというか、バルスと言いたい。いや、何回か言いかけました。
未解決問題です。めちゃくちゃ複雑なロジック問題です。
一旦解説はスキップします。
前回無事に部品を見つけ、機械は修理できました。この機械を起動するのに適切な信号を送らないといけないようです。
ここにいくつかのモジュールがあります。
- フリップフロップ(Flip-flop)モジュール(接頭辞が%)
オンかオフの状態を取ります。初期値はオフ。
Highパルス受信:無視
Lowパルス受信:オン・オフが入れ替わる。オフの場合はオンになり、Highパルスを送信。オンの場合はオフになりLowパルスを送信。
- 結合(Conjection)モジュール(接頭辞が&)
接続した入力モジュールから受信した最新のパルスタイプを記憶します。デフォルトはLowパルス。
パルスを受信すると、メモリを更新しパルスを記憶。すべての入力がHighパルスを記憶している場合は、Lowパルスを送信。それ以外はHighパルスを送信。
- ブロードキャストモジュール(名前はbroadcaster)
パルスを受信すると、同じパルスをすべての接続しているモジュールに送信。
- ボタンモジュール
最初に押すボタンです。ボタンを押すと、ブロードキャストモジュールにLowパルスを送信します。ボタンを押すと、すべての信号が流れきるまでボタンは押せません。
- アウトプット
出力を受け取るだけのモジュールです。
そして、回路の接続図(パルズインプット)があります。
broadcaster -> a, b, c
%a -> b
%b -> c
%c -> inv
&inv -> a
エルフは1000回ボタンを押します。これにより、送信されたパルスの数をカウントしますが、Lowパルスの数とHighパルスの数をかけあわせたものがパズルの答えです。
Part2は、最後にくっついているモジュール「rx」に一つのlowが送信されると機械がオンになるようです。このために押すボタンの回数を求めます(パズルの答え)。
・ネ
・
・タ
・
・バ
・
・レ
Part1,2を解いてみる
解決後に記載しますが、とにかくロジックがややこしいです・・・。
入力を可視化してみました。いわゆるグラフで描くことができます(もう少し見やすくならないかなぁ、、、)

Part1はひたすらロジック通り組めば良い、、、のですが、作ってると混乱してきてどないしたらええの?って何度もなりました。結局、データの持ち方を工夫することで少しはわかりやすくなりました。つまり、Alteryxの反復マクロは繰り返すことのできるデータストリームが一つしか無いのがすべての原因です。これを回避するには、若干冗長にはなりますが、無理やりくっつけます。
つまり、以下のようにしていました。データとして必要になるのは、入力から出力へのパルス信号、各デバイスのステータス、結合モジュールのメモリです。
パルス信号:

デバイスステータス:※といいつつ、実態としてはFlipflopのステータスしか管理していません。

結合モジュールのメモリ:

これを最終的に反復出力に渡すときに無理やりユニオンします。

反復入力で受け取ってからは、FieldTypeでフィルタをそれぞれかけてからセレクトツールで不要なフィールドを落として使います。それほどデータ量が大きくないため、メモリ的にはそれほど問題にならないです。あまり美しくはないですが、理解しやすくするためにはこの方法が一番良かったです。
これでなんとかロジックを組むことができた感じです。
1000回ボタンを押すための反復マクロ(外側)。

中のマクロです。ツールボックスで囲っている部分は、上からBroadcaster、Flipflop、Conjectionのそれぞれのモジュールの処理部分です。

- Broadcasterは何もしません。Orderを新たに作り直していますが、その他のステータスやメモリはさわりません。
- FlipFlopは、ステータスと受信信号に応じて出す信号を変えています。なお、PulseがHighのものはフィルタで取り除いています(これをしないとバグってました・・・)。ここでOrderを作り、ステータスを変更しています。
- Conjectionモジュールはややこしいです。Conjectionに信号が来た時にConjectionモジュールのメモリの書き換えとOrderを発行しています。ただし、これ自体はステータスは何も変えません。
もう一つ、デバッグの際にPulseがダブって出力されていたのでダブりを省いていたのですが、rxに対して出力しているPulseは省いてはいけません。これが実は最終のカウントに必要になります(カウントのときだけカウントすればいいだけです)。
ワークフロー全景です。

Part2については、このrxをどうにかしないといけないのですが、いわゆるパターンを見つける問題です。rxに接続されているモジュール(ここでは「tj」です)は結合モジュールなので、全ての接続先からhighが来ていれば、lowを出力します。これに結合しているのは、「vt」「sk」「xc」「kk」の4つです。この動きのパターンを観察します。

それぞれ定期的にhighを出すので、このLCMを取ればボタンを押す回数がわかります(今までのAoCの問題でも似たようなことはやりましたよね・・・)。まぁ、答えを見ると、たぶん確実にエルフの指は疲労で折れていることでしょう。
ちなみに、ワークフローとしては、Part1のマクロの内部にフィルタでtjへの信号の部分を抜き出すようにしただけです(For Partの部分です)。

マクロ内部の全景です。

ボタンを5000回押すようにした反復マクロです。

こちらも右下のP Outputのみ付け加えています。
ワークフロー全景です。

まとめ
- 今回の最後の大ボスですね・・・。今年一番理解しにくい問題の一つです。実際、最もAlteryxのリーダーボードで解けている人が少ない問題です。まともにボタンを一回押すだけでも大変です。ボタンを1000回押せるようになっても、rxのトリックに気づかないと厳しいです(まともにデバッグしてもこれはキツイ気がします)。Reddit様々。
- とにかくAlteryxと相性の悪い問題だとしか思えません。データ構造、ロジック、これを解くのはかなり至難の業です。
- Private Leaderboardでは、Part1は10位、Part2は7位となりました。最速の人でもPart1解くのに三時間かかっています。
コメント