サイコロの旅のすすめ
TL;DR
18きっぷが余っていたのでサイコロを振って出た目が示す所を目的地とするのを繰り返すサイコロの旅をしました。
サイコロの旅
どこに行くのか決まっている旅もいいですけど、たまにはどこに行くのか分からない旅もいいと思いませんか? このサイコロの旅ではどこに行くのかはダイスを振って決めます。 予定が直前まで分からないので管理された旅が苦手な方におすすめです。
京都発
18きっぷでサイコロの旅スタートしました。
— むらため (> <) (@paradigm_9) September 8, 2019
京都駅からの選択肢は 1:舞鶴 2:関空 3:岡山 4:柘植 / 5:勝山 6:岐阜 で、2が出たので関西空港に向かいます pic.twitter.com/rYQS9h2edG
本家水曜どうでしょうに合わせて近く4駅,遠い2駅から目的地は万遍なく選びました。 柘植とかが出るとこのあとの発展性があるんですが、今回は関西空港になりました。
関西空港
18きっぷサイコロの旅、現在関西空港に来ております。次の選択肢は 1:和歌山 2:王寺 3:徳島 4:神戸(フェリー経由) 5:岡山 で、1が出たので次は和歌山に向かいます。(結局とんぼ返りするためだけに関空に来たことになった…) pic.twitter.com/HGCIPM4U2Q
— むらため (> <) (@paradigm_9) September 8, 2019
次の目的地は和歌山になりました。
関西空港に来ましたが、18きっぷを使うのが目的なので海外には行きませんし、当日飛行機は高価なので飛行機を使うわけでもありません。 その日はイベントもありません。 完全に「大阪から和歌山行きに乗ったが間違えて先頭車両に乗ってしまい関西空港に着いた人」です*1。
周りが飛行機で旅たとうとしているなか、我々は改札で出した18きっぷで再度Uターンします。 まあこういう完全な無駄行動もサイコロの旅の醍醐味ですね。
ちなみにサイコロの出目によっては,関西空港から神戸港のフェリーに乗ったり、すきっぷ2000というお得きっぷを使うレアな機会があったのですが、これらの目は出ませんでした、悲しい〜
和歌山
18きっぷでサイコロの旅、現在和歌山から 1:松坂 2:王寺 3:徳島 4:神戸 で、3が出ました!このままフェリーで徳島に向かいます! pic.twitter.com/frP1c8savv
— むらため (> <) (@paradigm_9) September 9, 2019
徳島が出ました!フェリーか〜〜〜〜〜〜〜〜!
和歌山城・和歌山ラーメンを観光。
和歌山通過記念 pic.twitter.com/1LiRdUO0Xr
— むらため (> <) (@paradigm_9) September 9, 2019
知っている人は知っている、和歌山の地酒高垣を売っている酒店です。
デレの不思議な縁でできた観光地になっており、前に来たときよりも随分とグッズが増えていました。 こういうところはいいですね。
出港しています(18きっぷとは) pic.twitter.com/lmNvRPbOzI
— むらため (> <) (@paradigm_9) September 9, 2019
四国ローカルアニメらしいです pic.twitter.com/dGyFpjO7Eo
— むらため (> <) (@paradigm_9) September 9, 2019
フェリーに乗るとやたらとこのアニメがプッシュされていました。 「おへんろ。」という四国ローカルアニメらしいです。
どパスコ見てる / おへんろ。第1話https://t.co/X28adRk2rT pic.twitter.com/v6TMKQgXox
— むらため (> <) (@paradigm_9) September 9, 2019
どパスコが可愛かったのでツボに入りました。
徳島
18きっぷサイコロの旅、現在徳島に来ております。今日の宿泊地は 1:海部 2:小歩危 3:岡山 4:高松 で2がでたので小歩危に泊まります。山の中じゃん! pic.twitter.com/k1tuVJ5rbs
— むらため (> <) (@paradigm_9) September 9, 2019
というわけで最終目的地は小歩危(大歩危)になりました。 歩くのが危険と書いていますが、それは昔の話で今は道も整備され電灯もあり、歩くのは容易いです。
2日目
2日目もサイコロを振ると帰れなくなるので普通に大歩危を観光して帰ります。
かずら橋@徳島 pic.twitter.com/eAv3Apyxof
— むらため (> <) (@paradigm_9) September 10, 2019
— むらため (> <) (@paradigm_9) September 10, 2019
さいごに
サイコロを振って結果が出るまでどうなるか全然わからないのはかなり楽しいので、旅にちょっとしたスパイスをかけたいかたはぜひサイコロの旅をおすすめします。
おわり
nim-lang/Nim に issue を建ててちょっと恥ずかしい思いをした話
TL;DR
- Nim で C++ の std::set のラッパーを書いた
- 奇妙なコンパイラのバグを踏んだのでNimにissueを建てた
- 4年前に建てられていたissue と同様のものだったために close された、恥ずかし〜
はじめに
私は Nim が好きでNimをよく書いています。 最近AtCoderでstd::setを使う問題がよく出題されており、 Nimでも C++ の std::set を使えるようにラッパーを作っていました。
ところが、これを使用した方からコンパイルエラーが出るという報告をいただきました。
コンパイルエラー
type CSet {.importcpp: "std::set", header: "<set>".} [T] = object proc cInitSet(T: typedesc): CSet[T] {.importcpp: "std::set<'*1>()", nodecl.} proc insert[T](self: var CSet[T],x:T) {.importcpp: "#.insert(@)", nodecl.} proc `==`[T](x,y:CSet[T]):bool{.importcpp: "#==#", nodecl.} var (S1,S2) = (cInitSet(int),cInitSet(int)) S1.insert 1 if S1 == S2: echo "wrong" else: echo "correct"
このコードをコンパイルすると、以下のエラーがでて、NimからC++に変換した後のC++のコンパイルに失敗します。
Error: execution of an external compiler program 'clang++ -c -w -~/.choosenim/toolchains/nim-0.20.0/lib -I~/codes/nim/ -o ~/.cache/nim/t_d/t.nim.cpp.o ~/.cache/nim/t_d/t.nim.cpp' failed with exit code: 1 ~/.cache/nim/t_d/t.nim.cpp:153:7: error: invalid argument type 'TY_89ajMAHL4D29bP2aGRstu1mQ' (aka 'set<long long>') to unary expression if (!S1_X5vbOHyI0SI13LVkxEIzEQ==S2_q2YrwwKwgOf4xdemDnRTYA) goto LA4_; ^~~~~~~~~~~~~~~~~~~~~~~~~~ 1 error generated.
ほう、という感じですね、
このC++コードの全文は以下です。
N_LIB_PRIVATE N_NIMCALL(void, NimMainModule)(void) { { tyTuple_WVskh7WUQHL0ugdByxngAw T1_; nimfr_("t", "/path/to/t.nim"); nimln_(8, "/path/to/t.nim"); T1_.Field0 = std::set<NI>(); T1_.Field1 = std::set<NI>(); S1_X5vbOHyI0SI13LVkxEIzEQ = T1_.Field0; S2_q2YrwwKwgOf4xdemDnRTYA = T1_.Field1; nimln_(9, "/path/to/t.nim"); S1_X5vbOHyI0SI13LVkxEIzEQ.insert(((NI) 1)); nimln_(10, "/path/to/t.nim"); { if (!S1_X5vbOHyI0SI13LVkxEIzEQ==S2_q2YrwwKwgOf4xdemDnRTYA) goto LA4_; { echoBinSafe(TM_earX20ePV9cxitomwdUMS7g_2, 1); } } goto LA2_; LA4_: ; { nimln_(11, "/path/to/t.nim"); echoBinSafe(TM_earX20ePV9cxitomwdUMS7g_4, 1); } LA2_: ; popFrame(); } }
このエラーを見ると分かるように、要は if S1 == S2: ...
の条件分岐を C++ に翻訳した時に、 if (!S1 == S2 ) goto LA4_;
になってしまっているようです。
条件分岐をミスっている様子で、これは正解は if (!(S1 == S2 )) goto LA4_;
となるべきですね。
つまり、if A == B: X else: Y
をC++に翻訳する際に、 if (!(A == B))Y; else X;
となるべきところを、 if (!A == B)Y; else X;
としてしまっているわけです。どうして否定が入っているのかはわかりませんが、条件分岐のときにだけコンパイルが失敗するので、最適化か何かの都合で混入したバグなのでしょう。
nim-lang/Nim に issue を建てる
このバグは最新のNim0.20 でも発生します。流石にこれを放っておくわけにもいかないので issue を建ててみます。
勿論、(これはOSSを使うときには今までもよくあることなのですが) すでに issue で議論されていないか確認すべきです。
今回のissueに関連するワード equal
function
if
override
の単語で検索し、既に議論されていないか確認します。
無さそうだったので、早速 issue を建てます。
こんな感じで、Bug Report のissueを建てようとすると、指針としてテンプレートが既に書かれています。便利ですね。 これに従って書いていけばよさそうです。
Think about the title, twice
と初めに書いてあります。タイトルは大事ですからね。 普段技術文書を英語で読むことはよくあるのですが、それに対して英語を書くのはとても苦手なので最低限文法が間違っていないかをgrammarly でチェックします。
英語の文法ミス・綴りミスを直すためのサイトを検索するのに綴りミスをしている図です pic.twitter.com/fTGUfNc8ob
— むらため (> <) (@paradigm_9) September 12, 2019
そしてこれは戒めなのですが、最低限文法が通っているにしても意味が最低限間違っていないかをチェックするために Google 翻訳に2回かけるやつもやっておくべきでした...(英語→日本語→英語 or 日本語→英語→日本語 をして意味が崩れないかチェックするやつ)
例えば 建てた issue の冒頭、
Overriding equal function causes incorrect behaviour. Nim compiler translates !(a == b) into !a == b and fails to compile.
と拙い英語で書いていたのも、
Overriding an equal function will cause incorrect behavior. Nim compiler converts !(a == b)! to a == b and fails to compile.
になって、なんとなくこっちのほうがいい感じな気がします(非ネイティブ並の感想...)*1。
なんやかんやあって issue を建てました。 github.com
回答が来る
要は # == #
(#は出てきた順にその番目の引数を表す) ではなくて、 (# == #)
と定義してくれとのこと。なるほど *2。
Low Priority
のタグが付きます。
実は duplicate issue だった
その後、これは duplicate issue だよ、と指摘されます。あちゃ〜やっちまった〜〜。
そういうわけで4年前に建てられたissueと同一の内容であったので close されました。
Low Priority
なので 4年間放置されていたバグであっても仕方がないですね。
恥ずかし〜〜〜〜〜〜〜〜〜〜
どうすれば duplicate issue を回避できたか、 equal
function
if
override
で検索したのに加えて C++
compile
error
でも検索する、もしくは Label:C++ codegen
を探しておく、が正解だったっぽいですね。
う〜 。
未来永劫 duplicate issue を建てた者としてnim-lang/Nimに記録が残るのはちょっと悲しいですね〜〜〜。
総計 12028 件の Nimのissueのうち duplicate
を含む issue は たった 502 件 しかなくてそのうちの一つになっちゃったな〜〜〜〜。
うひ〜〜〜。
あ〜〜〜〜〜〜〜〜。
おわり。
パスワード を 平文 で保存していませんか???パスワードの最強の保存方法「Piet埋め込み」について調べてみました!
こんにちはwwwwww!!!!
暑い日が続きますねwwwwwww!
突然ですが、あなたはパスワードをどうやって保存していますか???
平文のままテキストファイルに保存していますか?
それとも適当なハッシュ関数でハッシュ化してそのハッシュをテキストファイルに保存したりしていませんか???
実はそれ、 とっても危険な保存方法 なんです!!!!!!!!
テキストファイルに怪しい文字が書いてあった場合、どんなに強いハッシュ関数でも賢い人達が衝突させてしまう可能性があります!!!!!!!!
大変ですよね!!!
一体どうすればいいんでしょう?????????
Piet
テキストファイルに怪しい文字を保存するのは危険 ですよね!!!!
なので、実はパスワードは画像に埋め込むべきなんです!!!!!!!!!!!
ここではオススメの埋め込み方法として、Piet にパスワードを埋め込む方法 を解説します!!!!!!!
(元画像は 東方32×32打ち直しシリーズまとめ! - えるるのだいあり ~Season2~ よりお借りしています!!! )
この画像は何の変哲もない32x32のフランちゃんのドット絵ですよね!!!!!!!!!
でも、この画像はなんとPiet として実行できる んです!!!!!!!!!!
実際にこの画像を(npietなどで)実行すると 「PietPiet」と表示されるんです!!!!!!!!!!
この「Piet埋め込み」 の方法を使えば安全にパスワードを保存できますね!!!!!!!
さらにさらに、僕は頑張ったので 好きな文字を好きなドット絵に、なるべく原型を保ったまま最良優先探索をしてPietとして埋め込むプログラムが既に存在してるんです!!!!!!!
すぐに使える実装コードはこちら!
この「Piet埋め込み」 の方法を使えば安全にパスワードを保存できますね!!!!!!!
さいごに
いかがでしたか??
もしパスワードを平文やテキストのまま保存していた場合はこの記事のようにパスワードをPiet化して保存したほうがいいかもしれませんね!
この記事が役に立ったら欲しい物リストのものを買って下さい!
ISUCON9で予選通過したのでbcryptについて書く
はじめに
ISUCON9の本戦出場が決まりました. 今年から学生枠が無くなったためかなり厳しい状況になるな〜と思っていましたが、チームメンバーのおかげで通過できました.
ISUCON本戦出場決定しました!! (チーム百万円ドリブン)https://t.co/Di7z2EKNX1 pic.twitter.com/5PahwX0SX9
— むらため (> <) (@paradigm_9) September 8, 2019
私のチーム(百万円ドブリン)の状況は@nakario のブログ に詳しく書いてあるのでそちらをご参照ください. 私がしたことはbcryptをいじっていただけだったのでチームメンバーの力が如何に素晴らしいかがわかります。
bcrypt 改善について
実は去年の本戦でもbcryptが出題されていました。 GoアプリのCPUフレームグラフを見るとbcryptの処理が支配的になるのですが、 その時はGoアプリのCPUはネックになっていないため、そこをどんなに改善してもスコアが上がらない、という構成でした。 昨年はここを改善しようとして沼にハマって大敗しました。 詳しくは isucon8本戦は低レイヤーがネックにならない良問だった - (/^^)/⌒●~*$ a(){ a|a& };a を読んでいただければ。
うって変わって今年の bcrypt はかなり強めの主張をしてきました。 アプリをチューニングしていくと頻繁に login 処理が入るようになり、bcryptの処理が支配的かつCPUを大量に消費するようになります。
これの直し方は以下の2つがあります。
- こちらが持っているハッシュ化済みのパスワードからもとのパスワードは推測できないので、ベンチマーカーからのログインを盗聴してハッシュアルゴリズムを変更し、徐々にコスト10のbcryptを取り除いていく。
- 本質的に改善不可能な処理なので、ログイン専用サーバーを構築し、処理を分散させる。
私は最初は1の方法を取ったのですが、実はログインはそんなに成功しないことが分かりました。 そのため試算の結果では数十回ベンチを回す必要がありそうでした*1。 加えてこの盗聴作業は楽しくなかったため、我々のチームはログイン専用サーバーを構築することにしました。 Nginxでどうやるんだったけ...とぼーっとしていたら aokabi がシュッと実装してくれました。
17時前、複数台にした途端点数が跳ね上がりました。やったぜ。aokabi最高! 勝利! もちろんbcryptが支配的になるまでチューニングしてくれたnakarioも最高! 優勝!
Go の bcrypt は遅い?
本戦終了後、twitterを見ていると 「Goのbcryptは遅い」という主張がたまに目につくようになります。 これは例えばPythonのbcryptは(Pythonそのままでbcryptすると遅すぎるので)内部的にC言語で書かれており、 そのためGoのbcryptとくらべて速いのでは、という主張です。 一理あったのでGo/Python/Rubyの3バージョンで雑に計測してみます*2。
結果、Go実装は15秒,Python実装は14秒,Ruby実装は13秒でした。Go実装はちょっとだけ遅いものの、そこまで差は無いように見えます*3*4。 どの言語を選んでもbcryptと向き合う必要がありそうです。
最後に
感想戦をする時に盗聴をしなくてもbcryptを完全に排除できるように、 全ユーザーの生パスワードを抽出したjsonファイルを作りました!お使い下さい!
今回こそは100万円を取るぞ!
AtCoderで青になるまでにやったこと
チームで制作しているゲームのミーティング(だいたい土曜日九時から)をサボってAtCoderに出るという行為を毎週続けていた結果、無事青になれました!
ごめんね!もう満足したから!
NimでAtCoder Beginners Selectionを解いてみた!
はじめに
昔にNimの競プロ記事を書いていたのですが、今改めてNimで競プロを始めるならやっぱりAtCoder Beginners Selectionの解説があった方が便利だよなーと思ったので 、マイナー言語Nimを普及させるために解いた記事を書いておきます。 Nimをご存じない方は、この記事を読んで、書きやすそうだなと思ってもらえれば幸いです。
古いAtCoder用Nim0.13.0のドキュメントやビルド情報はこちらにまとめてあります。
最新のNimの場合は こういう感じで コードの内部実装が手軽に読めるという特徴があります。
また、訳あって入力のとり方はやや非自明な取り方をしていますが、これは自分に合うものを使用してください。
入力のとり方について補足
入力が
n A1 A2 A3 ... An B1 B2 ... Bn
とあった時に通常は以下のように書くことが多いです。
let n = stdin.readLine.parseInt() let A = stdin.readline().split().map(parseInt) # 一行の中で空白で分割して取る let B = newSeqWith(n,stdin.readLine().parseInt()) # 行ごとに取る
この書き方では、場合に応じて2つの書き方を使い分けないといけない欠点があります。 長いしややこしいですよね。
なので最近ではC言語のscanf
を使ってこう書いています。
proc scanf(formatstr: cstring){.header: "<stdio.h>", varargs.} proc scan(): int = scanf("%lld\n",addr result) let n = scan() let A = newSeqWith(n,scan()) let B = newSeqWith(n,scan())
意味が明確かつ簡略になって、かなり見やすくなりました! 便利!
1 Product
proc scanf(formatstr: cstring){.header: "<stdio.h>", varargs.} proc scan(): int = scanf("%lld\n",addr result) if (scan() * scan()) mod 2 == 0 : echo "Even" else:echo "Odd"
ちなみに scan()
なしだと let tmp = stdin.readLine.split().map(parseInt);let a = tmp[0];let b = tmp[1]
と書かないといけなくてつらいです。
2 Placing Marbles
import sequtils echo stdin.readLine().filterIt(it=='1').len
配列の操作には なんとかit
が便利です。
Nimは fun(a,b,c)
を a.fun(b,c)
と書ける(UFCS)のでメソッドチェイン的なことができて便利です。
あとは getchar
を使う解法もあります。
proc getchar():char {. importc:"getchar",header: "<stdio.h>",discardable .} echo newSeqWith(3,getchar()).filterIt(it=='1').len
3 Shift only
import sequtils proc scanf(formatstr: cstring){.header: "<stdio.h>", varargs.} proc scan(): int = scanf("%lld\n",addr result) let n = scan() var A = newSeqWith(n,scan()) var ans = 0 while A.allIt(it mod 2 == 0): A = A.mapIt(it div 2) ans += 1 echo ans
allIt などの便利な なんとかIt を使っていきましょう。
定数への代入は let
、変数への代入には var
を使います。
while A.all(proc(x:int):bool = x mod 2 == 0): A = A.map(proc (x:int):int = x div 2)
のように関数を作成して書いてもOKです。
また、 import future
(最新のNimでは import sugar
) をすると
while A.all(x => x mod 2 == 0): A = A.map(x => x div 2)
みたいな感じにも書けます。 なんとか It は 言語機能にあるわけではなく、配列を操作するタイプで個別に実装しているだけなので、 こういうラムダ式形式の書き方も応用性は広く覚えておいて損はないです。
4 Coins
proc scanf(formatstr: cstring){.header: "<stdio.h>", varargs.} proc scan(): int = scanf("%lld\n",addr result) let a = scan() let b = scan() let c = scan() let x = scan() var ans = 0 for ia in 0..a: for ib in 0..b: for ic in 0..c: if 500 * ia + 100 * ib + 50 * ic == x : ans += 1 echo ans
forループは for i in 0..n
で n を含み、 for i in 0..<n
で n を含まない意味でループを回せます。
5 Some Sums
import sequtils,math proc scanf(formatstr: cstring){.header: "<stdio.h>", varargs.} proc scan(): int = scanf("%lld\n",addr result) let n = scan() let a = scan() let b = scan() var ans = 0 for i in 1..n: let isum = ($i).mapIt(it.ord - '0'.ord).sum() if a <= isum and isum <= b: ans += i echo ans
数字の文字列化は $
演算子を使います。 文字コードの数値を取得するには ord を使います。
6 Card Game for Two
import sequtils,algorithm proc scanf(formatstr: cstring){.header: "<stdio.h>", varargs.} proc scan(): int = scanf("%lld\n",addr result) let n = scan() let A = newSeqWith(n,scan()).sortedByIt(-it) var alice = 0 var bob = 0 for i,a in A: if i mod 2 == 0 : alice += a else : bob += a echo abs(alice - bob)
import algorithm
の後、 破壊的にソートする場合は sort()
が、新たに作成する場合は sorted
が使えます。
今回の場合は、ソートのキーを指定する .sortedByIt(-it)
の他に、通常のソート .sorted(cmp,Descending)
と書くのもありです。
for a in A
ではなく for i,a in A
と書くことで、 i に配列のindex を取得することができ、便利なことが多いです。
7 Kagami Mochi
import sequtils,tables proc scanf(formatstr: cstring){.header: "<stdio.h>", varargs.} proc scan(): int = scanf("%lld\n",addr result) let n = scan() echo newSeqWith(n,scan()).toCountTable().len
古いNim 0.13.0では deduplicate
がO(N2) かかるので注意が必要です。
最新のではソート済み情報を渡せるので大丈夫ですが。
8 Otoshidama
proc scanf(formatstr: cstring){.header: "<stdio.h>", varargs.} proc scan(): int = scanf("%lld\n",addr result) let n = scan() let y = scan() for ia in 0..n: for ib in 0..(n-ia): let ic = n - ia - ib if ic >= 0 and ia * 10000 + ib * 5000 + ic * 1000 == y : echo ia," ",ib," ",ic quit 0 echo "-1 -1 -1"
proc 内ではないためreturn
が書けないのですが、実は quit 0
でプログラムを終了できます。
9 Daydream
import strutils var S = stdin.readLine() proc check():bool = for word in ["dreamer","eraser","erase","dream"]: if S.endsWith(word): S = S[0..^(word.len+1)] return true return false while S.len > 0: if not check(): quit "NO",0 echo "YES"
Nim は文字列操作も割と得意です。
10 Traveling
proc scanf(formatstr: cstring){.header: "<stdio.h>", varargs.} proc scan(): int = scanf("%lld\n",addr result) let n = scan() var (ct,cx,cy) = (0,0,0) for _ in 0..<n: let (t,x,y) = (scan(),scan(),scan()) let (dt,dx,dy) = (t-ct,x-cx,y-cy) if dx.abs + dy.abs > dt or abs(dx + dy) mod 2 != dt mod 2: quit "No",0 (ct,cx,cy) = (t,x,y) echo "Yes"
Nimはタプルの分割代入が可能なため、必要に応じて使っていきましょう。
最後に
Nimはマイナー言語ですが、AtCoderでも毎回片手では数えられない程度の人数がNimで解いて提出しています! 書きやすくて速い最高の言語であるNimの使用者が増えてくれるといいな〜〜〜
就活をしてしまっていました...
最初のころのぼく
お酒を飲みながらコーディングできる職場を探しています現在就活中の理系京大院生です飲酒コーディング可能であれば月給少なくて構いませんのでどなたか早く雇ってください
— むらため (> <) (@paradigm_9) February 26, 2019
就活を終えたぼく
「内定を承諾します!理由はもちろんお分かりですね?あなたが私に内内定を出し、私が決断したからです!覚悟の準備をしておいて下さい。近いうちに入社します。引っ越しもします。お給金の準備をしておいて下さい!貴方は雇用者です!私の労働を楽しみにしておいて下さい!いいですね!」って送りたい
— むらため (> <) (@paradigm_9) June 13, 2019
いかがでしたか?
入る会社では多分飲酒コーディングはできないと思いますが、規定を読んだ限りでは業務中に飲酒をしてはいけないとは明文化されていなかったので可能性はあるかもしれませんね!
ちなみに僕はほろ酔いで真っ赤になります!!!!!!!!!!!!!!