参加しました
今回もリモート開催でした。毎度書きますけど、やっぱり問題の辛さに呻く声とかが聞こえなくて寂しいですね
今回も時間中に解答したものと、これを書くと同時にPowerShellでの解答も書いていこうと思います。できるのか…俺に…?
Q1
un.txt.gzから、UNITY, UNKO, unity, unkoの数をそれぞれなるべく短時間で数えてください。 単語の途中に改行が入っているものも数えてください。
問題の意味は簡単に理解できるので、パッと思いつくような書き方で行くなら
$ zcat un.txt.gz | tr -d \\n | grep -io -e "UNKO" -e "UNITY" | sort | uniq -c | sort -rn
ところが、これでは時間がかかりすぎるということで、速度の改善が必要とのこと。最初はzgrep
で改行を巻き込んだ検索をしようとしました
$ zgrep -oiP "(U\n?N\n?K\n?O\n?|U\n?N\n?I\n?T\n?Y\n?)" ./un.txt.gz | sort | uniq -c | sort -rn
これだとうまく引っかけられなかったので、長考。解答例では以下のように a
だけの行を削ってから検索すればよいとのこと
zgrep -vP "^a+$" *a/*50/un.txt.gz | tr -d \\n | grep -ioP "un(ko|ity)" | sort | uniq -c | sort -rn
ところがこれは
aaaaaaaaaaaaaaaaun aaaaaaaaaaaaaaaaaa koaaaaaaaaaaaaaaaa
というデータにぶつかったときに正しくなくなる。(参考)
それを踏まえてもう一度考える。
$ time zcat ./un.txt.gz| sed -E 's/a+/a/g' | tr -d \\n | grep -ioP "UN(KO|ITY)" | sort | uniq -c | sort -rn 3 unko 3 unity 2 UNKO 2 UNITY zcat ./un.txt.gz 5.39s user 0.83s system 61% cpu 10.102 total sed -E 's/a+/a/g' 7.67s user 2.43s system 99% cpu 10.102 total tr -d \\n 0.03s user 0.24s system 2% cpu 10.102 total grep -ioP "UN(KO|ITY)" 1.23s user 0.00s system 12% cpu 10.190 total sort 0.00s user 0.00s system 0% cpu 10.189 total uniq -c 0.00s user 0.00s system 0% cpu 10.189 total sort -rn 0.00s user 0.00s system 0% cpu 10.188 total
連続するa
を1文字のa
にしてから改行削除、grep
で絞り込みをしてみた。これだと10秒ぐらい。もっと速い解答もTwitterにあるけど、僕のはこれとしておきます。
続いてPowerShellです。
$ (7z e ./un.txt.gz -so |%{ $_ -replace "a+","a" }| Get-Unique) -join "" | Select-String -AllMatches "UN(KO|ITY)" | %{$_.Matches.Value} | Group-Object -CaseSensitive Count Name Group ----- ---- ----- 2 UNITY {UNITY, UNITY} 3 unko {unko, unko, unko} 2 UNKO {UNKO, UNKO} 3 unity {unity, unity, unity}
やることは同じです。PowerShellではgrep
の代わりにSelect-String
をよく使いますが、grep
の-o
に相当する機能を単独で持ちません。Select-String
はMatchInfo
というオブジェクトを出力するので、そこからマッチした部分(MatchInfo.Matches.Value
)を取り出すことでgrep -o
相当のことができます。あとはGroup-Object
で数え上げればOKですね。
Q2
un.txt.gzでaのみが書いてある行の行数をなるべく早くカウントしてください。
とのこと。
$ zgrep -cP "^a+$" *a/*50/un.txt.gz $ rg -cz "^a+$" *a/*50/un.txt.gz
このどちらかがまあ割と高速。解説することがないですね。
PowerShellでの解答も書いていこうと思います。
$ 7z e ./un.txt.gz -so | group
これで出るんじゃないかな・・・(手元じゃ終わらなかったのでわからない)
Q3
cat
コマンドを遅くする問題
cat ./un.txt > /dev/null
こう書くと、一瞬で終わるのだけど、このコマンドの前に何かを仕込んで遅くするというもの
$ function cat() { \cat $1 | \cat } $ cat a > /dev/null
単純に二段重ねにしたら遅くなるよねって感じですね。解答例としては、/proc/sys/vm/drop_caches
をいじってやるというもの。勉強になるなあ
$ echo 3 | sudo tee /proc/sys/vm/drop_caches $ cat a > /dev/null
Q4
1から1億までの数字をシャッフルして2列にしたデータ(ファイル名はaにしましょう)を作ってください。速いワンライナーを考えてください。
とのこと。最初に自分が書いたのがこれ
$ seq 1000000 | paste -d' ' - - | teip -f1 shuf | teip -f2 shuf > a'
xargs
では永遠に終わらないだろうなと思っていたので、paste
、シャッフルは後続のteip
に任せることに。これ結構いいと思ったんですが…
さっきの自分の回答を1億でマシンに放ったら止まりました。俺はおいて行ってくれ#シェル芸
— たいちょー (@xztaityozx_001) 2020年11月7日
マシンがやられてしまった。物理ボタンぶち押して再起動しました。悲しい。
再起動中TLを見ているとshuf
の-i
オプションというのがあるのを知ってへぇと思いました
$ shuf -i 1-10 8 3 7 2 9 10 1 6 4 5
PowerShellでの解答も書いていこうと思います。
$ 1..100000000 | Sort-Object { Get-Random } | %{[PSCustomObject]@{k=$_}} | Format-Wide -Column 2
横に並べるって言うのが中々大変でした。Format-Wide
でそのようなことができるのですが、一度PSCustomObject
にして置く必要があるんですねこれが。あとこれも全然終わりません。悲しい。どうしたらいいんだ
一応、Get-Content
の-ReadCount
を使えばファイルの中身を分割できるので、少しずつなら出力できますね。ただし、区切られた区間でのみのシャッフルになりますが
$ 1..100000000 > nums $ Get-Content ./nums -ReadCount 10000 | %{ $_ | Sort-Object{Get-Random} } | %{[PSCustomObject]@{k=$_}} | Format-Wide -Column 2
この問題の出力は次の問題で使うので、a
というファイルに書き出しておきます。
Q5
cat a | sort -k2,2n > b
を出力内容を変えずに高速化する問題。
これは知識問題。気づけるかな?という問題でした。cat
から始めていますが、sort
に直接渡した方がCPU使用率に差がでるとのこと。そういえばcat
からおもむろに始めることが多いので、知らないうちにワンライナーが遅くなっちゃってるかもですね。勉強になる
Q6
出題時マシン再起動中でした(前問を試していたら死んだ)。問題を見る限り、teip
が使えそうと思っていたらTLにもあったのでさすがteip
だなあと思いました
復活して書いたのがこれです。
$ awk '$1%2==$2%2==1' a | teip -og \\d+ -- factor | awk 'NF==4' > c
前段では、両方とも奇数なカラムだけを取り出して前処理、次に-og \\d+
で数値だけなカラムに対してfactor
を適用します。あとは awk
で切り出しですね。
PowerShellでは、なんだろう…teip
が使えないので、ForEach-Object
でfactor
するとかかな…?
Q7
a
に対して二つの操作をする問題。
- 行頭に A, B, C, ... , Z, A, ...を付ける
- 行頭のA-ZでGroupByする
これ、最中は同時にやるもんだと思っていたので以下の解答をしました
$ awk '{print $1"_"$2}' a | paste -d' ' -{,,,,,,,,,,,,,,,,,,,,,,,,,} | rs -T| tr -d _ | paste <(echo {A..Z} | fmt -1) -'
26個なのは決まっているので、paste
で横に26個並べてから rs -T
で転地。あとは行頭に A-Zを付けただけですね。まぁこれは題意とは違うのでダメなのですが・・・
Q8
Q7について、さらに各行の数字を小さい順にソートするという条件をつけてください。ans2というファイルに保存します。
Q5の解きなおしをしていたらマシンが死んだので再起動していました。本当に悲しい。
LT
今回はLTしました。以前のエントリで書いたワンライナーC#のocs
と、カラム選択を短縮できるsel
の紹介です。
ocs
の方はまぁ良かったんですが、sel
の発表中に、すでにself
という同じ感じのあるよというナイフを突きつけられてめちゃめちゃ焦りました。
一応冷静にself
とsel
の比較をしていたんですが、sel
には入力と出力のデリミタを指定できる、sed -i
みたいな上書きができるなどの優位点があります。
速度的にはチューニング後のsel
とHaskell版のself
で大体同じ。若干self
の方が速いです。ただしこれは、sel
の入力デリミタを固定文字(スペース)としたときなので、正規表現で分割するときは負けてしまいます。
あとはsel
はgo get
するだけ、self
はPythonの特定バージョン以上が必要。もしくは、ghc
などでHaskell版をコンパイルする必要があるなどが違いですかね。
正直 sel 1 2 3
みたいな使い方をしたいだけならどちらでもよいので、好きな方を使おうって感じですね。今回のことでパフォーマンスチューニングに興味も沸いたのでsel
を爆速にしてやりたいと思っております。
まとめ
今回もとても楽しかったです!あととっても疲れました!!Q8のあたりとかマシンもぼくも疲労困憊でした!次回も楽しみです!ありがとうございました!