[npl] / trunk / NationalProblemLibrary / WHFreeman / Rogawski_Calculus_Early_Transcendentals_Second_Edition / macros / freemanMacros.pl Repository:
ViewVC logotype

View of /trunk/NationalProblemLibrary/WHFreeman/Rogawski_Calculus_Early_Transcendentals_Second_Edition/macros/freemanMacros.pl

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2584 - (download) (as text) (annotate)
Tue Nov 8 15:17:41 2011 UTC (18 months, 1 week ago) by aubreyja
File size: 14103 byte(s)
Rogawski problems contributed by publisher WHFreeman. These are a subset of the problems available to instructors who use the Rogawski textbook. The remainder can be obtained from the publisher.

    1 sub textbook_ref {
    2     my ($text, $sec, $ex) = @_;
    3     return "$text section $sec, exercise $ex";
    4 }
    5 
    6 sub textbook_ref_corr {
    7     return "Similar to " . textbook_ref(@_) . ".";
    8 }
    9 
   10 sub textbook_ref_exact {
   11     return "From " . textbook_ref(@_) . ".";
   12 }
   13 
   14 sub list_random_multi_uniq($@) {
   15   my ($n, @list) = @_;
   16   my @result;
   17   while (@result < $n) {
   18     my $i = random(0,$#list,1);
   19     if ($i==0) {
   20       push @result, shift @list;
   21     } elsif ($i==$#list) {
   22       push @result, pop @list;
   23     } else {
   24       push @result, $list[$i];
   25       $list[$i] = pop @list;
   26     }
   27   }
   28   return @result;
   29 }
   30 
   31 sub IINT {
   32     return '\int\!\!\int';
   33 }
   34 
   35 sub IIINT {
   36     return '\int\!\!\int\!\!\int';
   37 }
   38 
   39 $IINT = IINT();
   40 $IIINT = IIINT();
   41 
   42 sub VUSAGE {
   43     return $BBOLD . ' Usage: ' . $EBOLD . 'To enter a vector, for example \(\langle x,y,z\rangle\), type "' . $LTS . ' x, y, z ' . $GTS . '".';
   44 }
   45 
   46 $VUSAGE = VUSAGE();
   47 
   48 sub PUSAGE {
   49     return $BBOLD . ' Usage: ' . $EBOLD . 'To enter a point, for example \( (x,y,z) \), type "(x, y, z)".';
   50 }
   51 
   52 $PUSAGE = PUSAGE();
   53 
   54 #sam# copied from bkellMacros.pl -- no reason to maintain two macro files.
   55 #sam# FIXME -- these should probably lose their "bkell_" prefixes.
   56 
   57 # bkellMacros.pl
   58 # Brian Kell <bkell@cse.unl.edu>
   59 # Last updated 3:24 CDT, 24 Jun 2007
   60 
   61 ###############################################################################
   62 # bkell_linear_simplify($a, $b)
   63 #
   64 # Returns a string representing $a*x+$b in simplified form, where "simplified"
   65 # means something along the lines of the following:
   66 #
   67 #  a    b   output
   68 # ---  ---  ------
   69 #  0    0      0
   70 #  1    1     x+1
   71 # -1   -1    -x-1
   72 # -1    5     5-x
   73 #  2    0     2x
   74 # -4    3    3-4x
   75 # -4   -3    -4x-3
   76 #
   77 sub bkell_linear_simplify
   78 {
   79   my ($a, $b) = @_;
   80 
   81   if ($a == 0) {
   82     return "$b";
   83   } elsif ($b == 0) {
   84     if ($a == -1) {
   85       return "-x";
   86     } elsif ($a == 1) {
   87       return "x";
   88     } else {
   89       return "${a}x";
   90     }
   91   } elsif ($a < 0 && $b > 0) {
   92     if ($a == -1) {
   93       return "$b-x";
   94     } else {
   95       return "$b-".(abs $a)."x";
   96     }
   97   } elsif ($a == 1) {
   98     return "x".sprintf("%+d", $b);
   99   } elsif ($a == -1) {
  100     return "-x".sprintf("%+d", $b);
  101   } else {
  102     return "${a}x".sprintf("%+d", $b);
  103   }
  104 }
  105 
  106 ###############################################################################
  107 # bkell_simplify_fraction($num, $denom)
  108 #
  109 # Simplifies the fraction $num/$denom; returns the list ($num, $denom).
  110 #
  111 sub bkell_simplify_fraction
  112 {
  113   my ($num, $denom) = @_;
  114 
  115   if ($num == 0) { return (0, 1); }
  116 
  117   my $sign = +1;
  118   if ($num < 0) { $sign *= -1; $num *= -1; }
  119   if ($denom < 0) { $sign *= -1; $denom *= -1; }
  120 
  121   for ($i = 2; $i <= $num && $i <= $denom; ++$i) {
  122     while ($num % $i == 0 && $denom % $i == 0) {
  123       $num /= $i;
  124       $denom /= $i;
  125     }
  126   }
  127 
  128   return ($sign*$num, $denom);
  129 }
  130 
  131 ###############################################################################
  132 # bkell_simplify_fraction_string($num, $denom [, $flags])
  133 #
  134 # Returns a string like "$num/$denom" in simplified form. If the string $flags
  135 # contains "+", then a leading "+" is included if the fraction is non-negative.
  136 # If $flags contains "0", then a fraction with a value of 0 will cause the
  137 # empty string to be returned. If $flags contains "1", then a fraction with a
  138 # value of 1 or -1 will cause only the sign to be returned (or the empty
  139 # string, if the fraction is non-negative and $flags does not contain "+"). If
  140 # $flags contains "(", then parentheses will be placed around "$num/$denom"
  141 # (the sign, if it exists, will be outside the parentheses); if $flags contains
  142 # "[", then parentheses will be used only if a sign is used and the denominator
  143 # is other than 1. A flag of "(" overrides "]". If $flags contains "f", then
  144 # the string returned will be in the form "{$num \over $denom}" instead of the
  145 # normal slashed version.
  146 #
  147 sub bkell_simplify_fraction_string
  148 {
  149   my ($num, $denom, $flags) = @_;
  150   if (!defined $flags) { $flags = ""; }
  151 
  152   ($num, $denom) = bkell_simplify_fraction($num, $denom);
  153 
  154   my $sign = ($num >= 0 ? ($flags =~ /\+/ ? "+" : "") : "-");
  155   $num = abs $num;
  156 
  157   my ($pre, $post);
  158   if ($flags =~ /\(/ || ($flags =~ /\[/ && $sign ne "" && $denom != 1)) {
  159     ($pre, $post) = ("(", ")");
  160   } else {
  161     ($pre, $post) = ("", "");
  162   }
  163 
  164   if ($num == 0 && $flags =~ /0/) { return ""; }
  165   if ($denom == 1) {
  166     if ($num == 1 && $flags =~ /1/) { return "$sign"; }
  167     return "$sign$pre$num$post";
  168   }
  169   if ($flags =~ /f/) { return "$sign$pre {$num \\over $denom}$post"; }
  170   return "$sign$pre$num/$denom$post";
  171 }
  172 
  173 ###############################################################################
  174 # bkell_poly_term($coeff_num, $coeff_denom, $var [, "+"])
  175 #
  176 # Produces and simplifies a term of a polynomial of the general form
  177 # "($coeff_num/$coeff_denom)$var". Handles special cases (such as $num==0,
  178 # $denom==1, etc.). If the fourth argument is "+", then a leading "+" is
  179 # included if the term is positive.
  180 #
  181 sub bkell_poly_term
  182 {
  183   my ($coeff_num, $coeff_denom, $var, $plus) = @_;
  184   if (!defined $plus) { $plus = ""; }
  185 
  186   if ($coeff_num == 0) { return ""; }
  187 
  188   my $sign = +1;
  189   if ($coeff_num < 0) { $sign *= -1; $coeff_num *= -1; }
  190   if ($coeff_denom < 0) { $sign *= -1; $coeff_denom *= -1; }
  191   $sign = ($sign > 0 ? ($plus eq "+" ? "+" : "") : "-");
  192 
  193   my ($num, $denom) = bkell_simplify_fraction($coeff_num, $coeff_denom);
  194 
  195   if ($denom == 1) {
  196     if ($num == 1) {
  197       return "$sign$var";
  198     } else {
  199       return "$sign$num$var";
  200     }
  201   }
  202 
  203   return "$sign($num/$denom)$var";
  204 }
  205 
  206 ###############################################################################
  207 # bkell_gcd($x, $y)
  208 #
  209 # Returns the greatest common divisor of $x and $y.
  210 #
  211 sub bkell_gcd {
  212   my ($x, $y) = @_;
  213   $x = abs $x;
  214   $y = abs $y;
  215   if ($x > $y) { ($x, $y) = ($y, $x); }
  216   if ($x == 0) { return $y; }
  217   my $r = $y % $x;
  218   if ($r == 0) { return $x; }
  219   return bkell_gcd($x, $r);
  220 }
  221 
  222 ###############################################################################
  223 # bkell_poly_eval($x, $a_n, ..., $a_0)
  224 #
  225 # Evaluates the polynomial a_n*x^n + ... + a_1*x + a_0 at the given value of x.
  226 #
  227 sub bkell_poly_eval
  228 {
  229   my $x = shift;
  230   my $value = shift;
  231   while (@_) { $value *= $x; $value += shift; }
  232   return $value;
  233 }
  234 
  235 ###############################################################################
  236 # bkell_real_zeros_finder($a_n, ..., $a_0)
  237 #
  238 # Returns a list of numerical approximations of the zeros of the polynomial
  239 # a_n*x^n + ... + a_1*x + a_0, in order from least to greatest.
  240 #
  241 # The possibility of overflow or underflow is ignored. Overflow is likely to be
  242 # a bigger problem than underflow.
  243 #
  244 # Do not use this code to guide missiles or control nuclear power plants.
  245 #
  246 sub bkell_real_zeros_finder
  247 {
  248   my @coeffs = @_;
  249 
  250   while (@coeffs && $coeffs[0] == 0) { shift @coeffs; }
  251   my $deg = $#coeffs;
  252 
  253   if ($deg == -1) {
  254     return ("x");  # zero polynomial is zero everywhere
  255   } elsif ($deg == 0) {
  256     return ();  # constant nonzero polynomial has no zeros
  257   } elsif ($deg == 1) {
  258     return (-$coeffs[1]/$coeffs[0]);  # linear polynomial has one zero
  259   }
  260 
  261   # find critical points
  262   my @derivative = @coeffs;
  263   pop @derivative;
  264   for (my $i = 0; $i < $#derivative; ++$i) {
  265     $derivative[$i] *= @derivative - $i;
  266   }
  267   my @cp = bkell_real_zeros_finder(@derivative);
  268 
  269   # if no critical points, we have a monotone function
  270   if (!@cp) {
  271     my ($lb, $rb) = (-1, 1);
  272     my $y1 = bkell_poly_eval($lb, @coeffs);
  273     my $y2 = bkell_poly_eval($rb, @coeffs);
  274     my $sign = ($y1 < $y2 ? +1 : -1);
  275     while ($sign * $y1 > 0) {
  276       $lb *= 2;
  277       $y1 = bkell_poly_eval($lb, @coeffs);
  278     }
  279     while ($sign * $y2 < 0) {
  280       $rb *= 2;
  281       $y2 = bkell_poly_eval($rb, @coeffs);
  282     }
  283     my $guess_x = ($lb + $rb) / 2;
  284     while ($lb < $guess_x && $guess_x < $rb &&
  285       (my $guess_y = bkell_poly_eval($guess_x, @coeffs)) != 0)
  286     {
  287       if ( ($y1 < 0 && $guess_y < 0) || ($y1 > 0 && $guess_y > 0) ) {
  288         $lb = $guess_x;
  289       } else {
  290         $rb = $guess_x;
  291       }
  292       $guess_x = ($lb + $rb) / 2;
  293     }
  294     return ($guess_x);
  295   }
  296 
  297   my @zeros = ();
  298 
  299   # search for a zero to the left of the first critical point
  300   {
  301     my $y = bkell_poly_eval($cp[0], @coeffs);
  302     # we catch this case when we check between critical points:
  303     last if $y == 0;
  304     # not really the limit, but only the sign matters:
  305     my $lim = $coeffs[0] * ($deg % 2 ? -1 : +1);
  306     if ( ($y > 0 && $lim < 0) || ($y < 0 && $lim > 0) ) {
  307       my ($lb, $rb) = (undef, $cp[0]);
  308       my $guess_x = $rb - 10;
  309       if ($guess_x >= 0) { $guess_x = -10; }
  310       while ((!defined $lb || $lb < $guess_x) && $guess_x < $rb &&
  311         (my $guess_y = bkell_poly_eval($guess_x, @coeffs)) != 0)
  312       {
  313         if ( ($y > 0 && $guess_y > 0) || ($y < 0 && $guess_y < 0) ) {
  314           $rb = $guess_x;
  315           if (defined $lb) {
  316             $guess_x = ($lb + $guess_x) / 2;
  317           } else {
  318             $guess_x *= 2;
  319           }
  320         } else {
  321           $lb = $guess_x;
  322           $guess_x = ($guess_x + $rb) / 2;
  323         }
  324       }
  325       push @zeros, $guess_x;
  326     }
  327   }
  328 
  329   # search for zeros between critical points
  330   for (my $i = 0; $i < $#cp; ++$i) {
  331     my $y1 = bkell_poly_eval($cp[$i], @coeffs);
  332     if ($y1 == 0) {
  333       push @zeros, $cp[$i];
  334       next;
  335     }
  336     my $y2 = bkell_poly_eval($cp[$i+1], @coeffs);
  337     if ($y2 == 0) {
  338       push @zeros, $cp[$i+1];
  339       ++$i;
  340       next;
  341     }
  342     next if ($y1 > 0 && $y2 > 0) || ($y1 < 0 && $y2 < 0);
  343     my ($lb, $rb) = ($cp[$i], $cp[$i+1]);
  344     my $guess_x = ($lb + $rb) / 2;
  345     while ($lb < $guess_x && $guess_x < $rb &&
  346       (my $guess_y = bkell_poly_eval($guess_x, @coeffs)) != 0)
  347     {
  348       if ( ($y1 > 0 && $guess_y > 0) || ($y1 < 0 && $guess_y < 0) ) {
  349         $lb = $guess_x;
  350       } else {
  351         $rb = $guess_x;
  352       }
  353       $guess_x = ($lb + $rb) / 2;
  354     }
  355     push @zeros, $guess_x unless @zeros && $zeros[-1] == $guess_x;
  356   }
  357 
  358   # search for a zero to the right of the last critical point
  359   {
  360     my $y = bkell_poly_eval($cp[-1], @coeffs);
  361     if ($y == 0 && $zeros[-1] != $cp[-1]) {
  362       push @zeros, $cp[-1];
  363       last;
  364     }
  365     if ( ($y > 0 && $coeffs[0] < 0) || ($y < 0 && $coeffs[0] > 0) ) {
  366       my ($lb, $rb) = ($cp[-1], undef);
  367       my $guess_x = $lb + 10;
  368       if ($guess_x <= 0) { $guess_x = 10; }
  369       while ($lb < $guess_x && (!defined $rb || $guess_x < $rb) &&
  370         (my $guess_y = bkell_poly_eval($guess_x, @coeffs)) != 0)
  371       {
  372         if ( ($y > 0 && $guess_y > 0) || ($y < 0 && $guess_y < 0) ) {
  373           $lb = $guess_x;
  374           if (defined $rb) {
  375             $guess_x = ($guess_x + $rb) / 2;
  376           } else {
  377             $guess_x *= 2;
  378           }
  379         } else {
  380           $rb = $guess_x;
  381           $guess_x = ($lb + $guess_x) / 2;
  382         }
  383       }
  384       push @zeros, $guess_x unless @zeros && $zeros[-1] == $guess_x;
  385     }
  386   }
  387 
  388   return @zeros;
  389 }
  390 
  391 ###############################################################################
  392 # bkell_floor($x)
  393 #
  394 # Returns the floor of $x. Normally this would be done with POSIX::floor, but
  395 # WeBWorK doesn't allow you to use standard modules like POSIX.
  396 #
  397 sub bkell_floor
  398 {
  399   my $x = shift;
  400   my $floor = int $x;
  401   if ($x < 0 && $x != $floor) { $floor -= 1; }
  402   return $floor;
  403 }
  404 
  405 ###############################################################################
  406 # bkell_ceil($x)
  407 #
  408 # Returns the ceiling of $x.
  409 #
  410 sub bkell_ceil
  411 {
  412   return -bkell_floor(-shift);
  413 }
  414 
  415 ###############################################################################
  416 # bkell_sigfigs($x, $n)
  417 #
  418 # Returns a string containing $x rounded to $n significant figures.
  419 #
  420 sub bkell_sigfigs
  421 {
  422   my ($x, $n) = @_;
  423 
  424   if ($x == 0) { return "0".($n > 1 ? "." : "").("0" x ($n-1)); }
  425 
  426   my $minus = "";
  427   if ($x < 0) {
  428     $minus = "-";
  429     $x = -$x;
  430   }
  431 
  432   my $floor_log = bkell_floor(log($x)/log(10));
  433 
  434   if ($floor_log+1 >= $n) {
  435     my $sf = 10**($floor_log-$n+1);
  436     return $minus.(sprintf("%.0f", $x/$sf)*$sf);
  437   } else {
  438     my $digits = $n-$floor_log-1;
  439     return $minus.sprintf("%.${digits}f", $x);
  440   }
  441 }
  442 
  443 ###############################################################################
  444 # bkell_125($x)
  445 #
  446 # Returns the value logarithmically nearest $x in the sequence
  447 #     ..., -1000, -500, -200, -100, -50, -20, -10, -5, -2, -1, -0.5, -0.2,
  448 #          -0.1, -0.05, -0.02, -0.01, ..., 0, ..., 0.01, 0.02, 0.05, 0.1,
  449 #          0.2, 0.5, 1, 2, 5, 10, 20, 50, 100, 200, 500, 1000, ... .
  450 #
  451 sub bkell_125
  452 {
  453   my $x = shift;
  454 
  455   if ($x == 0) { return 0; }
  456 
  457   my $sign = +1;
  458   if ($x < 0) { $sign = -1;  $x = -$x; }
  459 
  460   my $log = log($x)/log(10);
  461   my $characteristic = bkell_floor($log);
  462   my $mantissa = $log - $characteristic;
  463 
  464   my $log2 = log(2)/log(10);
  465   my $log5 = log(5)/log(10);
  466   my $m;
  467   if ($mantissa < $log2 / 2) {
  468     $m = 0;
  469   } elsif ($mantissa < ($log2 + $log5) / 2) {
  470     $m = $log2;
  471   } elsif ($mantissa < ($log5 + 1) / 2) {
  472     $m = $log5;
  473   } else {
  474     $m = 1;
  475   }
  476 
  477   return $sign * (10 ** ($characteristic + $m));
  478 }
  479 
  480 ###############################################################################
  481 # bkell_list_random_selection($n, @list)
  482 #
  483 # Returns a selection of $n distinct elements of @list. This is like
  484 # list_random_multi_uniq in freemanMacros.pl, except that this function will
  485 # always return the elements in the same order as they appear in @list.
  486 #
  487 sub bkell_list_random_selection
  488 {
  489   my $n = int abs shift;  # so strange $n won't cause infinite loop
  490   my @list = @_;
  491 
  492   my $needed = $n;
  493   my @result = ();
  494   while ($needed && @list) {
  495     if (random(1, scalar @list) <= $needed) {
  496       push @result, shift @list;
  497       --$needed;
  498     } else {
  499       shift @list;
  500     }
  501   }
  502 
  503   return @result;
  504 }
  505 
  506 ###############################################################################
  507 # bkell_graph_axis($a, $b)
  508 #
  509 # Returns a list ($min, $max, $step), where $min <= $a, $max >= $b, $step is a
  510 # power of 10, $min and $max are multiples of $step, abs($min) <= 9*$step, and
  511 # abs($max) <= 9*$step. Useful for deciding bounds for the axis of a graph. For
  512 # example, to make a graph axis that can handle values between $a and $b, call
  513 # bkell_graph_axis, and then set the minimum value of the axis to $min and the
  514 # maximum to $max, and put tick marks every $step units.
  515 #
  516 sub bkell_graph_axis
  517 {
  518   my ($a, $b) = @_;
  519 
  520   if ($a > $b) { ($a, $b) = ($b, $a); }
  521 
  522   my $s1 = 0;
  523   my $s2 = 0;
  524 
  525   if ($a != 0) { $s1 = bkell_floor(log(abs $a)/log(10)); }
  526   if ($b != 0) { $s2 = bkell_floor(log(abs $b)/log(10)); }
  527 
  528   my $s = ($s1 > $s2 ? $s1 : $s2);
  529 
  530   $step = 10**$s;
  531   $min = $step * bkell_floor($a/$step);
  532   $max = $step * bkell_ceil($b/$step);
  533 
  534   return ($min, $max, $step);
  535 }
  536 
  537 ####################################################################### EOF ###

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9