I have a non-linear equation defined as p^(n-1) * [1-(1-p)^n] * r - c = 0, where n, r, and c are given integers. I want to solve for p in the C programming language and decided to use Newton's method. I am wondering if this is the most suitable approach, and if there are other ways to solve this. (One important note that p stands for probability here, so it ranges from 0 to 1!)
Here is my current implementation, and it seems to work for some test cases but fails for others:
#include <stdio.h>
#include <math.h>
#define ld long double
#define TOLERANCE 1e-7
#define ITERATIONS 1000
ld function(ld p, int n, int c, int r) {
return pow(p, n - 1) * (1 - pow(1 - p, n)) * r - c;
}
ld derivative(ld p, int n, int r) {
return r * (n - 1) * pow(p, n - 2) * (1 - pow(1 - p, n)) + r * n * pow(p, n - 1) * pow(1 - p, n - 1);
}
ld pFinder(int n, ld c, ld r) {
ld p = 0.5;
if (n == 0) {
return 0;
}
for (int i = 0; i < ITERATIONS; i++) {
ld fValue = function(p, n, c, r);
ld fPrime = derivative(p, n, r);
p = p - fValue / fPrime;
if (fabs(fValue) < TOLERANCE) {
return p;
}
}
printf("Didn't converge after %d iterations", ITERATIONS);
return -1;
}
int main() {
int n, c, r;
scanf("%d %d %d", &n, &c, &r);
ld p = pFinder(n, c, r);
printf("%.15Lf\n", p);
return 0;
}
The issue arises with inputs like 18 30 2, where the result is 1.999993641483941, which is outside the valid range of [0, 1]. I do not understand why this is happening.
The constraints are 1 <= n <= 20, 1 <= c <= 10000, 1 <= r <= 10000, and the absolute error of p is up to 10^-6.
I appreciate any insights into what might be going wrong and how to improve my code!
P.S. This is my first time posting something here, and I am pretty new to coding, so I hope you don't mind that I'm not proficient in Markdown syntax!
For p to be in [0,1], c/r must also be in [0,1]. In that case, if you start with p = 1 (instead of p = 0.5), Newton's method will be well-behaved and will very quickly arrive at the desired precision. Much faster than bisection. The number of iterations will be small. For your tolerance, I can't get them to be more than 16. Usually just a handful.
In your example "10 30 2", c/r is 15, which is not in [0,1]. Check for c <= r before proceeding. (Your c >= 1 and r >= 1 already assures that c/r >= 0.)
Your use of
pow()eliminates any precision advantage of using long doubles. Usepowl()instead. Alsofabsl().To update, you only need two
powl()'s, which are slow. I would:If I had to do this a lot, I would use an integer power algorithm. It's about three times as fast as
powl(), which handles non-integer exponents that do not occur here.