加藤祐介ブログ

第64回シェル芸勉強会メモ

記事投稿日: 2023年4月23日(日)

最終更新日: 2024年3月30日(土)

はじめに

2023年4月22日に開催された第64回シェル芸勉強会にオンラインで参加しました。 そのときの記録をメモとして記事にしておこうと思います。 あと、解けそうな問題はChatGPT(GPT-4)にも解いていただこうと思います。

公式配信

問1 : メール解析

問題内容

メールの内容から必要な情報だけ抜き出します。

私の回答

cat S*/*64/*.txt|sed 's/-/ /g'|sed 's/:/ /g'|sed 's/:>//g'|sed 's/Re //g'|sort -k9|awk '{c[NR]=$3;d[NR]=$4;g[NR]=$7;h[NR]=$8;i[NR]=$9}END{for(j=1;j<length(i);j++){if(i[j]==i[j-1]){printf("%s %s %s %d\n",i[j],g[j-1]"->"h[j-1],g[j]"->"h[j],(c[j]-c[j-1])*24+d[j]-d[j-1])}}}'

私の感想

awkでごり押ししました。 まずsedでメール内容を整形して、時刻を比較したいメール同士が並ぶようにsortで並べ替えを行います。 それからawkでメール内容をすべて配列に入れて、時刻の差分を計算して必要な情報だけ出力しました。 ちなみに、私の回答は不完全なので分と秒を考慮できていません。 まだまだ勉強が足りていない気がしますね。 精進します。

問2 : 乱数生成

問題内容

正規分布に従う乱数を生成します。

私の回答

echo | awk 'BEGIN{srand();a=0}{for(i=0;i<12;i++)a+=rand()}END{printf("%lf\n",a-6)}'

私の感想

私は正規分布が分からないので0~1の乱数を12個足して6を引いた値を出力しました。 解説で紹介されていた「cat /dev/urandom」から乱数を生成する方法が勉強になりました。 これから乱数を生成するときは使ってみようと思います。 数値だけではなくアルファベットの文字列もランダムに生成できるとのこと。 便利ですね。ありがとうございます。

ChatGPT(GPT-4)の回答

awk 'BEGIN {srand(); u1=rand(); u2=rand(); z0=sqrt(-2*log(u1))*cos(2*3.14159265359*u2); print z0}'

Box-Muller変換を使っているらしいです。

問3 : 行列の転置

問題内容

与えられた行列が対称行列か判定します。

私の回答

cat S*/*64/mat | python3 -c 'import numpy as np;A=[input().split() for _ in range(5)];A=np.array(A);AT=A.T;r="YES" if np.array_equal(A,AT) else "NO";print(r)'

私の感想

awkで行列を扱うのは地獄になりそうだったのでPythonのNumpyを使って解きました。 解説ではdatamashコマンドが紹介されていて勉強になりました。 まだまだ知らないコマンドがたくさんありますね。 便利なコマンドは少しずつ覚えておきたい所存です。 また、二次元配列の入力に関しては以下の記事を参考にさせていただきました。

参考 : 競技プログラミングをPython3で解く

ChatGPT(GPT-4)の回答

cat S*/*64/mat | awk '{for(j=1;j<=5;j++)m[NR,j]=$j}END{sym=1;for(i=1;i<=5;i++){for(j=1;j<=5;j++){if(m[i,j]!=m[j,i]){sym=0;break}}if(!sym)break}print sym?"Symmetric":"Not symmetric"}'

awkで解いていただきました。

問4 : k-means法

問題内容

k-means法をシェル芸で実装してみます。

私の回答(小問1)

cat S*/*64/nums.0 | sort -k2 | awk '{a[$2]+=$1;b[$2]+=1}END{for(i=0;i<length(a);i++)printf("%d %lf\n",i,a[i]/b[i])}' > mean.1

私の感想(小問1)

awkが便利すぎますね。 ファイルの中身をsortを使ってクラスターごとに並べ替えます。 それからawkでクラスターごとの合計を配列に入れて最後に平均をとっています。 一次元配列で済む問題ならawkで十分戦える気がします。 もっとawkを勉強していきたい気持ちです。 勉強になりました。

小問2はワンライナーではしんどくなったので解説を見てなるほどなぁ~と感心していました。 あとChatGPTにBashのワンライナーでk-means法の実装をお願いしたところ、 「PythonやRを使ってください」と怒られてしまいました。すみません。

問5 : オイラーのφ関数

問題内容

オイラーのφ関数をBashのワンライナーで実装します。

私の回答

n=8;echo $n | factor | sed 's/.*://g;s/ /\n/g' | uniq | awk -F, -v N=$n 'BEGIN{a=N}NR1>{a*=(1-1/$1)}END{printf("%lf\n",a)}'

私の感想

オイラーのφ関数というものを初めて知りました。 awkにBash側の変数が渡せなくて困っていましたが、Fオプション付けたらなんとかなりました。 以下の記事を参考にさせていただきました。 ありがとうございます。 こんなに単純な関数なのに互いに素な自然数の個数が出てくるのは不思議ですね。 数学の神秘を体感しました。 勉強になりました。 楽しかったです。

参考 : awkの中でシェルで作成した変数を扱う

ChatGPT(GPT-4)の回答

n=8; phi_n=0; gcd () { [ $2 -eq 0 ] && echo $1 || gcd $2 $(($1 % $2)); }; for ((i=1; i<=n; i++)); do if [[ $(gcd $i $n) == 1 ]]; then ((phi_n++)); fi; done; echo $phi_n

gcd関数から実装していただけました。

問6 : 互いに素な自然数の列挙

問題内容

「seq 100 |」から始めて、各行の数字に対して、互いに素な自然数を列挙します。

私の回答

seq 100 | awk 'function gcd(a,b){while(b!=0){r=a%b;a=b;b=r}return a}{printf("%d : ",$1);for(i=1;i<=$1;i++){if(gcd(i,$1)== "1"){printf("%d ",i)}};printf("\n")}'

私の感想

gcd関数(最大公約数を求める関数)はChatGPTに実装していただきました。 互いに素な自然数がまだあまり理解できていませんが、 とりあえず解けたので良しとしましょう。 それにしてもChatGPTは便利ですね。 ある程度の無理を言っても許してくれる懐の深さも備えています。 最大公約数が1なら互いに素であると言えるということだけは理解できました(おそらく)。 勉強になりました。ありがとうございます。

おわりに

第64回シェル芸勉強会についてメモを書きました。 シェル芸の得手不得手が少し理解できた気がします。 また、今まで知らなかったコマンドにも出会えたので良かったです。 ChatGPTにもご協力いただいてとても勉強になりました。 次回も参加させていただきたいと思います。 一度くらいは現地参加もしてみたいですね。 ということで、誠にありがとうございました。 またよろしくお願いいたします。 次回も楽しみにしています。

お知らせ

シェル芸オンラインジャッジ

加藤祐介ブログはMITライセンスで公開されています。
Copyright Since 2023 (C) YusukeKato All Rights Reserved.