bashとclispとawkで小学三年生レベルの等差数列の総和

■bashとclispとawkで小学三年生レベルの等差数列の総和

 フィボナッチ数列で知られるレオナルドの算盤の書にある。
 ガウスは小学校三年生で再発見したとされている。
 よって、タイトルは釣りです。

 等差数列
 http://ja.wikipedia.org/wiki/%E7%AD%89%E5%B7%AE%E6%95%B0%E5%88%97

■初項1、項差1、項数100のbash+awk文。

$ echo "`seq 1 1 100`" | \
  awk '{sum+=$1;printf "%d+",$1};END{print "\n"sum}' | \
  sed s/"+\$"/"="/ | nkf -f70
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+30+31+32+33+34+35+36+37+38+39+40+41+42+43+44+45+46+47+48+49+50+51+
52+53+54+55+56+57+58+59+60+61+62+63+64+65+66+67+68+69+70+71+72+73+74+75+
76+77+78+79+80+81+82+83+84+85+86+87+88+89+90+91+92+93+94+95+96+97+98+99+
100= 5050

■上記をclispに渡すように書くと以下になる。

$ echo "(+ `seq 1 100`)" | clisp -q
[1]>
5050

■ガウスの解法の考え方が見えるように書くと。。。
 ※50100以外は2組で100。

$ for n in "`seq 1 49`";do \
    echo "$n" | awk '{print $1"+"100-$1"="$1+(100-$1)}'; \
  done | \
  awk -F\= '{sum+=$2;print $0}END{print sum+50+100}' | \
  column | \
  sed s/"\t\([0-9]*\$\)"/"\n\n\1"/
1+99=100        11+89=100       21+79=100       31+69=100       41+59=100
2+98=100        12+88=100       22+78=100       32+68=100       42+58=100
3+97=100        13+87=100       23+77=100       33+67=100       43+57=100
4+96=100        14+86=100       24+76=100       34+66=100       44+56=100
5+95=100        15+85=100       25+75=100       35+65=100       45+55=100
6+94=100        16+84=100       26+74=100       36+64=100       46+54=100
7+93=100        17+83=100       27+73=100       37+63=100       47+53=100
8+92=100        18+82=100       28+72=100       38+62=100       48+52=100
9+91=100        19+81=100       29+71=100       39+61=100       49+51=100
10+90=100       20+80=100       30+70=100       40+60=100

5050

■上記をclispに渡すように書くと。

$ for n in `seq 1 49`;do echo "$n $((100-$n))" ;done | \
    xargs echo -n | \
    sed s/"^"/"(+ "/ | \
    sed s/"\$"/" 50 100)"/ | \
  clisp -q
[1]>
5050

■上記の考えのまま、公式にすると、以下のようになる。
 (n/2-1)*n+n+(n/2)

$ echo "100" | awk '{print ((($1/2)-1)*$1)+$1+($1/2)}'
5050

■clispに渡すように書くと以下のように素敵なことになる。
 括弧を見ただけで、計算順序のレイヤーを頭の中に描ける人はかなりの変態です。

$ echo "(+ (+ (* (- (/ 100 2) 1) 100 ) 100) (/ 100 2))" | clisp -q
[1]>
5050

■また、以下のようにもまとめられます。
 (n/2)*n+(n/2) 

$ echo "100" | awk '{print (($1/2)*$1)+($1/2)}'
5050

■clispでは共通項が見やすい。

$ echo "(+ (* (/ 100 2) 100) (/ 100 2))" | clisp -q
[1]>
5050

■もう少しまとめると暗算が簡単になる。
 (n+1)(n/2)

$ echo "100" | awk '{print (($1+1)*$1)/2}'
5050

■clispでも格段に読みやすくなりました。

$ echo "(/ (* (+ 100 1) 100) 2)" | clisp -q
[1]>
5050

■項数=n項なので公式に入れるのはそれほど難しくありません。
 コメント行はシェルが無視してくれます。

$ echo "a1 d n" | 
  #初項
  sed s/"a1"/"1"/ | \
  #項差
  sed s/"d"/"1"/ | \
  #項数=n項
  sed s/"n"/"100"/g | \
  awk '{print (($3*((2*$1)+(($3-1)*$2))))/2}'
5050

■clispの場合も同じです。

$ echo "(/ (* n (+ (* 2 a1) (* (- n 1) d))) 2)" | \
  #初項
  sed s/"a1"/"1"/ | \
  #項差
  sed s/"d"/"1"/ | \
  #項数=n項
  sed s/"n"/"100"/g | \
  clisp -q
[1]>
5050

■変更したいsed行を修正します。
 以下はn項までの数を1000にしています。

$ echo "a1 d n" | \
   sed s/"a1"/"1"/ | \
  sed s/"d"/"1"/ | \
  sed s/"n"/"1000"/g | \
  awk '{print (($3*((2*$1)+(($3-1)*$2))))/2}'
500500

$ echo "`seq 1 1000`" | awk '{sum+=$1}END{print sum}'
500500

$ echo "(/ (* n (+ (* 2 a1) (* (- n 1) d))) 2)" | \
  sed s/"a1"/"1"/ | \
  sed s/"d"/"1"/ | \
  sed s/"n"/"1000"/g | \
  clisp -q
[1]>
500500

■以下は項数で求める公式です。
 項差が変わる場合、項数=n項では無くなり、
 seqに渡すには、項数に応じたn項の数が必要になります。

$ echo "a1 d n" | \
  sed s/"a1"/"1"/ | \
  sed s/"d"/"2"/ | \
  sed s/"n"/"1000"/g | \
  awk '{print (($3*((2*$1)+(($3-1)*$2))))/2}'
1000000

$ echo "(/ (* n (+ (* 2 a1) (* (- n 1) d))) 2)" | \
  sed s/"a1"/"1"/ | \
  sed s/"d"/"2"/ | \
  sed s/"n"/"1000"/g | \
  clisp -q
[1]>
1000000

■n項までの数を元に、初項+項差*(n項までの数-1)でn項が求まります。

$ echo "1 2 1000" | awk '{print $1,$2,$1+($2*($3-1))}' | seq `xargs` | wc -l
1000

$ echo "1 2 1000" | awk '{print $1,$2,$1+($2*($3-1))}' | seq `xargs` | tail -1
1999

$ echo "1 2 1000" | awk '{print $1,$2,$1+($2*($3-1))}' | seq `xargs` | awk '{sum+=$1}END{print sum}'
1000000

■clispの方が直感的だったりします。

$ echo "(+ 1 (* 2 (- 1000 1)))" | clisp -q
[1]>
1999

■bashとawkとclispは仲良しです。

$ echo "初項=1、項差=2、項数=1000"; \
  echo "(+ 1 (* 2 (- 1000 1)))" | \
  clisp -q | \
  tail -1 | \
  seq 1 2 `xargs` | \
  sed s/" "/"\n"/g | \
  awk '{sum+=$1}END{print "等差数列の総数:"sum}'
初項=1、項差=2、項数=1000
等差数列の総数:10000001から100までの間の偶数の総和。
 項数は半分になるのは分かりますよね。。。

$ echo "初項=2、項差=2、項数=50"; \
  echo "(+ 2 (* 2 (- 50 1)))" | \
  clisp -q | \
  tail -1 | \
  seq 2 2 `xargs` | \
  sed s/" "/"\n"/g | \
  awk '{sum+=$1}END{print "等差数列の総数:"sum}'
初項=2、項差=2、項数=50
等差数列の総数:2550

■n番目の項が分かっているので、以下でもかまいません。

$ echo "`seq 2 2 100`" | awk '{sum+=$1}END{print sum}'
2550

■公式をそのまま使うと以下の通りです。

$ echo "a1 d n" | \
  sed s/"a1"/"2"/ | \
  sed s/"d"/"2"/ | \
  sed s/"n"/"50"/g | \
  awk '{print (($3*((2*$1)+(($3-1)*$2))))/2}'
2550

$ echo "(/ (* n (+ (* 2 a1) (* (- n 1) d))) 2)" | \
  sed s/"a1"/"2"/ | \
  sed s/"d"/"2"/ | \
  sed s/"n"/"50"/g | \
  clisp -q
[1]>
2550