2

Problem:

I'm looking for a catch-all function that I can use to calculate the tangent of any polynomial function at x. I'm indifferent to the language used although JavaScript or Python would be prefered! I should be able to pass in any x value and an array of coefficients in the format, a + bx + cx^2 + dx^3 ... and so on.

Example function format:

function findTangent(x, coefficients) {

  // Do differential calculus here.

  return [tangentIntercept, tangentSlope]

}

Example function test:

Say I have the function, y = 2 + 7x + 5x^2 + x^3 and I want to to find the tangent at, x = -2. I could call this function like so, findTangent(-2, [2, 7, 5, 1]) and get a return value like this, [-2, -1] representing the tangent, y = -2 - x.

Notes:

I have looked for an answer on the Math Stackexchange and Google search but all the results are in a mathematical syntax and not code. I want a programmatic solution, I am far more comfortable with loops and if statements than funny symbols and math jargon!

Jacob Philpott
  • 538
  • 1
  • 8
  • 24

4 Answers4

4

Okay so after a day of struggling with it I think I have got the solution in both JavaScript and Python!

The JavaScript Solution:

function findTangent(x, coefficients) {

  let slope = 0
  let intercept = coefficients[0]

  for (let i = 1; i < coefficients.length; i++) {

    slope += coefficients[i] * i * Math.pow(x, i - 1)
    intercept += coefficients[i] * Math.pow(x, i)

  }

  return [intercept - slope * x, slope]

}

The Python Solution:

def find_tangent(x, coefficients):

    slope = 0
    intercept = coefficients[0]

    for i, coefficient in enumerate(coefficients):

        if i != 0:

            slope += coefficient * i * pow(x, i - 1)
            intercept += coefficient * pow(x, i)

    return [intercept - slope * x, slope]

I have tested the results against the Symbolab Tangent Calculator and they seem to be okay but please let me know if you find any errors! Also, I would love to see results in other languages so if you have a prefered language that's not mentioned here don't hesitate to post!

Jacob Philpott
  • 538
  • 1
  • 8
  • 24
  • 2
    "I am far more comfortable with loops and if statements than funny symbols and math jargon!" - LOL. Maybe `Array.prototype.reduce()` because that looks cooler! :-) Looks stellar - does exactly what your formula says. – Randy Casburn Jan 30 '21 at 00:38
  • Thanks @RandyCasburn, I was definitely pulling out my hair at some points but I think this is the most elegant and accurate solution I can come up with. – Jacob Philpott Jan 30 '21 at 00:49
2

Since you asked about other languages, here is a C function to compute the derivative (and value) of a polynomial at a point. The method, which I recommend, could be used in any language.

double  pol_eval_d( double x, int deg, const double* c, double* pdp)
{
double  p = c[deg];
double  dp = 0.0;
    for( int d=deg-1; d>=0; --d)
    {   dp = fma( dp, x, p);
        p = fma( p, x, c[d]);
    }
    *pdp = dp;
    return p;
}

This function takes the x value, the degree and coefficients of the polynomial and returns the value of the polynomial at x, and the value of its derivative in *pdp.

The coefficients are c[0] (power 0), c[1] (power 1), .. and c[deg] (power deg).

It calls the (C math library) function fma, for which

fma(x,y,z) = x*y+z, except that it is evaluated with extra precision.

If you don't have such a function you could replace the calls with the expression above, though you would lose a little accuracy.

The method used is Horner's method. This is generally faster and more accurate than other methods for evaluating polynomials.

To explain how it works, first consider the case where we didn't want the derivative. Then one could write

double  pol_eval( double x, int deg, const double* c)
{
double  p = c[deg];
    for( int d=deg-1; d>=0; --d)
    {   p = fma( p, x, c[d]);
    }
    return p;
}

If we step through what happens with a quadratic, and replace the fma calls with their mathematical equivalent we get

p = c[2]
p = p*x + c[1]
p = p*x + c[0]

That is we have evaluated

c[0]+x*c[1]+x*x*c[2] 

by

c[0] + x*(c[1] + x*c[2])

This is Horner's method.

To compute the derivative too, we differentiate each term in pol_eval. Initially p is a constant, so it's derivative is 0. Then when we update p by

p = fma( p, x, c[d]);

or in mathematical terms

p = p*x + c[d];

we differentiate that, using the product rule, so since c[d] is a constant

dp = dp*x + p

and note that we must do this before updating p

dmuir
  • 4,211
  • 2
  • 14
  • 12
1

Using Python Sympy which allows symbolic differentiation

Code

from sympy import Function, Symbol

def polynomial(coeficents, degrees, x):
    '''
        Evaluate polynomial
        
        Example
            coefficients = [1, 2, 4]
            degrees = [2, 1, 0]
            
            corresponds to polynomial x^2 + 2*x + 4
    '''
    return sum([coeficents[i]*x**degrees[i] for i in range(len(coeficents))])
         
# Using OP polynomial
coefficients = [1, 5, 7, 2]
degrees = [3, 2, 1, 0]
print(polynomial(coefficients, degrees, -2))  # Output: 15

# Create symbolic polynomial in x
# Define symbolic variable x
x = Symbol('x')  # symbolic variable x

# Create polynomial in x for OP polynomial
poly = polynomial(coefficients, degrees, x)
print(poly)                                  # Output: x**3 + 5*x**2 + 7*x + 2
# Evaluate at x = -2
print(poly.subs(x, -2))                      # Output: 7  (i.e. substitute x for 1 in equation)

####################################################
# Symbolic differentiation of polynomial 'poly'
####################################################
diff_poly = poly.diff(x)
print(diff_poly)                             # Output: 3*x**2 + 10*x + 7 (derivative of polynomial)
# Evaluate derivative at x = -2
print(diff_poly.subs(x, -2))                 # Output: -1   (derivate at x = -1)
DarrylG
  • 16,732
  • 2
  • 17
  • 23
1

The autoaccepted answer is just fine and here is my take;

For better efficiency the following code can be put under a single .reduce() statement however I would like to keep it in it's extended form to help clarification;

function tangent(x,as){
  var y = as.reduce((p,c,i) => p+c*x**i),     // find the y value at x
      m = as.map((a,i) => a*i)                // find the derivative
            .reduce((p,c,i) => p+c*x**(i-1)); // find the slope m at x
  return [y-m*x,m];                           // find the line from y & m
}

var line = tangent(-2, [2, 7, 5, 1]);
console.log(`The equation of the tangent line is "y = ${line[1]}x${(Math.sign(line[0])+"")[0]}${Math.abs(line[0])}"`);
Redu
  • 25,060
  • 6
  • 56
  • 76
  • 1
    Nice, I like that you printed the result in a readable string rather than just returning the raw numbers! – Jacob Philpott Apr 11 '22 at 07:00
  • I think it would be a fun addition to add speed to the problem and see which function/language can calculate tangents the fastest! – Jacob Philpott Apr 11 '22 at 07:04
  • Might try to run some speed tests this week if I find the time – Jacob Philpott Apr 11 '22 at 07:06
  • 1
    @JacobPhilpott JS is not a really functional language. I think higher level abstractions like map and reduce won't cut the imperative loops. However while i would expect your code to be faster the task is so simple that you wont feel much unless you do thousands if not millions of tangent calculation. – Redu Apr 11 '22 at 09:40