くんすとの備忘録

IT系技術メモ

移転しました。

15秒後に自動的にリダイレクトします。

「第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スクリプトおいしいれす