【問題と解答】jus共催 第46回人類はおそらくシェル芸に仕事を奪われるか奪われないかのどちらかであるシェル芸勉強会
Sat Feb 15 16:53:29 JST 2020 (modified: Sat Feb 15 16:53:29 JST 2020)
views: 2113, keywords:プログラミング,勉強会,シェル芸,シェル芸勉強会 この記事は最終更新日が4年以上前のものです。
- 問題で使われているデータファイルはGitHubにあります。クローンは以下のようにお願いします。
git clone https://github.com/ryuichiueda/ShellGeiData.git $
- 環境: 解答例はUbuntu Linux 19.10 で作成。Macの場合はcoreutilsをインストールすると、GNUのコマンドが使えます。BSD系の人は玄人なので各自対応のこと。
- 出典
Q1
次のようなデータがあります。
$ cat data
32 -12 42 -4 3 34 32 9 22 24 25 19 18 -14 -4
これを、次のように1列目に十の位、2列目に一の位の数を並べた表現(幹葉表示)に変換してください。
-0 44
0 39
4 2
-1 24
3 224
2 245
1 89
解答例
$ cat data | tr ' ' \\n | sed 's/.$/ &/' | awk 'NF==1{print 0,$1}NF!=1'
| sed 's/^- /-0 /' | sort -n
| awk '{a[$1]=a[$1]$2}END{for(k in a)print k,a[k]}' | sed 's/^. / &/'
-0 44
0 39
4 2
-1 24
3 224
2 245
1 89
Q2
次のdata2
は、1列目のデータに対して2列目にチェック用のビットを書き込んであるファイルです。 2列目の左右のビットがそれぞれ1列目の0と1の数の偶奇を表しており、 偶数なら0、奇数なら1になっています。1列目と2列目の整合性がとれていない行の番号を出力してください。
$ cat data2
0101010110101010101101010101101010101 10
0100000000010101010110101010101011101010 11
101011010101011010101010101010101010100101 00
010110101010111111111110101010101110 01
011010101010110101010101010101010 01
解答例
$ cat data2 | awk '{print gsub(/0/,"",$1)%2 gsub(/1/,"",$1)%2,$2}' | awk '$1!=$2{print NR}'
4
Q3
次のファイルについて、1〜3列目の3行、4〜6列目の3行をそれぞれ3x3行列とみなして掛け算してください。
$ cat matrix
3 4 -2 1 -9 4
3 -1 2 3 -2 -8
2 5 6 0 2 -3
解答例
$ awk 'NR<4{for(i=1;i<=3;i++)a[NR][i]=$(i+3)}NR>=4{for(i=1;i<=3;i++)
{printf("%d ",$1*a[1][i] + $2*a[2][i] + $3*a[3][i])}print ""}' matrix matrix
15 -39 -14
0 -21 14
17 -16 -50
Q4
A, B, C, Dの4種類の文字を2つにグループ分けするときの全通りのパターンを列挙してください。各グループかならず一つの文字が入っている(空集合を認めない)こととします。
解答例
$ echo {A..D}{A..D}{A..D}{A..D} | tr ' ' \\n | grep -Ev '(.).*\1'
| awk '{for(i=1;i<4;i++){print substr($1,1,i), substr($1,i+1)}}' | tr ' ' \\n
| awk 'BEGIN{a[1]="A";a[2]="B";a[3]="C";a[4]="D"}{for(i=1;i<=4;i++)if(index($1,a[i])){printf a[i]}print ""}'
| xargs -n 2 | sort -u | awk '{print ($1~/A/) ? $1" "$2 : $2" "$1}' | sort -u
A BCD
AB CD
ABC D
ABD C
AC BD
ACD B
AD BC
$ echo {A..D}{A..D}{A..D}{A..D} | tr ' ' \\n | grep -Ev '(.).*\1'
| awk '{for(i=1;i<4;i++){print substr($1,1,i), substr($1,i+1)}}'
| opy '["".join(sorted(x)) for x in F[1:]]' | opy '[sorted(F[1:])]' | sort -u
['A', 'BCD']
['AB', 'CD']
['ABC', 'D']
['ABD', 'C']
['AC', 'BD']
['ACD', 'B']
['AD', 'BC']
Q5
次のような出力(パスカルの三角形)を得てください。完全に左右対称である必要はありませんが、なるべく左右対称にしてください。
1
1 1
1 2 1
1 3 3 1
1 4 6 4 1
1 5 10 10 5 1
1 6 15 20 15 6 1
1 7 21 35 35 21 7 1
1 8 28 56 70 56 28 8 1
1 9 36 84 126 126 84 36 9 1
解答例
$ n="0 1 0" ; for i in {1..10} ; do echo $n ;
n="0 "$(awk '{for(i=1;i<=NF-1;i++)printf $i+$(i+1)" "; print ""}' <<< $n)" 0 " ;
done | sed 's/^0 //;s/ 0$//' | tac
| awk 'NR==1{a=length($0)}{d=(a-length($0))/2;for(i=1;i<d;i++)printf " ";print}' | tac
1
1 1
1 2 1
1 3 3 1
1 4 6 4 1
1 5 10 10 5 1
1 6 15 20 15 6 1
1 7 21 35 35 21 7 1
1 8 28 56 70 56 28 8 1
1 9 36 84 126 126 84 36 9 1
Q6
次のファイル内のそれぞれ数字について、上の桁の数からそれぞれ10, 9, 8, ..., 1をかけて総和を求めましょう。例えば4065170060なら、4*10 + 0*9 + 6*8 + ... + 0*1
を計算してください。さらに、それぞれの行で求めた数について、最大公約数を求めてください。
$ cat isbn
4065170060
4774173444
4822239292
4048930699
4839952981
4839924015
解答例
### 総和 ###
$ cat isbn | rev | awk -F "" '{for(i=1;i<=NF;i++)a+=$i*i;print a;a=0}'
176
264
231
242
330
286
### 最大公約数 ###
$ cat isbn | rev | sed 's/./& /g'
| awk '{for(i=1;i<=NF;i++)a+=$i*i;print a;a=0}'
| python3 -c 'import sys;import numpy as np;a=[int(x) for x in sys.stdin];print(np.gcd.reduce(a))'
11
Q7
41人を円形に並べ、1, 2, 3, 4, ..., 41番と番号をつけます。そして、生き残っている3人ごと(最初は3番, 6番, 9番, ...)の順に殺していきます。最後に残る二人は何番になるか、ワンライナーで答えてください。(ヨセフスの問題)
解答例
$ echo {1..41} | awk '{for(i=1;i<=3*39;i+=3){$(NF+1)=$i;$(NF+1)=$(i+1);print}}' | tail -n 1 | awk '{print $(NF-1),$NF}'
16 31
Q8
SEND + MORE = MONEY
の各アルファベットに0から9の数字1字を割り当てて足し算を完成させてください。異なるアルファベッドには異なる数字を割り当ててください。S
、M
には0を割り当てないでください。答えが分かるものであれば出力はなんでも構いません。
解答例
$ seq -w 90123456 99999999 | grep '^....1' | grep -vE '(.).*\1' | awk -F "" '{print $1$2$3$4,$5$6$7$2,$5$6$3$2$8}' | awk '$1+$2==$3'
9567 1085 10652
### ベタにやった場合 ###
$ time seq -w 10234567 99999999 | grep -Ev '(.).*\1' | awk -F "" '{print $1$2$3$4,$5$6$7$2,$5$6$3$2$8}' | grep -v '^0' | grep -v ' 0' | awk '$1+$2==$3'
9567 1085 10652
real 3m27.610s
user 4m11.102s
sys 0m0.662s
### grepを分散させるとCPUが多い場合は速くなる ###
$ time seq -w 10234567 99999999 | grep -Ev '(.)\1' | grep -Ev '(.).\1' | grep -Ev '(.)..+\1' | awk -F "" '{print $1$2$3$4,$5$6$7$2,$5$6$3$2$8}' | grep -v '^0' | grep -v ' 0' | awk '$1+$2==$3'
9567 1085 10652
real 2m26.417s
user 6m13.170s
sys 0m2.952s