Just another Ruby porter,

〜2016年2月下旬〜


<Older(,) | Newer(.)> | Recent(/)>> | RDF

2016-02-21 (Sun)

Project Euler Problem 34 #シェル芸

Digit factorials. 桁の階乗。 まずは最大を。9!*7=2540160なので7桁を超えることはない。
1から9の階乗は連想配列に入れておく。

% seq 10 2540160|awk 'BEGIN{f[0]=1;for(i=1;i<=9;i++)f[i]=i*f[i-1]}{s=0;for(j=1;j<=NF;j++)s+=f[$j];if(s==$0)t+=$0}END{print t}' FS= 
40730

ひねりなし。gawkで5秒程度。実は145と40585の2つしかない。


2016-02-22 (Mon)

Project Euler Problem 35 #シェル芸

Circular primes. 巡回素数。たとえば197を巡回させると197,971,719と全部素数になる。
100万未満の巡回素数は何個あるか。単純な力技だとすごく時間がかかりそうだ。
というわけでまずは候補をしぼる。
まずは偶数や5が含まれていると1の位に来たときに素数ではなくなるので除外できる。
(ただし、2と5も除外されちゃうので最後に2を足す必要あり。)

% TIMEFMT='%U user %S system %P cpu %*E total'
% time (seq 1 999999|grep -v '[024568]'|wc -l)
5460
0.06s user 0.00s system 124% cpu 0.048 total

かなり減った。時間も速い。そもそも最初から奇数だけ扱えばいいのでseqで奇数指定する。

% time (seq 1 2 999999|grep -v '[024568]'|wc -l)
5460
0.31s user 0.00s system 111% cpu 0.280 total

なぜか遅くなった。佐藤さんの調査によるとGNU coreutils seqは +1だけ最適化してるという話で、なかなか興味深い。
それはそれとしてfactorとawkで素数を抜き出す。

% seq 1 999999|grep -v '[024568]'|factor|awk 'NF==2'|wc -l
1111

候補は1111個とかなり扱いやすい個数になった。
あとはawkで巡回させて素数かどうか判断するという話になるが、
実は判断しなくてもわかる。
たとえば197だったら巡回させた3個とも1111個の中に含まれているので、
お互いそれぞれ巡回させれば3個ずつ出てくる。
uniq -cすれば3個つまり桁数分だけ見つかる。
19だと91は素数ではないので1111個の中にはなく、
unic -cしても1個つまり桁数とは違う数になる。
これを利用して巡回素数を見つける。

% time (seq 999999|grep -v '[024568]'|factor|\
  awk 'NF==2{for(i=1;i<=length($2);i++){print substr($2,i+1)substr($2,1,i)}}'|\
  sort -n|uniq -c|awk '$1==length($2)'|wc -l)
53
0.05s user 0.03s system 151% cpu 0.053 total

+2で55個。結構速い。


2016-02-23 (Tue)

月の最初と最後 #シェル芸

2016/01/01<タブ>2016/01/31という形式で1年分出力するというシェル芸tweetが先週ぐらいにあって、
いろいろ考えてみた。

月末は今月の1日の1ヶ月後の前日という表現にすればdateがいい具合に受け付けてくれる。
つまり2016-02-01 month -1dayというような感じ。
あとはコピーして日付を01にしたものを用意すればいいのでsedで変換。

% seq -f '2016-%02g-01 month -1day' 12|date -f- +%Y/%m/%d|sed 'h;s/..$/01/;G;s/\n/\t/'

awkでも同じようなもん。

% seq -f '2016-%02g-01 month -1day' 12|date -f- +%Y/%m/%d|awk -F/ '$0=$1"/"$2"/01\t"$0'

別の考え方として最初から1日と月末を用意してしまう方法。
2行に分かれてしまうのでpasteで1行に。pasteはうまい具合にタブ区切りになる。

% seq -f '2016-%02g-01' 12|sed 'h;G;s/$/ month -1day/'|date -f- +%Y/%m/%d|paste - -

ここまでは先週思い付いた解で、ふと今日別解が降りてきた。
1日は固定なんだからdateのフォーマットでもう1個作っちゃえばいいんだった。

% seq -f '2016-%02g-01 month -1day' 12|date -f- +'%Y/%m/01%t%Y/%m/%d'                 
2016/01/01	2016/01/31
2016/02/01	2016/02/29
2016/03/01	2016/03/31
2016/04/01	2016/04/30
2016/05/01	2016/05/31
2016/06/01	2016/06/30
2016/07/01	2016/07/31
2016/08/01	2016/08/31
2016/09/01	2016/09/30
2016/10/01	2016/10/31
2016/11/01	2016/11/30
2016/12/01	2016/12/31

%tでタブってのは気づいてなかった。


2016-02-24 (Wed)

Project Euler Problem 36 #シェル芸

Double-base palindromes. 二種類の基数による回文数。
回文はpasteとrevを使うと比較的簡単にチェックできる。

% paste <(seq 12320 12330) <(seq 12320 12330|rev)|awk '$1==$2'
12321	12321

10進の回文を抜き出しbcで2進に変換。
同じようにpasteとrevで回文を抜き出しbcで10進に戻し総和を求める。

% TIMEFMT='%U user %S system %P cpu %*E total'
% time (paste <(seq 999999) <(seq 999999|rev) | awk '$1==$2{print "obase=2;",$1}' | bc > tmp.txt)
0.90s user 0.26s system 193% cpu 0.597 total
% time (paste tmp.txt <(rev tmp.txt) | awk '$1==$2{print "ibase=2;",$1}'|bc|jq -s add)            
872187
0.01s user 0.00s system 131% cpu 0.009 total

結構速いが一時ファイルを作らないといけないのが難点。


2016-02-25 (Thu)

volume container以外のcontainerを消す

volume containerなんて使ってなかった頃は問答無用でdocker ps -aでよかったが、
区別して消さないといけなくなった。
STATUSを見ればCreatedになるのでawkで抜き出せばよさげだが、
docker psの-fオプションでフィルターできるのであった。

% docker ps -qa -f status=exited | xargs -r docker rm

終了してるのだけ消したければこれでいい。


2016-02-26 (Fri)

libuim: [fatal] cannot execute /path/of/filter-programを出さなくする

この件が解決した。単にuim-skkの設定で注釈のカスタムフィルタを適当にTCPサーバにすればいいだけだった。
そんなサーバは動いてないがメッセージは出なくなった。
というよりここでパイプにしてたのがいけなかったようで、
たぶん触ってるうちに手違いがあったんだろう。


2016-02-27 (Sat)

dockerで不要なimageを消す

-aで出てくる<none>:<none>消しちゃいけないimageもあるので安易に消せないが、
-f dangling=trueしたものはどこに関連してないimageなので消しても問題ない。

% docker images -qf dangling=true | xargs -r docker rmi

でok。xargs -rはそんなimageが1つもないときにはdocker rmiを実行しないという意味。


2016-02-28 (Sun)

DockerfileのCMDの罠

CMDには配列と文字列(shell form)の2つの表記が存在するが、
shell formにするとsh -c経由で起動されるためdocker stopで嵌る。
たとえばDockerfileのCMD指定で

CMD /script.sh

のように書いてbuildしたイメージをdocker runすると、
実際はsh -c /script.shと実行される。これがPID 1になる。
一方docker stopはPID 1にSIGTERMを送るのでこのshが受け取る。
つまりその子プロセスであるシェルスクリプトにはSIGTERMが届かない。
しばらくしてdocker stopからSIGKILLが送られてきてそれで死ぬことになる。
これでは後始末できない。
回避するには

CMD ["/script.sh"]

と書けばいい。これならsh -cを経由しないのでシェルスクリプトの実行がPID 1になる。
sh -c /script.shではなくsh /script.shと実行される。

ENTRYPOINTも同様。


2016-02-29 (Mon)

閏年の判断をcalとgrepで

2/29ということで。先日 閏年の判断をdateでやってみたが、calでもできなくはない。

% cal 2 2016
   February 2016      
Su Mo Tu We Th Fr Sa  
    1  2  3  4  5  6  
 7  8  9 10 11 12 13  
14 15 16 17 18 19 20  
21 22 23 24 25 26 27  
28 29                 
                      

grep '29$'でいけそうだが、実はcalコマンドって目に見えない余計な空白が周りにあったりする。

% cal 2 2016 | tr ' ' -
---February-2016------
Su-Mo-Tu-We-Th-Fr-Sa--
----1--2--3--4--5--6--
-7--8--9-10-11-12-13--
14-15-16-17-18-19-20--
21-22-23-24-25-26-27--
28-29-----------------
----------------------

つまりgrep ' 29 'としないといけない。
grep 29でもいいと思うかもしれないが、2029年になると困る。
あ、' 29 'でも西暦29年で困るな。

% cal 2 2016 | tr -d '\n ' | grep '29$'
February2016SuMoTuWeThFrSa1234567891011121314151617181920212223242526272829

というわけで改行と空白を全部消してみるのがいいかもしれない。


<Older(,) | Newer(.)> | Recent(/)>> | RDF


WWW を検索 jarp.does.notwork.org を検索

わたなべひろふみ
Key fingerprint = C456 1350 085F A320 C6C8 8A36 0F15 9B2E EB12 3885
Valid HTML 4.01!