/ 最近 .rdf 追記 設定 本棚

脳log[2022-02-02~]



2022年02月02日 (水)

最終更新: 2022-02-11T22:02+0900

[Ruby] Ruby が謎に止まる。(解決済み)

先日解いていた「JOIG 2021/2022 本選競技課題」(テストケースのダウンロードができる) の F 問題へのこの提出に関して>20220129p01.05

Hash#shift を意味がある限り繰り返したあとで Hash#clear を呼ぶようにしている(25 行目)。shift は破壊的なメソッドだから shift を十分に呼び出した後の Hash は空になっているはずで、clear を呼ぶ意味はなさそうに思える。それでも呼ぶようにしているのは、これがないとテストケースの in/05-01.txt に限ってスクリプトの応答がなくなって TLE になると思ったから。少なくともローカル Windows 環境では Ruby-2.7 と Ruby-2.5 で止まる。Ruby-1.9 は平気。他のテストケースでは止まらないけど、問題のケースでは必ず止まる。止まるタイミングはバラバラだけど、いずれも Hash への代入で止まっているようだった。

再現スクリプトはこんな感じ(これを見つけたから日記に書いている)。他所で再現するかは、どうでしょうね。

Many = 100 # 10 回では少ない。テキトーに大きく。
Some = 100 # ウチでは 30 回目までは到達しない。
# ウチの Ruby-2.5: ruby 2.5.5p157 (2019-03-15 revision 67260) [x64-mingw32]
# ウチの Ruby-2.7: ruby 2.7.1p83 (2020-03-31 revision a0c7c23c9c) [x64-mingw32]

Some.times{|some|
	warn "#{some} begin"
	hash = {}
	Many.times{
		some.times{|j|
			k = Random.rand 0..Many
			hash[k] = 1 # maybe hang
		}
		0 while hash.shift
	}
	warn "#{some} end"
}
warn :exit

Ruby-2.5 と Ruby-2.7 では "N begin" (N は 20 前後) を表示して止まる。2.5 より 2.7 の方が早く止まるという傾向はあるけど、具体的な回数にはバラツキがある。それはまあ乱数を使ってるからだと思うけど、ハッシュのキーを k に代えて j にしたり、シャッフルした順列にしたりすると止まらなくなるので(正常動作)、乱数は外せない。

こういう Issue「Bug #17779: 特定の順序でHashのkeyを削除した場合に Hash#first が遅くなる - Ruby master - Ruby Issue Tracking System」を思い出したけど、全然関係はなさそう? 全然わからない。

AtCoder のコードテストを使わせてもらったら(目的外失礼)、こういう結果。AtCoder の Ruby も今のバージョンは 2.7。

終了コード9
実行時間10501 ms
メモリ9112 KB

標準エラー出力は 30 begin で途切れている。完走しても 10 秒もかかるはずないから、ハングして実行を打ち切られたのだと思う。コードテストとジャッジサーバーが同じだとして、ジャッジサーバーのスペックはこう>Linux ip-***-***-***-*** 4.15.0-1041-aws #43-Ubuntu SMP Thu Jun 6 13:39:11 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux*

 ruby 3.1.0p0 (2021-12-25 revision fb4df44d16) [x64-mingw-ucrt]

Ruby-3.1 が「ウチの Ruby」に今日加わりました。大体ループの 20 から 30 回目で止まるんだけど、弾みで 2 回だけ完走したりもした。

 テスト(改訂版)

パラメータが2つもあると面倒なので。

改訂するにあたって予想外に止まらなくなったりした(正常動作)のを通して、たぶん直接的な原因もわかった。

# ウチではだいたい 20 から 30 回で "empty?: true" を最後にして止まる。
# ウチの Ruby-2.5: ruby 2.5.5p157 (2019-03-15 revision 67260) [x64-mingw32]
# ウチの Ruby-2.7: ruby 2.7.1p83 (2020-03-31 revision a0c7c23c9c) [x64-mingw32]
# ウチの Ruby-3.1: ruby 3.1.0p0 (2021-12-25 revision fb4df44d16) [x64-mingw-ucrt]

H = {}
100.times{|n|
	while H.size < n
		k = Random.rand 0..1<<30
		H[k] = 1 # たぶんここで止まる。
	end
	warn "size: #{H.size} before shifting."
	H.shift until H.empty?
	raise if H.shift # no exception. だけど Hash が空になった後の shift が後でハングをひき起こす?
	warn "empty?: #{H.empty?}"
}
warn :exit

Hash を空にする方法として 0 while H.shift を選ぶとその後の Hash#[]= でハングするが、Hash を空にする方法として H.shift until H.empty? を選ぶとハングしなかった。条件を揃えていくと、Hash#empty? が作用してハングを回避しているのではなく、Hash が空になったあとの Hash#shift がハングをひき起こしているようだった。ちなみに Hash#clear にハングを回避する効果があるらしいのは、JOIG の問題への提出で確認済み(とはもう書いた)。

 対応していただきました! (Bug #18578)

投稿して次に見たときにはすべてが終わっていた。早いぜ。

こういう仕組みだったようです。

メモ:entries_bound は使用中のビン(DELETEDになったビンを含む)の数に(ほぼ)対応していて、これをみてテーブルをリビルドしている(rebuild_table_if_necessary)。空のハッシュに対する Hash#shift はなぜか entries_bound を 0 にしているので、リビルドすべきタイミングを逃し、ビンがすべて使用中になった状態で空きビンを探そうとするので無限ループに陥っていた(find_table_bin_ind)。

* [[Language Test 202001 - AtCoder|https://atcoder.jp/contests/language-test-202001]]

 [[AtCoder 2019/7 Language Update - Google スプレッドシート|https://docs.google.com/spreadsheets/d/1PmsqufkF3wjKN6g1L0STS80yP4a6u-VdGiEv5uOHe0M/edit]]


2022年02月01日 (火) 最近になって Dropbox から壊れた HTTP レスポンスヘッダ Strict-Transport-Security: max-age=31536000; includeSubDomains, max-age=31536000; includeSubDomains; preload が送られて来るようになって困っている。二重指定とカンマ区切りと。Firefox はコンソールに Strict-Transport-Security: サイトに指定されたヘッダーを正しく解析できませんでした。 とメッセージを出してページを処理してくれないのだけど、他に困っている人はいないのか。


2022年01月31日 (月) [AtCoder] 昨日あった ABC237提出一覧。B 問題がなんかやけに難しいなと思いながら、だけど ABC-B だからってなめてないので謙虚に(それほど)焦らずに解いて提出したら、サンプルから何からすべて WA で、デバッグ出力を消し忘れたかと見直したけどそんなことはなくて、問題を読み直してみたらさっき解いた問題と全然違う! C 問題もさっき解いたのとは違う。D 問題を開いてやっと見覚えのある問題が見つかった。そんな間違いある? 2行もずれてクリックミスしたの? いつも問題ページから提出するのに問題と提出先が食い違うってことある? と不思議に思いながら A>D>B>C>E といつもと違う変則的な順番で解いていた。■質問タブってコンテスト中も終了後も全然見ないんだけど(自分とはほぼ無関係だから)、そういえばコンテスト中にピコンとページ上部に動きがあったなと見てみたら、どうもこういうことが起こっていたらしい。「B問題に誤った問題文が入っておりました。現在修正が完了しております。なお、元のB問題はD問題であるため、そちらを解いた方はD問題に提出してください。」「D問題の回答をB問題に提出してしまった方は、コンテスト終了後5分以内に質問タブからご連絡ください。順次提出を無効化いたします。」 知らないよー。■舞台裏。「社長が1個1個手作業で無効化しているので、Rating更新少し遅れます……ごめんなさい!!!」「コンテスト中1時間半ずっとこれ対応してて、今1/3のところなので、あと3時間くらいで終わると思います……!」■「【ABC237】D問題→B問題への誤提出ですが、先頭の300件の質問から、 「'L'」「'R'」「"L"」「"R"」「76」「82」 の6種の文字列が含まれているかどうかで、誤提出を100%検出可能であることを確認しましたので、機械的に誤提出への対応を行います。当てはまらない方がいたらご連絡下さい。」 Ruby なので 'L' でも 76 でもなくて ?L.ord なのよね。ま、5分(考えようによっては5分50秒)の違いは大差ないやろ。■@2022-01-31 01:12 提出一覧から 2022-01-30 21:25:29 の提出が消えたみたい。ペナルティ5分が消えて嬉しい。


2022年01月30日 (日) Sony Reader が現役なのにも関わらず Reader Store を使わなくなった理由は、支払い方法がクレジットカードに一本化されたからです。PlayStation のディスクモデルを必要としている理由は、CERO Z 指定のゲームを PS Store で買うのにクレジットカードの登録が求められるからです。俺はクレカがインフラだとも唯一の(そして真っ当な?)成人認証手段だとも思わんよ。販売者が代替手段で楽をするから俺は客になれない。


2022年01月29日 (土)

最終更新: 2023-07-22T23:29+0900

[AtCoder] JOIG 2021/2022 本選 過去問/F 問題 タクシー 2 (Taxis 2)

 ステップ1

  • タクシーが赤色の場合,乗車後の所持金が a−1 円になる.
  • タクシーが青色の場合,乗車後の所持金が「a÷2 を整数に切り捨てた値」円になる.

町 1 から出発し,1 円以上の所持金を残した状態で町 Tj​ に到着するために,最初に少なくとも何円の所持金を持っている必要があるか

問題のこの部分は、まあ、逆に考えます。所持金が1の状態でスタートして、所持金が +1 になる、×2 になる、というように。そうすれば初期金額を探索しなくて良い。ただしそれぞれの町をスタート地点に設定して町1への最短経路を繰り返し求めるということは許されていないので、スタート地点は町1のままにしておきたい。

ところが町1を1円でスタートして、+1/×2 しながら各町に着いたときいくらになっているかをダイクストラ法で求めてもサンプルが合わない。合わなかった。

たぶん、-1/÷2 の逆計算は +1/×2 でいいのだけど、((((-1)-1)÷2)-1)÷2 の逆計算は ((((×2)+1)×2)+1)+1 ではないとか、合ってるけどその通りに計算できていないとか、そういう理由。

わからんなりにテキトーに 2×2 の正方行列に 2 とか 1 とか 0 を割り当てて実際に計算してみれば、必要な係数が2つだということと、それをどう変化させればいいのかがわかった。スクリプトの中の x,y = x,x+yx,y = x+x,y というのがタクシーごとの変化。式を見ても意味はわからない。

 ステップ2

逆計算がわかったからダイクストラ法で解けたかというと、小課題の4あたりから答えが稀にあわなかったり時間をオーバーしたりする。した。小課題の3までは問題のグラフが木だから複数の経路の競合がなかったのだな。

これはたぶん、ある町における状態というのが所持金というパラメータ1つで優先順位を付けられないせいだと思う。さっき x と y の式を2つ書いたけど、そして所持金というのが d = x+y で表されるのだけど、ある町からある町へ移動するにあたりどちらのタクシーを使っても所持金の変化は x+x+y == d+x で共通している。だけどその次の移動での増加量が x の値にのみ依存するので、x を増加させることで所持金を変化させたタクシーの方が分が悪い。

 ステップ3

2つのタクシーの運賃を普通の経済感覚で比べてみると、どの場合でも所持金が1円減るだけの赤色のタクシーを利用できるときは利用して損をすることがないことがわかる。たぶん。01BFS みたいなステップを踏めば解けるような気がするなー。

 提出 #28854159 (AC / 1242 Byte / 2410 ms)

頭の中が整理されていないので無駄がまだあると思う(嘘解法の可能性も大いにある)。でも1秒しかなかった JOIG-2021-F「デジタルアート (Digital Art)」(20211210p01) と違って制限時間が4秒もあったので間に合った。嬉しい。

D 問題まではやるだけだったけど、E 問題「エゴイ展 (EGOI Exhibition)」がまだ解けていない。価値の正負と絵の種類で場合分けとクラス分けをして DP をしようとしたんだけど……。


PQ#dn_heap がバグってるね。不等号の向きが逆。これは要するに、PQ には繰り返す幅優先探索の無駄を気休め程度に取り除く意味しかなく、実際にはその効果さえなかったということ。

 提出 #28975251 (AC / 502 Byte / 1158 ms) ← 2410 ms

PQ#dn_heap のバグをとったとしても定数倍が重いせいで、初期パラメータが異なる複数の地点をスタート地点に設定した幅優先探索のようなものを、距離の更新がなくなるまでぐるぐる回した方が速い。優先度付きキューを使うと確かに最短距離を複数回更新するという無駄はないのだけど。

Array をキューにするのと Hash をキューにするのでは Hash の定数倍が重い。キュー内で要素が重複することを気にせず Array につっこんだ方が速かったりする。

そんな(良くない)理由でメインループ内の前半のループのキューは PQ でも(後半のループのように) Hash でもなく Array になっている。

 提出 #28988344 (AC / 600 Byte / 863 ms) ← 1158 ms

キューを2本持つことでプライオリティキューを使わないでもソート済みの状態を保ちながらキューを伸ばせる場合があるというのは、AtCoder について書いた2番目の日記に書いてある>20190916p01.01。今回もこれが使える。

863 ms というのは Ruby に限らない AC の中で1ページ目に入っているのだ(全部で9ページではあるのだけど)。


2022年01月26日 (水) [AtCoder] 精進。第八回 アルゴリズム実技検定-L「K番目の絶対値」 どうしても Nlog^2 の方法しか思いつかなくて TLE が避けられなかった。気がついてみれば単に累積和と二分探索を使うだけの典型も典型だった。自分がこれまで見て来なかったのは、累積和を使って定数時間で数列のある範囲の和を求めるというときに、添字の大小関係は絶対ではないということ。S[j]-S[i] で和を求めるときに、i<j でも j<i でも構わないということ。符号を入れ替えればいいだけ。そしてこの問題に関して言えば、問われているのが絶対値であることで符号の入れ替えさえ不要だった。絶対値が問題を面倒にしているとばかり思っていたぜ。■提出 #28807480 (AC / 232 Byte / 1853 ms)。累積和をソートすることも解を二分探索することも早くからわかっていた。二分探索の中で数列をスキャンする中で j<i となるケースを除外するために BIT なりで対数時間の処理を加えてしまうと Nlog^2 になって困ったなーと考えていた。事前にじっくり準備して BIT に頼らないデータ、それも N×N よりコンパクトなデータが作れないかなーと考えていた。実は j<i となるケースを除外する必要などなかった。累積和の使い方がわかっていない! 精進の道とは自分のアホさを確認する道なのか。


2022年01月17日 (月) [AtCoder] 精進。ABC027-D「ロボット」(黄 diff)。初見ではない。埋めきれなかった古い ABC を埋め直している。入力に関する制約が解法を制約するヒントになっている。一度だけスキャンして答えが出せる。縦のものを横に見られるかどうか。+/- に応じて +x/-x するというのが縦の見かたなら、M に応じて x+1/-1 することが +x/-x する操作にそれぞれ何回寄与するかを考えるのが横からの見かた。提出 #28608177 (AC / 157 Byte / 72 ms)。気がつくと簡単に答えが出て気持ちがいい。■横からの見かたが培われたのはたぶんこのとき>「足し算とはそういうものだ、と言われたら言葉がない。私は足し算がわかりません。」 なんだ、より難しい類題を解説 AC していたから解けただけだった。


2022年01月16日 (日) [AtCoder] 精進。キーエンスプログラミングコンテスト2021-Nov. (AtCoder Beginner Contest 227)-E「Swap」(黄 diff)。「D 問題ダメでした」の回の ABC なので E 問題は読んでいなかった。考えたのはどうやって状態をまとめることができるか。並び順を区別してしまうと、たとえば K,E,Y がそれぞれ 10 個ずつ使われている場合に 30!÷10!÷10!÷10! > 5.5 兆通り(本当?)の文字列を考えないといけなくなって間に合わない。並びではなくて、それぞれの文字をいくつ使ったかと、それだけ使うのに何回の操作を要したかの組み合わせを考えることにした。たとえば K と E と Y を1回ずつ使って長さ3の文字列を構成することを考えると、KEY,KYE,EKY,EYK,YKE,YEK のそれぞれを作るのに要する操作回数は同じになる場合も異なる場合もあるので、操作回数ごとにまとめて場合の数を記録する。それら操作回数と場合の数のペアが (K×1;E×1;Y×1) という(長さ3の文字列がとる1つの)状態に対応する。これを長さ0の文字列から始めて1文字、2文字と遷移していく DP で。■提出 #28591194 (AC / 1008 Byte / 92 ms)。1007 Byte を書き下して実行してみれば文字列が閉じていないというシンタックスエラーを除いてバグ無しでびっくりした。入力される文字列長が最大で 30 だから内側のループで線形時間の愚直スキャンをしても許されるのが優しい。だからこその ABC-E だったのかもしれないけど……(E 問題はおろか D 問題も解けなかった)。■こちらの提出(#27283710)によると公式解説動画でメモ化再帰を紹介していたもよう。すっごく短く書けるっぽい。長くはなったけど 92 ms は Python と比べても速いみたいなので良しとしよう。■ダメだった D 問題は「実は……」と解説に書かれている内容を鵜呑みにして AC をとっただけなので日記にはなりませんでした。提出 #28342280 (AC)。■ところで、E-Swap に提出した AC スクリプトにおける I 変数は、昨日あった HHKB プログラミングコンテスト 2022(AtCoder Beginner Contest 235)-C「The Kth Time Query」のためのデータと同じ構造をしている。C 問題が E 問題を解く道具になっていることが確認できるのはいいね。


2022年01月11日 (火) [AtCoder] 精進。第九回 アルゴリズム実技検定 過去問-M「名前の変更」。やることは明確で簡単(英語の問題名が「Deadnames」ってこれヒントですよね? 言語差別だー)。問題は制約の大きさ。BIT でやった>提出 #28465041 (AC / 1054 Byte / 2778 ms)。提出したスクリプトで BIT#index が担当する、値から添字を逆引きする処理をこれまで書いたことがなく、できるかどうか、どうやるのかも知らず、デバッグに大変苦労した。そういう処理を書かなくても BIT#[] メソッドと二分探索を組み合わせて代わりにすることはできるだろうけど、計算量が log^2 になるのが問題になりそうだった。その失敗は LCA のダブリングを初めて書いたときにすでに経験している>20210617p01.02。log が log^2 になったとき 2778 ms が制限時間の4秒におさまるかどうかは、どうだろう。ところで、サイズ N の BIT を N より多く確保して許されるかどうかは運でした。■Python で一番速い提出では BalancingTree という構造を使っていたが知らない>#28312971。その次の人は AVL 木という名前の構造を使っていたが知らない>#28339871。Python で一番短くて速い提出が2つのメソッドと2つの配列で何をやっているのかはさっぱりわからない>#28288268。■どうせ運に任せるのなら N×N の規模の BIT を1つだけ使うのでも良かったのでは?>提出 #28467644 (AC / 757 Byte / 2894 ms)。ちょっとだけ遅くなった。


2022年01月09日 (日) [AtCoder] 昨日あった ABC234-D「Prefix K-th Max」。解けなかったんだよ。言い訳がいくつもある。一度はプライオリティキューを貼り付けたし、プライオリティキューを使わないで適当なメモを使ってうまくやる方法もずっと考えていた。一番の誤算は「K 番目に大きい値」に対する勘違い。解法を一通り検討した後でサンプルを読んで間違いに気がついた。この用語の難しさについては AtCoder 社長のツイートで読んで知っていたのに、迷いなく小さい方から K 番目の値を答えようとしていたのだからどうしようもない。そこから頭の中をリセットするということがまず難しかった。K 個の値を蓄えたプライオリティキューから最大の値を答えつつ、K+1 個目の最小値をキューから追い出さなければいけないような気がしてしまって手詰まりに陥っていた。意味不明なキメラ。言い訳ですよ。能力の絶対値が低いところをさまよっているからこういうところでつまづくし、そのまま転んでしまう。■PQ 解法>#28432346 (810 ms)。メモ-スライド解法>#28433511 (527 ms)。■自分は雰囲気で文字を読んでいる。2nd largest だとか for the first time in 10 years だとか K-th max だなんてのは罠なんだ。maximum value より大きい値が K-1 個もあるだなんて詐欺なんだ(K-1 個は小さい方にありそうな「雰囲気」がしない?)。中途半端な値は見ようによって大きい値でもあるし小さい値でもある。自分に言わせればそれは単に「大きい方から K 番目の値」なんであって、大きい値でも小さい値でもない。はい、愚痴でした。何回も水色に上がれるなんてお得だね(果たして次はあるのか?)。


2022年01月07日 (金) [BOOX Max2] 3年ちょっと前に購入した BOOX Max2。先週あたりにバッテリーの具合が急変して、たいへんよろしくない。どういう状態か。満充電から 70-80 % まで順当に使用していたと思ったら、次の瞬間に残量が 15% を切ったときの警告音が鳴る。その後数分で電源が落ちる。その落ち方も異常だ。これまでならバッテリーが尽きるまで使用を続けていると、電源が切れると同時に画面にはバッテリーマークが表示されていた。落ちるときにも最後に画面を書き換えるだけの余力を残していた。それが最近の数回はページめくりの途中で、乱れた画面を残してボタン操作への反応を喪失してしまう。電源ボタン長押しも効かない。画面の異常さからフリーズしたのかとあせるけど、電源につなげば無事に再起動する。だけどほんの5分10分前にはまだバッテリー残量は半分以上残っていたのだし、満充電から半日も使用していないのだからバッテリー警告は何かの間違いだと思っていた。バッテリー容量が7割方どこかへ消失したかのようだ。それも徐々に劣化して、というのではなく。リチウムイオン電池は寒さで見かけの容量が減る(暖めると回復する)けど、氷点下でも屋外でもないのだよ。過放電で不可逆的に劣化するというけど、残り数%まで使ってはいけなかったのか(15%で警告が出てから、キリがいいところまでは読みたいじゃない)。充電の進捗もおかしい。ケーブルをつなげば残量が10数%あって、30分程度かけても 2-3%しか増えない。今後は常時ひも付きで使うしかないのかなあ。ちょっと不便。■こういうことをしている人がいたよ。「2021年9月 BOOX MAX2をバッテリー交換して使っています。 | きょうは毒きのこ日和です - 楽天ブログ」「なす漬: ONIX Boox max2 Battery repair (DIY)」■ちなみに 10 年ちょっと前に買った Sony Reader (PRS-650) とは使い分けていて、そちらは外出&風呂用にしている。バッテリーの持ちは悪くなってるけど数日は使えるし、毎週のように充電している(要するに毎週ではない。5、6日に1回かな)。バッテリーの具合は知らんけど、予備機も確保してある(そんで思い出した頃に充電している)。


2021年12月24日 (金) [AtCoder] PAST の支払いチャレンジ。支払い方法の案内がなくて実際に手続きを進めるしか知る方法がなかった。以前は途中で引き返したけど今日は atcoder.jp のログインパスワードを引っ張り出してきてもう少し先へ進んだ。(オンラインサービスが採用する第一の選択肢だと思ってる)クレジットカードだけなんだろうと半ばあきらめていたのだけどインターネットバンキングにも対応していた(「銀行決済取り扱い可能な金融機関一覧(2020年2月現在)」)。一覧に利用している銀行名があったので進んだのだけど、そういえばインターネットバンキングの利用開始手続きは5年前に断念したっきりだった。「インターネットバンキングを申し込もうとして、利用規定と個人情報の取り扱いについての PDF 2つを流し読みして、読みました同意しますとチェックを入れたのだが、タイムアウトで次のページへ移行できなかった。読んでないことをチェックするためにタイムアウトが存在している」。クレカを(AtCoder のために)持つよりインターネットバンキングを利用する方がハードルが低いけど、どうだろうなあ。OS が古いとかブラウザが古いとかで弾かれそうなイメージがある。OS を Windows 10 にするとか変なプログラムをインストールするとかは考えられないよ。とりあえず次は利用開始手続きに再挑戦するところから。


2021年12月23日 (木) 今期は運命ちゃん(コゼット)が可愛かった。何も知らんかったけどたまたま目に入った1話目で惚れたよね。まずはキャラデザの勝利。それからキャラクター造形とシナリオの勝利。大勝利。良かった。タイタンいい子。