「第14回東京居残りシェル芸勉強会&第32回蟹ではなくピザが出るUSP友の会定例会」の問題をなるべく解きました
当日は用事があり参戦できなかったので、ブログ参戦です!
TLをネタバレしない程度に覗いてみましたが、今回の問題はいつもにも増して頭がおかしいとレベルだとの評判で、なかなか楽しみであります!
各種リンク
参加環境
Jail on FreeBSD 10.1 amd64 on VMWare Player on WIndows7 64bit
ではではスタート!
Q1 100!を計算してください。正確に。
jotとbcを使って
% jot -s \* 100 | bc 93326215443944152681699238856266700490715968264381621468592963895217\ 59999322991560894146397615651828625369792082722375825118521091686400\ 0000000000000000000000
もしくは
% jot 100 | awk 'BEGIN{a=1}{a = a * $1}END{print a}' 9.33262154439441021883256061086e+157
こんな感じでしょうか。
Q2 次のseqからsed(と言ってもgsed)だけでfizzbuzzを完成させてください。
なんぞこの問題www
% jot 100 | sed -e 'n;n;s/.*/Fizz/' | sed -e 'n;n;n;n;s/.*/Buzz/' | sed -e 'n;n;n;n;n;n;n;n;n;n;n;n;n;n;s/.*/FizzBuzz/' 1 2 Fizz 4 Buzz Fizz 7 8 Fizz Buzz 11 Fizz 13 14 FizzBuzz (以下略)
※TLカンニングしました><
sedコマンド、s以外にもあって興味深い・・・
Q3 このうち素数はどれでしょうか?
0xaf 0x13 0x0d 0x24 0x58
どうもpkgngにfactorコマンドが見つからなかったので、awkで無理やり計算しました。
% echo 0xaf 0x13 0x0d 0x24 0x58 | tr ' ' '\n' | awk 'function div(x,y){if(x==y||x%y==0){return y}else{return div(x, y+1)}}{n=sprintf("%d",$1);if($1==div(n, 2)){print $1, "OK"}else{print $1, "NG"}}' 0xaf NG 0x13 OK 0x0d OK 0x24 NG 0x58 NG
「何かで割れたら素数じゃない」っていう考え方で、力技です。
Q4 次の16進数(UTF-8)で書かれたメッセージを復元してください。
e89fb9e3818ce9a39fe381b9e3819fe38184
% echo e89fb9e3818ce9a39fe381b9e3819fe38184 | xxd -p -r 蟹が食べたい
※TLカンニングしました><
なんぞこれwwww
これならperlでもいけそう。
% echo e89fb9e3818ce9a39fe381b9e3819fe38184 | perl -nle 'print pack("H*", $_)' 蟹が食べたい
なるほどー。
Q5 次のようなファイルを作ってください。
(catするとahoとだけ出て、容量は1GB。)
ueda@remote:~$ cat hoge
aho
ueda@remote:~$ ls -l hoge
- rw-r--r-- 1 ueda ueda 1000000000 12月 7 14:53 hoge
ナニソレイミワカンナイですよもう!!
TLコッソリ見てみると、ddでNULLぶっこんだらいけるみたい???
% echo aho>hoge;dd bs=`expr 1000 - 4` count=1 </dev/zero>>hoge
(1GBはきついので、1000バイトにしときました)
……文字列終端だから大丈夫ってわけね!
ちなみにxxdしてみると
% xxd hoge 0000000: 6168 6f0a 0000 0000 0000 0000 0000 0000 aho............. 0000010: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 0000020: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 0000030: 0000 0000 0000 0000 0000 0000 0000 0000 ................ (中略) 00003c0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 00003d0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 00003e0: 0000 0000 0000 0000 ........
こうなっていて、なるほどって感じです。
Q6 日本の山を標高の高い順から並べていってください。順位と標高も一緒に出力してください。
(こちら(日本の山一覧 (高さ順) - Wikipedia)からcurlで持ってきて加工してください)
→ ただの作業になってきたので省略。
Q7 分数で正確に答えを求めてください。できれば約分してください。
echo '1/4 + 2/5 + 7/16 - 5/9'
とりあえずbcに
% echo '1/4 + 2/5 + 7/16 - 5/9' | bc 0
orz
awkでがんばる
% echo '1/4 + 2/5 + 7/16 - 5/9' | gsed 's%^%+,%' | gsed 's/ [+-]/\n&,/g' | tr -d ' ' | awk -F"[/,]" 'BEGIN{n=1}{n=n*$3;a[NR]=$0}END{for(i=1;i<=NR;i++){print a[i] "," n}}' | awk -F"[/,]" '{if($1=="+"){n=n+$NF/$3*$2}else{n=n-$NF/$3*$2}}END{print n "/" $NF}' | awk -F"/" 'END{for(i=$2;i>1;i--){if($1%i==0 && $2%i==0){$1=$1/i;$2=$2/i}};print $1"/"$2}' 383/720
ざっくりとした解説
①まず符号と、全ての分母の積を求めます。
% echo '1/4 + 2/5 + 7/16 - 5/9' | gsed 's%^%+,%' | gsed 's/ [+-]/\n&,/g' | tr -d ' ' | awk -F"[/,]" 'BEGIN{n=1}{n=n*$3;a[NR]=$0}END{for(i=1;i<=NR;i++){print a[i] "," n}}' +,1/4,2880 +,2/5,2880 +,7/16,2880 -,5/9,2880
②①で求めた分母をもとに通分して足し引きをします。
(前略) | awk -F"[/,]" '{if($1=="+"){n=n+$NF/$3*$2}else{n=n-$NF/$3*$2}}END{print n "/" $NF}' 1532/2880
③割られる数を--1しながら両方を割り算して、力技で約分します。
(前略) | awk -F"/" 'END{for(i=$2;i>1;i--){if($1%i==0 && $2%i==0){$1=$1/i;$2=$2/i}};print $1"/"$2}' 383/720
こんな感じです
Q8 をポキポキ折ってください。
****************************************************************
なんですかねぇこれは・・・(困惑
awkで頑張ってみましょう。
% echo '*****************************************************************' | grep -o . | awk '{b=int(rand()*10>7);if(b==0){printf}else{print}}' | awk 'BEGIN{sum=0}{print sum,$0;sum=sum+length-1}' | awk 'NR==1{print $2}NR>1{printf "%"$1"c", " ";print $2}' **** ****** * ******** ** *** ***** ** ******** * ** ****** ** ** *** ** * * ** ****
ざっくりとした解説
①grep -o で横にしてから、awkのrand()関数を使って適当に分割します。
% echo '*****************************************************************' | grep -o . | awk '{b=int(rand()*10>7);if(b==0){printf}else{print}}' **** ****** * ******** ** *** ***** ** ******** * ** ****** ** ** *** ** * * ** ****
②その横に、累計の列数(出力時の横オフセット)を出力します。
(前略) | awk 'BEGIN{sum=0}{print sum,$0;sum=sum+length-1}' 0 **** 3 ****** 8 * 8 ******** 15 ** 16 *** 18 ***** 22 ** 23 ******** 30 * 30 ** 31 ****** 36 ** 37 ** 38 *** 40 ** 41 * 41 * 41 ** 42 ****
③②で求めたオフセットに従って空白を出力し、文字列を出力します。
(前略) | awk 'NR==1{print $2}NR>1{printf "%"$1"c", " ";print $2}' **** ****** * ******** ** *** ***** ** ******** * ** ****** ** ** *** ** * * ** ****
以上、お疲れ様でしたっ!!
まとめ
sedスクリプトおいしいれす