〜2016年2月下旬〜
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つしかない。
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/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でタブってのは気づいてなかった。
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
結構速いが一時ファイルを作らないといけないのが難点。
volume containerなんて使ってなかった頃は問答無用でdocker ps -aでよかったが、
区別して消さないといけなくなった。
STATUSを見ればCreatedになるのでawkで抜き出せばよさげだが、
docker psの-fオプションでフィルターできるのであった。
% docker ps -qa -f status=exited | xargs -r docker rm
終了してるのだけ消したければこれでいい。
この件が解決した。単にuim-skkの設定で注釈のカスタムフィルタを適当にTCPサーバにすればいいだけだった。
そんなサーバは動いてないがメッセージは出なくなった。
というよりここでパイプにしてたのがいけなかったようで、
たぶん触ってるうちに手違いがあったんだろう。
-aで出てくる<none>:<none>消しちゃいけないimageもあるので安易に消せないが、
-f dangling=trueしたものはどこに関連してないimageなので消しても問題ない。
% docker images -qf dangling=true | xargs -r docker rmi
でok。xargs -rはそんなimageが1つもないときにはdocker rmiを実行しないという意味。
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も同様。
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
というわけで改行と空白を全部消してみるのがいいかもしれない。