I've created some code that generates the Bernoulli Numbers based off of formula 33 on MathWorld. This is given at https://mathworld.wolfram.com/BernoulliNumber.html and should work for all integers n but it diverges from the expected results extremely quickly once it gets to n=14. I think the issue may be in the factorial code, although I have no idea.
It's pretty accurate up until 13, all odd numbers should be 0 besides 1 but the values past 14 give weird values. For instance 14 gives a number like 0.9 when it should give something around 7/6 and say 22 gives a very negative number in the order of 10^-4. The odd numbers give strange values like 15 gives around -11.
Here is all the related code
public static double bernoulliNumber2(int n) {
double bernoulliN = 0;
for (double k = 0D; k <= n; k++) {
bernoulliN += sum2(k,n)/(k+1);
}
return bernoulliN;
}
public static double sum2(double k, int n) {
double result = 0;
for (double v = 0D; v <= k; v++) {
result += Math.pow(-1, v) * MathUtils.nCr((int) k,(int) v) * Math.pow(v, n);
}
return result;
}
public static double nCr(int n, int r) {
return Factorial.factorial(n) / (Factorial.factorial(n - r) * Factorial.factorial(r));
}
public static double factorial(int n) {
if (n == 0) return 1;
else return (n * factorial(n-1));
}
Thank you in advance.
The problem here is that floating point arithmetic doesn't need to overflow to experience catastrophic loss of precision.
A floating point number has a mantissa and an exponent, where the value of the number is mantissa * 10^exponent (real floating point numbers use binary, I'm using decimal). The mantissa has limited precision.
When we add floating point numbers of different signs we can end up with a final result which has lost precision.
e.g. let's say the mantissa is 4 digits.
If we add:
1.001 x 10^3 + 1.000 x 10^4 - 1.000 x 10^4
we expect to get 1.001 x 10^3.
But 1.001 x 10^3 + 1.000 x 10^4 = 11.001 x 10^3, which is represented as 1.100 x 10^4, given that our mantissa has only 4 digits.
So when we subtract 1.000 x 10^4 we get 0.100 x 10^4, which is represented as 1.000 x 10^3 rather than 1.001 x 10^3.
Here's an implementation using BigDecimal which gives better results (and is far slower).
import java.math.BigDecimal;
import java.math.RoundingMode;
public class App {
public static double bernoulliNumber2(int n) {
BigDecimal bernoulliN = new BigDecimal(0);
for (long k = 0; k <= n; k++) {
bernoulliN = bernoulliN.add(sum2(k,n));
//System.out.println("B:" + bernoulliN);
}
return bernoulliN.doubleValue();
}
public static BigDecimal sum2(long k, int n) {
BigDecimal result = BigDecimal.ZERO;
for (long v = 0; v <= k; v++) {
BigDecimal vTon = BigDecimal.valueOf(v).pow(n);
result = result.add(BigDecimal.valueOf(Math.pow(-1, v)).multiply(nCr(k,v)).multiply(vTon).divide(BigDecimal.valueOf(k + 1), 1000, RoundingMode.HALF_EVEN));
}
return result;
}
public static BigDecimal nCr(long n, long r) {
return factorial(n).divide(factorial(n - r)).divide(factorial(r));
}
public static BigDecimal factorial(long n) {
if (n == 0) return BigDecimal.ONE;
else return factorial(n-1).multiply(BigDecimal.valueOf(n));
}
public static void main(String[] args) {
for (int i = 0; i < 20; i++) {
System.out.println(i + ": " + bernoulliNumber2(i));
}
}
}
Try changing the scale passed to the division in sum2 and watch the effect on the output.
Related
In the following (naive) implementation of a pow(x, n) method, ignoring completely any optimized approach, I find the following problem:
public double pow(double x, int n) {
boolean negative = n < 0;
long power = Math.abs(n);
double ans = 1.0;
for(long i = 0; i < power; i++) {
ans = ans * x;
}
return negative ? 1.0/ans: ans;
}
Here I have made the assumption that for the case of negative exponent I simply calculate the x^n and then return 1/(x^n) since e.g. 2^(-3) = 1/(2^3)
Problem:
The code fails in the following case:
pow(2.00000, -2147483648)
The output is 1.00000 while the expected correct result is 0.00000
If I change the code as follows:
public double pow(double x, int n) {
long power = n;
if(power < 0) {
x = 1 / x;
power = -power;
}
double ans = 1.0;
for(long i = 0; i < power; i++) {
ans = ans * x;
}
return ans;
}
The result is correct!
So what is the difference between doing the approaches? I was expecting them to be equivalent but they are not
Math.abs(n) is still an int, and only afterwards it is assigned to a long, Therefore, the absolute value of -2147483648 was -2147483648 again (this is noted in the documentation of Math.abs(int)). With the negative bound, the loop performed no iterations.
Math.abs((long)n) would work around that issue.
I have made a function that converts a double to a simplified fraction in Java:
public static int gcm(int a, int b) {
return b == 0 ? a : gcm(b, a % b);
}
public static String toFraction(double d) {
int decimals = String.valueOf(d).split("\\.")[1].length();
int mult = (int) Math.pow(10, decimals);
int numerator = (int) (d * mult);
int denominator = mult;
// now simplify
int gcm = gcm(numerator, denominator);
numerator /= gcm;
denominator /= gcm;
return numerator + "/" + denominator;
}
It works, except for the fact that if I use toFraction(1.0/3), this will, understandably, return "715827882/2147483647". How may I fix this to return "1/3"?
You have to allow for a certain error and not all fractions can be exactly represented as scalar values.
public static String toFraction(double d, double err) {
String s = Long.toString((long) d);
d -= (long) d;
if (d > err) {
for (int den = 2, max = (int) (1 / err); den < max; den++) {
long num = Math.round(d * den);
double d2 = (double) num / den;
if (Math.abs(d - d2) <= err)
return (s.equals("0") ? "" : s + " ") + num +"/"+den;
}
}
return s;
}
public static void main(String... args) {
System.out.println(toFraction(1.0/3, 1e-6));
System.out.println(toFraction(1.23456789, 1e-6));
System.out.println(toFraction(Math.E, 1e-6));
System.out.println(toFraction(Math.PI, 1e-6));
for (double d = 10; d < 1e15; d *= 10)
System.out.println(toFraction(Math.PI, 1.0 / d));
}
prints
1/3
1 19/81
2 719/1001
3 16/113
3 1/5
3 1/7
3 9/64
3 15/106
3 16/113
3 16/113
3 3423/24175
3 4543/32085
3 4687/33102
3 14093/99532
3 37576/265381
3 192583/1360120
3 244252/1725033
3 2635103/18610450
Note: this finds the 21/7, 333/106 and 355/113 approximations for PI.
No double value is equal to one third, so the only way your program can be made to print 1/3 is if you change the specification of the method to favour "nice" answers rather than the answer that is technically correct.
One thing you could do is choose a maximum denominator for the answers, say 100, and return the closest fraction with denominator 100 or less.
Here is how you could implement this using Java 8 streams:
public static String toFraction(double val) {
int b = IntStream.rangeClosed(1, 100)
.boxed()
.min(Comparator.comparingDouble(n -> Math.abs(val * n - Math.round(val * n))))
.get();
int a = (int) Math.round(val * b);
int h = gcm(a, b);
return a/h + "/" + b/h;
}
There is no nice approach to this. double is not very good for this sort of thing. Note that BigDecimal can't represent 1/3 either, so you'll have the same problem with that class.
There are nice ways to handle this but you will need to look at special cases. For example, if the numerator is 1 then the fraction is already reduced and you simply strip out the decimal places and return what you were given.
I have to write simple code to calculate cos(x) value with McLaurin series approximation. I have to do it recursively. Problem is that with too big angle (param in radians) or too high precision (loop has to stop while last term is smaller or equal than given ε) I get StackOverFlowError. At first I did it non-recursively and it worked perfectly, but it's not fully correct according to assignment. I tried to decrease number of calls for term, but I couldn't fix this. Is there any way to improve this code?
public int factorial(int n) {
int result = 1;
for (int i = 1; i <= n; i++) {
result = result * i;
}
return result;
}
public double term(double x, int n) {
return Math.pow(-1, n) * (Math.pow(x, 2*n) / factorial(2*n));
}
public double laurin(String param, String epsilon) {
double x, eps;
double result = 1;
int n = 0;
x = Double.parseDouble(param);
eps = Double.parseDouble(epsilon);
while(Math.abs(term(x, n)) > eps) {
result += term(x, n) * (term(x, n+1) / term(x, n));
n++;
}
return result;
}
Changing factorial(int n) to non-recursive hasn't changed much, I still get an error real quick.
Given an array with x elements, I must find four numbers that, when summed, equal zero. I also need to determine how many such sums exist.
So the cubic time involves three nested iterators, so we just have to look up the last number (with binary search).
Instead by using the cartesian product (same array for X and Y) we can store all pairs and their sum in a secondary array. So for each sum d we just have to look for -d.
This should look something like for (close to) quadratic time:
public static int quad(Double[] S) {
ArrayList<Double> pairs = new ArrayList<>(S.length * S.length);
int count = 0;
for (Double d : S) {
for (Double di : S) {
pairs.add(d + di);
}
}
Collections.sort(pairs);
for (Double d : pairs) {
int index = Collections.binarySearch(pairs, -d);
if (index > 0) count++; // -d was found so increment
}
return count;
}
With x being 353 (for our specific array input), the solution should be 528 but instead I only find 257 using this solution. For our cubic time we are able to find all 528 4-sums
public static int count(Double[] a) {
Arrays.sort(a);
int N = a.length;
int count = 0;
for(int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
for (int k = 0; k < N; k++) {
int l = Arrays.binarySearch(a, -(a[i] + a[j] + a[k]));
if (l > 0) count++;
}
}
}
return count;
}
Is the precision of double lost by any chance?
EDIT: Using BigDecimal instead of double was discussed, but we were afraid it would have an impact on performance. We are only dealing with 353 elements in our array, so would this mean anything to us?
EDITEDIT: I apologize if I use BigDecimal incorrectly. I have never dealt with the library before. So after multiple suggestions I tried using BigDecimal instead
public static int quad(Double[] S) {
ArrayList<BigDecimal> pairs = new ArrayList<>(S.length * S.length);
int count = 0;
for (Double d : S) {
for (Double di : S) {
pairs.add(new BigDecimal(d + di));
}
}
Collections.sort(pairs);
for (BigDecimal d : pairs) {
int index = Collections.binarySearch(pairs, d.negate());
if (index >= 0) count++;
}
return count;
}
So instead of 257 it was able to find 261 solutions. This might indicate there is a problem double and I am in fact losing precision. However 261 is far away from 528, but I am unable to locate the cause.
LASTEDIT: So I believe this is horrible and ugly code, but it seems to be working none the less. We had already experimented with while but with BigDecimal we are now able to get all 528 matches.
I am not sure if it's close enough to quadratic time or not, time will tell.
I present you the monster:
public static int quad(Double[] S) {
ArrayList<BigDecimal> pairs = new ArrayList<>(S.length * S.length);
int count = 0;
for (Double d : S) {
for (Double di : S) {
pairs.add(new BigDecimal(d + di));
}
}
Collections.sort(pairs);
for (BigDecimal d : pairs) {
BigDecimal negation = d.negate();
int index = Collections.binarySearch(pairs, negation);
while (index >= 0 && negation.equals(pairs.get(index))) {
index--;
}
index++;
while (index >= 0 && negation.equals(pairs.get(index))) {
count++;
index++;
}
}
return count;
}
You should use the BigDecimal class instead of double here, since exact precision of the floating point numbers in your array adding up to 0 is a must for your solution. If one of your decimal values was .1, you're in trouble. That binary fraction cannot be precisely represented with a double. Take the following code as an example:
double counter = 0.0;
while (counter != 1.0)
{
System.out.println("Counter = " + counter);
counter = counter + 0.1;
}
You would expect this to execute 10 times, but it is an infinite loop since counter will never be precisely 1.0.
Example output:
Counter = 0.0
Counter = 0.1
Counter = 0.2
Counter = 0.30000000000000004
Counter = 0.4
Counter = 0.5
Counter = 0.6
Counter = 0.7
Counter = 0.7999999999999999
Counter = 0.8999999999999999
Counter = 0.9999999999999999
Counter = 1.0999999999999999
Counter = 1.2
Counter = 1.3
Counter = 1.4000000000000001
Counter = 1.5000000000000002
Counter = 1.6000000000000003
When you search for either pairs or an individual element, you need to count with multiplicity. I.e., if you find element -d in your array of either singletons or pairs, then you need to increase the count by the number of matches that are found, not just increase by 1. This is probably why you're not getting the full number of results when you search over pairs. And it could mean that the number 528 of matches is not the true full number when you are searching over singletons. And in general, you should not use double precision arithmetic for exact arithmetic; use an arbitrary precision rational number package instead.
I'm trying to write a function in Java that calculates the n-th root of a number. I'm using Newton's method for this. However, the user should be able to specify how many digits of precision they want. This is the part with which I'm having trouble, as my answer is often not entirely correct. The relevant code is here: http://pastebin.com/d3rdpLW8. How could I fix this code so that it always gives the answer to at least p digits of precision? (without doing more work than is necessary)
import java.util.Random;
public final class Compute {
private Compute() {
}
public static void main(String[] args) {
Random rand = new Random(1230);
for (int i = 0; i < 500000; i++) {
double k = rand.nextDouble()/100;
int n = (int)(rand.nextDouble() * 20) + 1;
int p = (int)(rand.nextDouble() * 10) + 1;
double math = n == 0 ? 1d : Math.pow(k, 1d / n);
double compute = Compute.root(n, k, p);
if(!String.format("%."+p+"f", math).equals(String.format("%."+p+"f", compute))) {
System.out.println(String.format("%."+p+"f", math));
System.out.println(String.format("%."+p+"f", compute));
System.out.println(math + " " + compute + " " + p);
}
}
}
/**
* Returns the n-th root of a positive double k, accurate to p decimal
* digits.
*
* #param n
* the degree of the root.
* #param k
* the number to be rooted.
* #param p
* the decimal digit precision.
* #return the n-th root of k
*/
public static double root(int n, double k, int p) {
double epsilon = pow(0.1, p+2);
double approx = estimate_root(n, k);
double approx_prev;
do {
approx_prev = approx;
// f(x) / f'(x) = (x^n - k) / (n * x^(n-1)) = (x - k/x^(n-1)) / n
approx -= (approx - k / pow(approx, n-1)) / n;
} while (abs(approx - approx_prev) > epsilon);
return approx;
}
private static double pow(double x, int y) {
if (y == 0)
return 1d;
if (y == 1)
return x;
double k = pow(x * x, y >> 1);
return (y & 1) == 0 ? k : k * x;
}
private static double abs(double x) {
return Double.longBitsToDouble((Double.doubleToLongBits(x) << 1) >>> 1);
}
private static double estimate_root(int n, double k) {
// Extract the exponent from k.
long exp = (Double.doubleToLongBits(k) & 0x7ff0000000000000L);
// Format the exponent properly.
int D = (int) ((exp >> 52) - 1023);
// Calculate and return 2^(D/n).
return Double.longBitsToDouble((D / n + 1023L) << 52);
}
}
Just iterate until the update is less than say, 0.0001, if you want a precision of 4 decimals.
That is, set your epsilon to Math.pow(10, -n) if you want n digits of precision.
Let's recall what the error analysis of Newton's method says. Basically, it gives us an error for the nth iteration as a function of the error of the n-1 th iteration.
So, how can we tell if the error is less than k? We can't, unless we know the error at e(0). And if we knew the error at e(0), we would just use that to find the correct answer.
What you can do is say "e(0) <= m". You can then find n such that e(n) <= k for your desired k. However, this requires knowing the maximal value of f'' in your radius, which is (in general) just as hard a problem as finding the x intercept.
What you're checking is if the error changes by less than k, which is a perfectly acceptable way to do it. But it's not checking if the error is less than k. As Axel and others have noted, there are many other root-approximation algorithms, some of which will yield easier error analysis, and if you really want this, you should use one of those.
You have a bug in your code. Your pow() method's last line should read
return (y & 1) == 1 ? k : k * x;
rather than
return (y & 1) == 0 ? k : k * x;