Thursday, April 27, 2006

sum/avg/max/min/percent of column(s)

#! /bin/sh
#
# Name: 
#   colmath.sh
# 
# Description: 
#   Calculate the average/sum/min/max/percent of column
#   position(s), default to column 1 if not given
#   Usage: colmath.sh <file> <avg|max|min|percent|sum> [col] ...
#
# Written by: 
#   Chi Hung Chan, chihung@singnet.com.sg
#
# Examples:
#    $ ./colmath.sh some.log sum 7 9 11 13
#    36 66 164 5364
#    $ ./colmath.sh some.log percent 7 9 11 13
#    0.64 1.17 2.91 95.28
#    $ ./colmath.sh some.log max 7 9 11 13
#    2 2 2 52
#    $ ./colmath.sh some.log min 7 9 11 13
#    0 0 0 0


if [ $# -eq 0 ]; then
 echo "Usage: $0 <file> <avg|max|min|percent|sum> [col] ..." 1>&2
 exit 1
fi


# check file existence
file=$1
if [ ! -f $file ]; then
 echo "Error. cannot find $file" 1>&2
 exit 2
fi
shift


#
# validate action
#
action="$1"
case $action in
 sum|avg|min|max|percent)
  ;;
 *)
  echo "Error. Invalid action \"$action\"" 1>&1
  exit 3
  ;;
esac
shift


if [ $# -eq 0 ]; then
 args="1"
else 
 args="$@"
fi


#
# calculate percentage
# run itself to get the sum for each column and calculate the percentage
#
if [ "$action" = "percent" ]; then
 $0 $file sum $args | awk '
 {
  for ( i=1 ; i<=NF ; ++i ) {
   sum+=$i
   data[i]=$i
  }
 } 
 END{
  for ( i=1 ; i<=NF ; ++i ) {
   printf("%.2f ",100*data[i]/sum)
  }
 }'
 exit
fi



for i in $args
do
 case $action in
 sum)
  awk '
  {
   sum+=$'$i'
  } 
  END {
   printf("%d ",sum)
  }' $file
  ;;
 avg)
  awk '
  {
   sum+=$'$i'
  } 
  END {
   printf("%f ",sum/NR)
  }' $file
  ;;
 min)
  awk '
  BEGIN {
   min=sprintf("%d",2^32-1)
  }
  {
   if ( min > $'$i' ) {
    min=$'$i'
   }
  } 
  END {
   printf("%d ",min)
  }' $file
  ;;
 max)
  awk '
  BEGIN {
   max=sprintf("-%d",2^32-1)
  }
  {
   if ( max < $'$i' ) {
    max=$'$i'
   }
  } 
  END {
   printf("%d ",max)
  }' $file
  ;;
 esac
done

Wednesday, April 19, 2006

Secondary 1 Maths Question

[Question]
Given the equation 28x +30y + 31z = 365 and that x, y, and z are natural numbers. Can we predict the value of x + y + z? Discuss and show your thinking.

[Anwser]
Natural number => integer number greater than 0

Determine the range for each variable
y=1,z=1 then x=10.85, 1 <= x <= 10
x=1,z=1 then y=10.20, 1 <= y <= 10
x=1,y=1 then z=9.90, 1 <= z <= 9

Rearrange equation
28x +30y + 31z = 365
28(x+y+z) + (2y+3z) = 365
x+y+z = [ 365 - (2y+3z) ] / 28

x+y+z has to be whole number
therefore 365-(2y+3z) has to be multiple of 28

Tabulate data

x+y+z365-(2y+3z)(2y+3z)5<=(2y+3z)<=47
1028085Out of range
1130857Out of range
1233629OK
133641Out of range

ANSWER: x+y+z is 12
So, what is x, y, z.
Let's try the brute force methods in
AWK, BC, Perl, Ruby, SH, Tcl


Answer is given at the end of this blog.

# AWK
awk '
END {
  for(x=1;x<=10;++x) {
    for(y=1;y<=10;++y) {
      for(z=1;z<=9;++z) {
        s=28*x+30*y+31*z
        if (s==365) {
          print x, "+", y, "+", z, "=", s;
        }
      }
    }
  }
}' /dev/null
# BC
for(x=1;x<=10;++x) {
  for(y=1;y<=10;++y) {
    for(z=1;z<=9;++z) {
      s=28*x+30*y+31*z
      if(s==365) {
        x
        "+"
        y
        "+"
        z
        "="
        s
      }
    }
  }
}
# Perl
foreach $x (1..10) {
  foreach $y (1..10) {
    foreach $z (1..9) {
      $s=28*$x+30*$y+31*$z;
      if ( $s == 365 ) {
        print $x, "+", $y, "+", $z, "=", $s, "\n";
      }
    }
  }
}
# Ruby
(1..10).each do
  |x|
  (1..10).each do
    |y|
    (1..9).each do
      |z|
      s=28*x+30*y+31*z
      if s==365
        print x, "+", y, "+", z, "=", s, "\n"
      end
    end
  end
end
# SH, extremely slow
#
# Works in Linux, Cygwin
# if you do not have "seq" in your system, define this
# seq()
# {
#   awk 'END{for(i='$1';i<='$2';++i) {print i}}' /dev/null
# }
for x in `seq 1 10`; do
  for y in `seq 1 10`; do
    for z in `seq 1 9`; do
      s=`expr 28 \* $x + 30 \* $y + 31 \* $z`
      if [ $s -eq 365 ]; then
        echo "$x + $y + $z = $s"
      fi
    done
  done
done
# Tcl: Method 1
for { set x 1 } { $x <= 10 } { incr x } {
  for { set y 1 } { $y <= 10 } { incr y } {
    for { set z 1 } { $z <= 9 }  { incr z } {
      set s [expr {28*$x + 30*$y + 31*$z}]
      if { $s == 365 } {
        puts  "$x + $y + $z = $s"
      }
    }
  }
}
# Tcl: Method 2
foreach x {1 2 3 4 5 6 7 8 9 10} {
  foreach y {1 2 3 4 5 6 7 8 9 10} {
    foreach z {1 2 3 4 5 6 7 8 9} {
      set s [expr {28*$x + 30*$y + 31*$z}]
      if { $s == 365 } {
        puts  "$x + $y + $z = $s"
      }
    }
  }
}
# Tcl: Method 3
set nine {1 2 3 4 5 6 7 8 9} 
set ten {1 2 3 4 5 6 7 8 9 10} 
foreach x $ten {
  foreach y $ten {
    foreach z $nine {
      set s [expr {28*$x + 30*$y + 31*$z}]
      if { $s == 365 } {
        puts  "$x + $y + $z = $s"
      }
    }
  }
}

[ANSWER]
x=1, y=4, z=7
x=2, y=1, z=9