As this is a transcendent equation, finding all roots is not an option. You cannot (in general) find an expression that gives a closed-form for the roots of the equation. So you need to do some analysis first. Plotting the function, or plotting the left-hand-side and right-hand-side of the equation (and hence, the intersections of both curves are the roots) will give you some hints on how to tackle the problem. I define the LHS as $\tan(x)$ and the RHS as $\frac{2x}{x^{2}-1}$.
What you can observe is the following:
- Since both LHS and RHS are (anti-)symmetric around $x=0$, you only need to consider the positive $x$ domain.
- There is no root in the interval $[0,1[$ ($x=1$ being the location of the pole of the RHS function).
- There is a root in the interval $[1,\frac{\pi}{2}]$.
- Since the function of the RHS is monotonically decreasing for $x > 1$, there will be a single root in each interval $[(2k-1)\frac{\pi}{2},(2k+1)\frac{\pi}{2}]$ for $k=1,2,3, \ldots$.
- You can see that the RHS function goes to zero for $x\to\infty$. This implies that for larger $x$, the solutions will convergence to the zero crossings of the tangent function (i.e. $r_{l} \approx (l+1)\pi$, where $r_{l}$ is the $l$'-th root. You could use these estimates as initial guess for an iterative method.
So what will be the strategy to find (not all, but an arbitrary number) of roots? You use an iterative solving method in each interval. Now you have the choice between a plethora of methods but two methods seem very appropriate:
- Since you have a very good estimate for the root (a good initial guess), you could use Newton-Raphson. But then you need to calculate the derivative (in this case, you could do it analytically).
- Since each root is known to be in an interval, you could use the derivative-free method of Dekker-Brent. Just make sure that you don't use the strict boundaries for each interval, because they are poles of the tangent function and Dekker-Brent will not know how to handle that. Add some margin to the initial interval.
Using the Python code below, I can find the first $N$ roots using this approach. A plot for the interval [200,300] is given as an example. Since I'm lazy and didn't want to calculate the derivative, I chose the Dekker-Brent option (where I asked for a relative tolerance of $1e-14$. For what it's worth, the first 1000 roots took me 10 ms on an Intel i5 laptop. Of course, you can validate the results by calculating the residues (the function value in each calculated root).
Python code:
import numpy as np
import matplotlib.pyplot as plt
from scipy.optimize import brentq
plt.figure(figsize=(12,8))
plt.style.use('bmh')
x = np.linspace(0,20,1002)
plt.ylim(-5,5)
plt.plot(x,np.tan(x),label='$tan(x)$')
plt.plot(x,2*x/(x*x-1),label='$2x/(x^2-1)$')
plt.legend(loc=1)
plt.savefig('fun.png',dpi=300,bbox_inches = 'tight')
def f(x):
# The function
return np.tan(x)-2*x/(x*x-1)
def roots(N):
# Find N roots of the equation
#Allocate space
roots = np.zeros(N)
#Margin to stay away from poles
margin = 1e-8
#First root
roots[0] = brentq(f, 1.0 + margin, np.pi/2 - margin, rtol=1e-14)
#Subsequent N-1 roots
for i in range(1,N):
left = (2*i - 1)*np.pi/2
right = (2*i + 1)*np.pi/2
roots[i] = brentq(f, left + margin, right - margin, rtol = 1e-14)
return roots
result = roots(1000)
left = 200
right = 300
plt.figure(figsize=(24,8))
plt.style.use('bmh')
x = np.linspace(left,right,(right-left)*101)
plt.ylim(0,2e-2)
plt.plot(x,np.tan(x),label='$tan(x)$')
plt.plot(x,2*x/(x*x-1),label='$2x/(x^2-1)$')
rts = result[result>=left]
rts = rts[rts<=right]
plt.plot(rts,np.tan(rts),'o', markersize=14, fillstyle='none', label='roots')
plt.legend(loc=1)
plt.savefig('roots1.png',dpi=300,bbox_inches = 'tight')