Polynomial: a0x^0 + a1x^1 +a2x^2 + a3x^3 + ... + anx^n
Array: array_a[] = {a0, a1, a2, a3 ... an};
I wrote a function to calculate this polynomial in Java:
public double cal(double x) {
double y = 0.0;
for (int index = array_a.length - 1; index >= 0; index--) {
y = array_a[index] + y * x;
}
return y;
}
This seems 5 times faster than the loop y += array_a[index] * Math.Pow(x, index);
But I wondering if there is a better way to compute this polynomial?
** For anyone thinks it's a different calculation: I did test the function above. It does the same thing with y += array_a[index] * Math.Pow(x, index); and they compute the same result.
Thanks.
This is Horner's method. If you only want to calculate it once per polynomial, this is the most efficient algorithm:
… Horner's method requires only n additions and n multiplications, and its storage requirements are only n times the number of bits of x. …
Horner's method is optimal, in the sense that any algorithm to evaluate an arbitrary polynomial must use at least as many operations. Alexander Ostrowski proved in 1954 that the number of additions required is minimal. Victor Pan proved in 1966 that the number of multiplications is minimal.
If you need to evaluate the polynomial extremely many times and the degree is very high, then there are methods to transform the representation of the polynomial (preconditioning) so that the number of multiplication is reduced to ⌊n/2⌋ + 2. This seems not very practical though, at least I've never seen this in the wild. I've found an online paper that describes some of the algorithms if you are interested.
Also mentioned in the paper, due to the CPU architecture it might be more efficient if you evaluating even and odd terms separately so they can be placed in parallel pipelines:
public double cal(double x) {
double x2 = x * x;
double y_odd = 0.0, y_even = 0.0;
int index = array_a.length - 1;
if (index % 2 == 0) {
y_even = array_a[index];
index -= 1;
}
for (; index >= 0; index -= 2) {
y_odd = array_a[index] + y_odd * x2;
y_even = array_a[index-1] + y_even * x2;
}
return y_even + y_odd * x;
}
The JIT/compiler might be able to do this conversion for you or even use SIMD to make it very fast automagically. Anyway, for this kind of micro-optimization, always profile before committing to a final solution.
Related
I am currently working on a Java math library which will include a variety of correctly rounded functions (i.e. sqrt, cbrt, exp, sin, gamma, and ln). I have already used the Babylonian method to write a square root algorithm that is correct to within 1 ulp of the correct answer. However, I cannot figure out how to properly calculate which way the number should be rounded to represent the best possible approximation to the actual square root of the input. Answers containing principles which can be extended to other functions would be preferred, but I have heard that sqrt is a simpler case than many transcendental functions, and specialized solutions would also be much appreciated.
Also, here is a cleaned-up version of my code as of this question's original submission:
public static double sqrt(double x) {
long bits = Double.doubleToLongBits(x);
// NaN and non-zero negatives:
if (Double.isNaN(x) || x < 0) return Double.NaN;
// +-0 and 1:
if (x == 0d || x == 1d) return x;
// Halving the exponent to come up with a good initial guess:
long exp = bits << 1;
exp = (exp - 0x7fe0000000000000L >> 1) + 0x7fe0000000000000L >>> 1 & 0x7ff0000000000000L;
double guess = Double.longBitsToDouble(bits & 0x800fffffffffffffL | exp);
double nextUp, nextDown, guessSq, nextUpSq, nextDownSq;
// Main loop:
while (true) {
guessSq = guess * guess;
if (guessSq == x) return guess;
nextUp = Math.nextUp(guess);
nextUpSq = nextUp * nextUp;
if (nextUpSq == x) return nextUp;
if (guessSq < x && x < nextUpSq) {
double z = x / nextUp;
if (z * nextUp > x) z = Math.nextDown(z);
return z < nextUp ? nextUp : guess;
}
nextDown = Math.nextDown(guess);
nextDownSq = nextDown * nextDown;
if (nextDownSq == x) return nextDown;
if (nextDownSq < x && x < guessSq) {
double z = x / guess;
if (z * guess > x) z = Math.nextDown(z);
return z < guess ? guess : nextDown;
}
// Babylonian method:
guess = 0.5 * (guess + x / guess);
}
}
As you can see, I was using division as a test. However, I believe that requires the division to round towards 0, which obviously doesn't happen in Java.
By the Taylor theorem, the square root function is locally approximated by a linear function, of slope 1/2√x, which is positive. So you can relate the error to the error in the square, x - (√x)², where √x is understood to be the approximate root. Then you round in the direction that minimizes this error.
Anyway, the computation of x - (√x)² is subjected to catastrophic cancellation and you may need extended accuracy to compute it reliably. Not sure the benefit is worth the effort.
How do I map numbers, linearly, between a and b to go between c and d.
That is, I want numbers between 2 and 6 to map to numbers between 10 and 20... but I need the generalized case.
My brain is fried.
If your number X falls between A and B, and you would like Y to fall between C and D, you can apply the following linear transform:
Y = (X-A)/(B-A) * (D-C) + C
That should give you what you want, although your question is a little ambiguous, since you could also map the interval in the reverse direction. Just watch out for division by zero and you should be OK.
Divide to get the ratio between the sizes of the two ranges, then subtract the starting value of your inital range, multiply by the ratio and add the starting value of your second range. In other words,
R = (20 - 10) / (6 - 2)
y = (x - 2) * R + 10
This evenly spreads the numbers from the first range in the second range.
It would be nice to have this functionality in the java.lang.Math class, as this is such a widely required function and is available in other languages.
Here is a simple implementation:
final static double EPSILON = 1e-12;
public static double map(double valueCoord1,
double startCoord1, double endCoord1,
double startCoord2, double endCoord2) {
if (Math.abs(endCoord1 - startCoord1) < EPSILON) {
throw new ArithmeticException("/ 0");
}
double offset = startCoord2;
double ratio = (endCoord2 - startCoord2) / (endCoord1 - startCoord1);
return ratio * (valueCoord1 - startCoord1) + offset;
}
I am putting this code here as a reference for future myself and may be it will help someone.
As an aside, this is the same problem as the classic convert celcius to farenheit where you want to map a number range that equates 0 - 100 (C) to 32 - 212 (F).
https://rosettacode.org/wiki/Map_range
[a1, a2] => [b1, b2]
if s in range of [a1, a2]
then t which will be in range of [b1, b2]
t= b1 + ((s- a1) * (b2-b1))/ (a2-a1)
In addition to #PeterAllenWebb answer, if you would like to reverse back the result use the following:
reverseX = (B-A)*(Y-C)/(D-C) + A
Each unit interval on the first range takes up (d-c)/(b-a) "space" on the second range.
Pseudo:
var interval = (d-c)/(b-a)
for n = 0 to (b - a)
print c + n*interval
How you handle the rounding is up to you.
if your range from [a to b] and you want to map it in [c to d] where x is the value you want to map
use this formula (linear mapping)
double R = (d-c)/(b-a)
double y = c+(x*R)+R
return(y)
Where X is the number to map from A-B to C-D, and Y is the result:
Take the linear interpolation formula, lerp(a,b,m)=a+(m*(b-a)), and put C and D in place of a and b to get Y=C+(m*(D-C)). Then, in place of m, put (X-A)/(B-A) to get Y=C+(((X-A)/(B-A))*(D-C)). This is an okay map function, but it can be simplified. Take the (D-C) piece, and put it inside the dividend to get Y=C+(((X-A)*(D-C))/(B-A)). This gives us another piece we can simplify, (X-A)*(D-C), which equates to (X*D)-(X*C)-(A*D)+(A*C). Pop that in, and you get Y=C+(((X*D)-(X*C)-(A*D)+(A*C))/(B-A)). The next thing you need to do is add in the +C bit. To do that, you multiply C by (B-A) to get ((B*C)-(A*C)), and move it into the dividend to get Y=(((X*D)-(X*C)-(A*D)+(A*C)+(B*C)-(A*C))/(B-A)). This is redundant, containing both a +(A*C) and a -(A*C), which cancel each other out. Remove them, and you get a final result of: Y=((X*D)-(X*C)-(A*D)+(B*C))/(B-A)
TL;DR: The standard map function, Y=C+(((X-A)/(B-A))*(D-C)), can be simplified down to Y=((X*D)-(X*C)-(A*D)+(B*C))/(B-A)
int srcMin = 2, srcMax = 6;
int tgtMin = 10, tgtMax = 20;
int nb = srcMax - srcMin;
int range = tgtMax - tgtMin;
float rate = (float) range / (float) nb;
println(srcMin + " > " + tgtMin);
float stepF = tgtMin;
for (int i = 1; i < nb; i++)
{
stepF += rate;
println((srcMin + i) + " > " + (int) (stepF + 0.5) + " (" + stepF + ")");
}
println(srcMax + " > " + tgtMax);
With checks on divide by zero, of course.
This question already has answers here:
pow (x,y) in Java
(4 answers)
Closed 6 years ago.
I'm a bit stumped. I'm working on a homework question that requires you to find e^x through a series of 30 iterations: 1 + x + x^2/2! ... x^n/n!. In this case n = 30 and the range for x is -3 through 3. Seems straightforward enough. However when trying to execute it in Java I'm finding that I'm baffled.
The thought was to run two for loops. The outer loop for the iterations of x and the inner loop for the iterations for n. Here is the code so far:
double series = 1.0;
for (int x=-3; x<=3; x++)
{
for (int n=1; n<=x+30; n++)
{
series = series + (x^n)/n;
n*=n;
}
System.out.println(""+ x +"\t "+ (series+1));
}
Any advice would be greatly appreciated.
You might have to pay more attention to every single variable and the value changes inside the loops to better verify your code.
You have done a few mistakes:
multiplied n to itself and then divided by n as well. According to your formula you need to power the number by n, not nFactorial. Moreover nFactorial is a large number to be stored (and get accurate results), better use the advantage of dividing by n and multiplying by x at every step (see below code).
You have also used the XOR operator. Instead use the Math.pow() method.
You need to initialize the series value in each inner loop start.
If you started with initial value of 1, do not need to print (series+1) at end. Better initialize it to 0, or remove +1 in the print.
The following code would solve your purpose better.
double series = 1.0;
double term = 1.0;
for (int x = -3; x <= 3; x++) {
series = 1.0;
term = 1.0;
for (int n = 1; n <= 30; n++) {
term = (term * x / n);
series += term;
}
System.out.println("" + x + "\t " + (series));
}
you can write your formula with MATH.POW like this:
for (int x=-3; x<=3; x++)
{
for (int n=1; n<=x+30; n++)
{
series = series + (Math.pow(x,n)/n);
n*=n;
}
System.out.println(""+ x +"\t "+ (series+1));
}
I'm trying to convert the math presented on this paper:
http://www.bouncingchairs.net/pskalman-lategecco.pdf
Around page 3 forward into working code. The algorithm itself is given
around page 6, but I don't speak greek or math; so for the time being I'm stuck.
If I understand the code, it should run down like this:
vt = particle velocity, 1d array
vbest = best particle velocity 1d array
v_prime = 1d storage array
v_hat = 1d storage array
alpha = 0.45
sigma = 0.60
denom = float
denom_best = float
Prep:
for(int i = 0; i < vt.length; i++)
{
denom += vt[i] ^ 2
denom_best += vbest[i] ^ 2
}
denom = denom ^ (1/2)
denom_best = denom_best ^ (1/2)
Equation 7:
for(int i = 0; i < vt.length; i++)
{
v_prime[i] = alpha * (vt[i]/denom) + (1 - alpha) * (vbest[i]/denom_best)
}
Equation 8:
for(int i = 0; i < vt.length; i++)
{
v_hat[i] = Rand_Gauss(v_prime[i], sigma) //gaussian random number with
//v_prime[i] average, and sigma StDev
}
Equation 9:
for(int i = 0; i < vt.length; i++)
{
vt[i] = (alpha * denom + (1 - alpha) * denom_best) * v_hat[i]
}
Is this even close to what the math is saying?
Thanks in advance,
-JW
I think you might be missing the calls to "norm(...)". Normalizing a vector is just dividing each component of the vector by the length. In Equation 9, they calculate the weighted sum of the lengths of vt and vbest and multiply that average length by norm(vbar). You're just multiplying it by vbar directly.
The intent behind that equation seems to be to create a new vector v_{t+1} whose length is the average length of vt and vbest. However, vhat could be any length at all, so the multiplication in Equation 9 most of the time won't give you the correct answer unless you force the length of the vhat vector to be exactly one. That's what the vector norm does.
The norm is just the components of the vector divided by the length. So replace your code for equation 9 with something like this:
vtlen=0
for(int i=0; i<vt.length; i++)
{
vtlen+=vt[i]*vt[i];
}
vtlen=sqrt(vtlen);
for(int i=0; i<vt.length; i++)
{
vt[i] = (alpha * denom + (1 - alpha) * denom_best) * (v_hat[i] / vtlen);
}
You've also ignored the norm operation in Equation 7. I haven't read the paper, but it may not be strictly necessary here as the weights sum to one and the vectors are already normalized. I'd have to spend a bit more time to convince myself one way or the other, but it certainly wouldn't hurt to go ahead and normalize that calculated v' vector too.
I need help writing a program that uses binary search to recursively compute a square root (rounded down to the nearest integer) of an input non-negative integer.
This is what I have so far:
import java.util.Scanner;
public class Sqrt {
public static void main(String[] args) {
Scanner console = new Scanner(System.in);
System.out.print("Enter A Valid Integer: ");
int value = console.nextInt();
calculateSquareRoot(value);
}
public static int calculateSquareRoot(int value) {
while (value > 0) {
double sqrt = (int) Math.sqrt(value);
System.out.println(sqrt);
}
return -1;
}
}
The fact that it has to use binary search to compute the square root is the part that is confusing me. If anyone has any suggestions on how to do this, it would be greatly appreciated. Thank you
Teh codez:
def sqrt(n):
low = 0
high = n+1
while high-low > 1:
mid = (low+high) / 2
if mid*mid <= n:
low = mid
else:
high = mid
return low
To understand it, just think of the loop invariant, namely:
lowlow <= n < highhigh
If you understand this code, writing a recursive version should be trivial.
You can use this java method (Iterative)
public class Solution {
// basic idea is using binary search
public int sqrt(int x) {
if(x == 0 || x == 1) {
return x;
}
int start = 1, end = x / 2;
while(start <= end) {
int mid = start + (end - start) / 2;
if(mid == x / mid) {
return mid;
}
if(mid < x / mid) {
start = mid + 1;
} else {
end = mid - 1;
}
}
return start - 1;
}
}
You can drive your own recursive method
Essentially the idea is that you can use binary search to get closer to the answer.
For example, say you are given 14 as an input. Then, you are sure that the square root of 14 is between 0 and 14. So, 0 and 14 are your current "boundaries". You bisect these two end points and obtain the mid point: 7. Then you try 7 as a candidate - If the square of 7 is greater than 14, then you have a new boundary (0,7); otherwise you would have a new boundary (7,14).
You keep repeating this bisection until you are "close enough" to the answer, for example you have a number square of which is within 14-0.01 and 14+0.01 - then you declare that as the answer.
OK, that much hint should be good enough for HW. Don't forget to cite StackOverflow.
I'm assuming this is homework so I'm only going to give a hint.
To conduct a binary search, you pick a point as close as possible the median of possible correct values. So the question becomes what is a typical median value for a square root, that is either constant or can be computed via multiplication. Obviously using an arbitrary constant will not work for most inputs, so you need to arrive at your guess by multiplying the input by a constant.
As for what that constant C to multiply by should be, that should be chosen based on what values you expect as input. For example, if you expect your inputs to be around 250,000, then:
C * 250,000 ~= sqrt(250,000)
C = sqrt(250,000) / 250,000
C = 500 / 250,000
C = 1 / 500
I see two important computing concepts in your question. The first is binary search, the second is recursion. Since this is homework, here is a contribution towards understanding a binary search, recursion and how to think about them.
Think of binary search as dividing the solution "space" in half, keeping the half the solution is in and doing that in succession so that the process converges on the solution. The key concepts for doing this are that you need to engineer a solution "space" that has the following properties:
1) can be subdivided, usually in half or at least two pieces
2) of the two pieces after subdivision, there is a way to determine which half has the solution so that the process can be repeated on only one half.
Recursion involves a function (method in O-O speak) invoking itself. Recursion works really well for a process that converges to a conclusion. It either recurses forever or until you run out of some resource, usually memory, and it fatally stops. The two key concepts for recursion are:
1) convergence through some invariance (more on invariance below).
2) termination condition (one that recognizes sufficient convergence).
Now, for your square root routine. The requirements for the routine are:
1) Integer input.
2) Integer square-root approximation that gives the floor integer closest to the actual square root.
3) Use recursion.
4) Use binary search.
It helps to know some mathematics about square roots for this. Elementary calculus and analytical geometry concepts are helpful too. Lets do some reasoning.
We have an arbitrary positive integer x. We want its root y. If we choose some test value for y, we can see if it is the root of x if y * y = x. If y is too big, y * y > x. if y is too small, y * y < x. We also know that 0 <= root <= x and that square-roots of 0 and 1 are trivially zero and 1. Since we are looking for largest integer where y * y <= x (i.e. a floor value) we'll have to account for that too.
Here is some mathematical reasoning to help. We know that x = y * y where y is the square root of x. That means: y = x/y.
Hmmm... what happens if y is to large to be the square root of x? Then: x < y * y and: x/y < y which means x/y is also too small to be the square root of x. So we know that, for y too large, x/y < square-root of x < y. So, lets find a new y, say y1, between x/y and y as a new test value. The average of x/y and y will do. y1 = (x/y0 + y0)/2 will give a y1 that is closer to the square root of x than y0 if y0 is too large.
Does this converge? Well, in mathematics using positive real numbers, the average will always be above the value but getting closer each iteration. This satisfies the condition that we successively divide the solution "space" into two parts and know which of the two to keep. In this case, we successively calculate new values below previous ones and below which the answer still lies, allowing us to discard all values above the new one. We stop when we reach a condition where no more new values above the answer exist. Using computers, however, results in binary approximations of real numbers. With integers, there is truncation in division. This may affect the convergence beneficially or adversely. In addition, your answer is supposed to be the largest integer smaller than or equal to the square root. It's wise to take a look at the kind of convergence we will get.
Because of integer division turncation, y1 = (x/y0 + y0)/2 will converge until successive iterations reach an integer root or a floor value for (i.e. the largest integer less than) the root. This is ideal. If we start with a proposed value for the root that has to be larger than the root, say x itself, the first value for yn where yn * yn <= x is the desired result.
The simple answer is that, when we start with y0 > y, the first new yn that is less than or equal to y, then y - yn < 1. That is, yn is now the floor value for which we've been looking and we now have a termination condition that exactly satisfies the conditions for the required answer.
Here are basic iterative and recursive solutions. The solutions don't incude safety features to ensure negative values are not input for x. The one major concern is to avoid dividing by zero in case someone wants to find the square root of 0. Since that is a trivial answer, both the recursive and iterative methods return 0 before division by zero can take place. Both the recursive and iterative solutions work with the trivial cases for finding the square roots of 0 and of 1.
There is another analysis that always has to be done with int and long arithmetic in Java. A major concern is integer overflow since Java does nothing about int or long overflow. Overflow results in twos-complement values (look that up elsewhere) that can lead to bogus results and Java does not throw exceptions with int or long overflow.
In this case, it is easy to avoid arithmetic that could result in an internal overflow with large values of x. If we create a termination condition such as y0 * y0 < x we risk overflow if x is greater than the square root of Integer.MAX_VALUE since y0 * y0, an intermediate value, will immediately exceed the maximum int value. However, we can rearrange the termination condition to y0 < x / y0. We still have a problem with the calculations: ((x / y0) + y0) / 2) if x and y0 are Integer.MAX_VALUE since it wll attempt Integer.MAX_VALUE + 1. However, we can always start with a value less than x that is guaranteed to be > y. x / 2 works for all values of x > 1. Since the square root of x where x is either 0 or 1 is simply x, we can easily test for those values and simply return the correct and trivial value. You can construct code to prevent using values < 0 or values > Integer.MAX_VALUE. The same can be applied if we use long instead of int. Welcome to computing in the real world!
public static int intSqRootRecursive (int x) {
// square roots of 0 and 1 are trivial and x / 2 for
// the y0 parameter will cause a divide-by-zero exception
if (x == 0 || x == 1) {
return x;
}
// starting with x / 2 avoids overflow issues
return intSqRootRecursive (x, x / 2);
} // end intSqRootRecursive
private static int intSqRootRecursive(int x, int y0) {
// square roots of 0 and 1 are trivial
// y0 == 0 will cause a divide-by-zero exception
if (x == 0 || x == 1) {
return x;
} // end if
if (y0 > x / y0) {
int y1 = ((x / y0) + y0) / 2;
return intSqRootRecursive(x, y1);
} else {
return y0;
} // end if...else
} // end intSqRootRecursive
public static int intSqRootIterative(int x) {
// square roots of 0 and 1 are trivial and
// y == 0 will cause a divide-by-zero exception
if (x == 0 || x == 1) {
return x;
} // end if
int y;
// starting with y = x / 2 avoids overflow issues
for (y = x / 2; y > x / y; y = ((x / y) + y) / 2);
return y;
} // end intSqRootIterative
You can test the recursive solution to find out how many instances will result on the frame stack, but you will see that it converges very fast. It's interesting to see that the iterative solution is much smaller and faster than the recursive one, something that is often not the case and is why recursion gets used where it can be predicted that stack resources are sufficient for the recursion depth.
Here is the recursive solution in Java using binary search :
public class FindSquareRoot {
public static void main(String[] args) {
int inputNumber = 50;
System.out.println(findSquareRoot(1, inputNumber, inputNumber));
}
public static int findSquareRoot(int left, int right, int inputNumber){
// base condition
if (inputNumber ==0 || inputNumber == 1){
return inputNumber;
}
int mid = (left + right)/2;
// if square of mid value is less or equal to input value and
// square of mid+1 is less than input value. We found the answer.
if (mid*mid <= inputNumber && (mid+1)*(mid+1) > inputNumber){
return mid;
}
// if input number is greater than square of mid, we need
// to find in right hand side of mid else in left hand side.
if (mid*mid < inputNumber){
return findSquareRoot(mid+1, right, inputNumber);
}
else{
return findSquareRoot(left, mid-1, inputNumber);
}
}
}
Iterative binary solution:
public static double sqrt(int n) {
double low = 0;
double high = n;
double mid = (high - low) / 2;
while (Math.abs((mid * mid) - n) > 0.000000000001) {
if ((mid * mid) > n) {
high = mid;
mid = (high - low) / 2;
} else{
low = mid;
mid = mid + ((high - low) / 2);
}
}
return mid;
}
edst solution is good, but there is a mistake in line 11:
mid = (high - low) / 2;
should be
mid = low + (high - low) / 2;