WeBWorK Problems

Large integer precision for math objects

Large integer precision for math objects

by Oscar Levin -
Number of replies: 4
I have written a number of problems about the Principle of Inclusion and Exclusion in discrete math (counting surjective functions, derangements, etc). I've run into a problem when the components of my formula that give the numeric answer contain large numbers inside of a Compute math object.

For example, the number of derangments of 10 objects is 10! - [C(10,1)9! - C(10,2)8! + ... -C(10,10)0!]. To implement this using a random variable $n instead of 10, I'm writing a loop:

$next = 0;
for ($j=1; $j <= $n; $j++)
{ $next = Compute("$next + (-1)^($j+1) * C($n,$j) * ($n-$j)!"); }

and then $ans = Compute("$n! - $next");

This works some times, but when $n gets large, Compute returns a real number like 6.70443E+11, which is not exactly correct. So the loop introduced rounding error (at least I think this is why I'm getting wrong answers some times).

Does anyone have any ideas how to work around this?

Thanks,
Oscar.
In reply to Oscar Levin

Re: Large integer precision for math objects

by Alex Jordan -
Hi Oscar,

There are a few issues here. To control how a Math Object displays digits, see:
http://webwork.maa.org/wiki/Modifying_Contexts_(advanced)#Number_Formats

In particular, using:
Context()->{format}{number} = "%.12g";
Should use 12 digits before switching into scientific notation. Don't go past 16 or you will be higher than perl is set up to keep track of.

Your loop uses quotes inside the Compute. So at each iteration, it is using a string version of the number that has already cut it to only 6 significant digits and moved to scientific notation if its a long number. You could use a loop that doesn't use Math Objects, and computes it all in plain perl. You may have to implement a factorial() subroutine first though.

You might be able to simply use:
$ans = int(Real("$n ! + 1/2")/e);
as a non-loopy way to get the derangement number.

So far I'm just talking about getting the displayed answer to be right. It's a separate question about what precision you want to impose upon the students when entering an answer. By default if they are within 0.1% of the correct answer, they will be awarded credit. If you are after something else, I can get into that too.

In reply to Alex Jordan

Re: Large integer precision for math objects

by Oscar Levin -
Hi Alex,

Thanks, that helps. I'm still not clear on the relationship between how the number is displayed and how it is stored as a string version of the number (from Compute). If I set the number format to 12 digits, but still use quotes inside Compute, will the numbers used in the computation still get rounded?

Come to think of it, what happens when a student enters a long computation included C(20,6)*14!? Does the answer checker round to 6 digits?

No worries about the student precision. At least for these answers, the final answer is pretty small so the default precision should force students to give the exact answer.

Oscar.
In reply to Oscar Levin

Re: Large integer precision for math objects

by Alex Jordan -
I think of it all as there being three things.
  1. The actual value of the number stored somewhere to all the precision perl can store it.
  2. The value that is presented on screen. This is taking the above and applying its string() or tex() method.
  3. The range of numbers that could be entered by a student and they'd get credit.
The number format line affects what happens with 2 above.

When you do something like Compute("$x"), the Compute isn't the important part. The double quotes around the $x cause that $x's string() method to be applied. So if $x is really 3.14159265359, then "$x" is 3.14159. And Compute is only being fed 3.14159. Compute takes that and builds a MathObject with underlying value 3.14159, not 3.14159265359. Does that explain it?

> If I set the number format to 12 digits, but still use quotes inside Compute, will the numbers used in the computation still get rounded?

Only if they are longer than 12 digits. Now "$x" would come back as
3.14159265359, so no harm.

> Come to think of it, what happens when a student enters a long computation included C(20,6)*14!?

It's a context flag if they are even allowed to enter an expression with C and factorial like this. But assuming that they are, then the rounding is not relevant (because that has to do with making a MathObject into a string for some purpose, which is not part of answer checking.) There is the correct answer, which I assume is correct down to the ones place. There is whatever the student types in, number or expression. If it's an expression, it will be computed as a number without rounding (well, unless it gets longer than 16 or 17 digits). The two numbers will be compared using some error tolerance method. By default, it will count their answer correct if the student answer is within 0.1% of the correct answer. (There are alternatives for this if you would like to use them, including a way to make the student enter every last digit correctly. But it sounds like you don't need that.)