たいちょーの雑記

ぼくが3日に一度くらい雑記をかくところ

第64回2の6乗シェル芸勉強会に参加しました

参加しました。今回はタイミングよく本会場の近くにいたため、現地参加してきました。 いろんな人にお会いできてよかったなあと思いました。

今回も参加記として自分の解答と復習のPowerShell解を書きます。

問題と解説は当日の配信をご覧ください

問題1

replay.txtはメールの件名、送信者、受信者、送信時刻が行区切りで書かれているデータです。このデータの中から、送信と返信のペアを見つけて、返信までに何時間かかったかを出力するという問題。

$ join -j9 ShellGeiData/vol.64/reply.txt{,} | awk '"Re:"$4==$NF{print $4, $3, $7, $1"T"$2, $5"T"$6}' | teip -f 4,5 -- date -f- +%s | awk '{print $1,$2,$3,($5-$4)/3600}' | sort -k4n
やったー 太郎->花子 花子->太郎 3.18278
首記の件 花子->太郎 太郎->花子 22.8919
こんにちは 太郎->花子 花子->太郎 25.1964
げんき? 太郎->花子 花子->太郎 52.0769
毎々お世話になります 太郎->花子 花子->太郎 81.9994

join -j9でクロスジョインすることでペアを全列挙します。そこから欲しいペアだけawkで出力していくやり方を採用しました。awkで状態を持たなくていいのでちょっと楽ですけど、カラムが多くて数えるのが面倒です。 ペアを出したあとは、teipで日付をUnixTimeにして、時間の差分を取るって感じにしました。dateutilsdatediffだともうちょっと簡単に書けたかもです。

PowerShell解もやってみます。

$ cat .\ShellGeiData\vol.64\reply.txt | % -Begin {$hash=@{}}  { $x=$_ -split ' '; $hash[$x[3]]=@{t=Get-Date -Date ($x[0..1] -join ' '); ft=$x[2]} } -End { $hash.Keys | ?{ $hash["Re:$_"] } | %{ @($_, $hash[$_].ft,$hash["Re:$_"].ft, $hash["Re:$_"].t.Subtract($hash[$_].t).TotalHours) -join ' ' }} | Sort-Object {[decimal]($_ -split ' ')[3]}
やったー 太郎->花子 花子->太郎 3.18277777777778
首記の件 花子->太郎 太郎->花子 22.8919444444444
こんにちは 太郎->花子 花子->太郎 25.1963888888889
げんき? 太郎->花子 花子->太郎 52.0769444444444
毎々お世話になります 太郎->花子 花子->太郎 81.9994444444444

シュッとクロスジョインできなかったので、ForEach-Objectでゴリゴリしました。そんなにいうことはないんですけど、Hashじゃない単純なテキストファイルのソートでSort-Objectを使うとき、列の指定に {($_ -split ' ')[N]}を使うってのを見てそうなんだ…。ってなりました。

問題2

平均値ゼロ、標準偏差1の正規分布に従う乱数を延々と出力する問題。よくわからない場合は0~1の乱数を12個足して6引くのでもOKとのこと。よくわかんないので後者で行きます。

$ yes | awk 'BEGIN{srand()}{print rand()}' | xargs -n12 | sel -D + 0 | addr -6 | bc | head
-.5806370
.266194
-1.616049118
-.6380601
-.297515
-.5362764
-1.0671419
1.218777
.5491392
-.9868898

はい…。解説することがないぐらいシンプルにできました。ちなみにaddrはegzactのやつで、右端に文字を追加するっていうコマンドです。sedでもいいです。

PowerShell解もやってみます

$ while($true) {(Get-Random -SetSeed (Get-Date).Nanosecond -Maximum 1.0 -Count 12 | measure -Sum).Sum - 6}

こちらも解説することはあんまないです。

問題3

matファイルには行列が書かれています。これが対称行列であることを示してください。という問題。

$ cat ShellGeiData/vol.64/mat | rs -T | sel -a 0 | diff - ShellGeiData/vol.64/mat && echo そうだよ || echo ちゃうよ
そうだよ

そうらしいです。rs-Tで転置して、diffが出なければOKって感じですね。

PowerShell解も大体同じになっちゃうんですが、こっちはjuliaでやってみます。

$ diff (("transpose([$((cat .\ShellGeiData\vol.64\mat) -join ';')])" | julia)[1..5] -replace ' +',' ' -replace '^ ') (cat .\ShellGeiData\vol.64\mat); echo $?
True

juliaで転置を計算し、不要な部分を削ってから diffを取ってます。それだけです

問題4

nums.0ファイルは、一行に[value] [key]という形式でデータが書かれたファイルです。

小問1

[key]で集計し、[value]の平均値を求めて下さい

小問2

nums.0[key]を付け替えてください。さっき求めた平均値に最も近い[key]を選ぶようにしてください。

という感じの問題。小問1はさっと行けたんですが…。

$ cat ShellGeiData/vol.64/nums.0 | awk '{c[$2]++;t[$2]+=$1}END{for(k in c) print k, t[k]/c[k]}'
0 5.33333
1 6.33333
2 6

小問2は時間切れになりました。解説によると、平均値を 5.33333 6.33333 6みたいに1行にしてから、nums.0の右に連結、awkで一番近いのを計算しつつ、フィールド番号からキーを付け替えるということでした。むず

問題5

オイラーのファイ関数を実装してください。という問題。

$ echo 12 | factor | zniq | tr -d : | teip -f2- -- sed 's:.*:*(1-1/&):g' | julia
4.0

素因数をuniqし、k番目の素因数をpkとしたとき、この式はN * (1-1/p1) * (1-1/p2) * ... * (1-1/pk)となるので、これをシェルで再現すればよいということになりますね。素因数分解といえばfactorコマンドです。この出力からユニークな素因数一覧を取り出したいです。fmt -1|uniqみたいなことをすればいいですが、横方向のuniqをしたいときはegzactのzinqを使えばサッとできるのでオススメです。あとはteipで1カラム目以降を(1-1/pk)の形に整形してjuliaで計算してます。別にjuliaじゃなくてもよいです。

PowerShell解もやってみます。と思ったんですけど、juliaを調べてたらtotientがありました

juliamath.github.io

$ echo 12 | %{ julia -E "using Primes;totient($_)" }
4

PowerShell解じゃなくない?とはなってます

問題6

小問1

seq 100 |から初めて、各行の数字に対して互いに素な自然数を列挙してください

小問2

列挙した自然数のリストが、ファイ関数の出力と一致することを確認してください

という問題。時間内にどちらも解けませんでした!

# 小問1
$ seq 100 | xargs -I@ julia -E '(@, [x for x in 1:@ if gcd(@,x) == 1])' | tr -d \(\)
1, [1]
2, [1]
3, [1, 2]
4, [1, 3]
5, [1, 2, 3, 4]
6, [1, 5]
7, [1, 2, 3, 4, 5, 6]
8, [1, 3, 5, 7]
9, [1, 2, 4, 5, 7, 8]
10, [1, 3, 7, 9]
11, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
12, [1, 5, 7, 11]
...

またjuliaです。Pythonみたいなリスト内包表記が使えるってのをみてめっちゃええやんってなりました。

小問2も、ここにちょっと足すだけで解けますね。

# 小問2
$ seq 100 | xargs -I@ julia -E 'using Primes; (@, totient(@), [x for x in 1:@ if gcd(@,x) == 1])' | tr -d \(\)
1, 1, [1]
2, 1, [1]
3, 2, [1, 2]
4, 2, [1, 3]
5, 4, [1, 2, 3, 4]
6, 2, [1, 5]
7, 6, [1, 2, 3, 4, 5, 6]
8, 4, [1, 3, 5, 7]
9, 6, [1, 2, 4, 5, 7, 8]
10, 4, [1, 3, 7, 9]
11, 10, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
12, 4, [1, 5, 7, 11]
...

2要素目にファイ関数の出力を置きました。あとは見比べればいいですね。

PowerShell解はほぼ同じになっちゃうので省略です。

LT

今回自分はLTしませんでしたが、面白い話を聞いたので紹介します。

qiita.com

ジョークRFCのrfc9402の実装をしたっていう内容でした。めっちゃよかった。

感想

以前juliaをオススメされてから少しずつ使ってはいたのですが、今回の問題だと使える場面があって良かったです。これで数学系の問題がきても平気ですね。

今回もとても楽しかったです!企画、運営などありがとうございました!