I wrote a n-section algorithm for finding roots of a function. The working principle is exactly the same as is bisection method, only the range is divided into N equal parts instead. This is my C code:
/*
Compile with: clang -O3 -o nsect nsect.c -Wall -DCOUNT=5000000 -DNSECT=? -funroll-loops
*/
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#define N 6
#ifndef COUNT
#error COUNT not defined!
#endif
#ifndef NSECT
#define NSECT 2
#endif
float polynomial[N];
float horner( const float poly[N], float x )
{
float val = poly[N-1];
for ( int i = N - 2; i >= 0; i-- )
val = poly[i] + x * val;
return val;
}
float f( float x )
{
return horner( polynomial, x );
}
float nsect( float a, float b, const float eps_x )
{
float fa = f( a );
float fb = f( b );
if ( fa == 0 ) return a;
else if ( fb == 0 ) return b;
else if ( fa * fb > 0 ) return 0;
float x[NSECT];
float fx[NSECT];
while ( b - a > eps_x )
{
x[0] = a;
if ( ( fx[0] = f( x[0] ) ) == 0 ) return x[0];
int found = 0;
for ( int i = 0; i < NSECT - 1; i++ )
{
x[i + 1] = a + ( b - a ) * (float)( i + 1 ) / NSECT;
if ( ( fx[i + 1] = f( x[i + 1] ) ) == 0 )
return x[i + 1];
else if ( fx[i] * fx[i + 1] < 0 )
{
a = x[i];
b = x[i + 1];
found = 1;
break;
}
}
if ( !found )
a = x[NSECT - 1];
}
return ( a + b ) * 0.5f;
}
int main( int argc, char **argv )
{
struct timeval t0, t1;
float *polys = malloc( COUNT * sizeof( float ) * N );
float *p = polys;
for ( int i = 0; i < COUNT * N; i++ )
scanf( "%f", p++ );
float xsum = 0; // So the code isn't optimized when we don't print the roots
p = polys;
gettimeofday( &t0, NULL );
for ( int i = 0; i < COUNT; i++, p += N )
{
memcpy( polynomial, p, N * sizeof( float ) );
float x = nsect( -100, 100, 1e-3 );
xsum += x;
#ifdef PRINT_ROOTS
fprintf( stderr, "%f\n", x );
#endif
}
gettimeofday( &t1, NULL );
fprintf( stderr, "xsum: %f\n", xsum );
printf( "%f ms\n", ( ( t1.tv_sec - t0.tv_sec ) * 1e6 + ( t1.tv_usec - t0.tv_usec ) ) * 1e-3 );
free( polys );
}
EDIT: This is the command I used to compile the code: clang -O3 -o nsect nsect.c -Wall -DCOUNT=5000000 -DNSECT=? -funroll-loops.
I ran everything on i7-8700k.
I decided to test the algorithm performance for different N values. The test consists of measuring time needed to find any root in range (-100;100) for each of 5,000,000 polynomials of degree 5. The polynomials are randomly generated and have real coefficients ranging from -5 to 5. The polynomial values are calculated using Horner's method.
These are results I got from running the code 10 times for each N (x=N, y=time [ms]):
My understanding of the worst case performance here is that the amount of work to be done in the main while loop is proportional to N. The main loop needs logN(C) (where C > 1 is a constant - ratio of initial search range to requested accuracy) iterations to complete. This yields following equation:
The plot looks very similar to the violet curve I used above to roughly match the data:
Now, I have some (hopefully correct) conclusions and questions:
Firstly, is my approach valid at all?
Does the function I came up with really describe the relation between number of operations and N?
I think this is the most interesting one - I'm wondering what causes such significant difference between N=2 and all the others? I consistently get this pattern for all my test data.
Moreover:
That function has minimum in e≈2.718..., which is closer to 3 than to 2. Also, f(3) < f(2) holds. Given that the equation I came up with is correct, I think that would imply that trisection method may actually be more efficient than bisection. It seems a bit counter intuitive, but the measurements seem to acknowledge that. Is this right?
If so, why does trisection method seem to be so unpopular compared to bisection?
Thank you
I am commenting on the general question, not on your particular code, which I don't fully understand.
Assuming that there is a single root known to be in an interval of length L and the desired accuracy is ε, you will need log(L/ε)/log(N) subdivision stages. Every subdivision stage requires N-1 evaluations of the function (not N), to tell which subinterval among N contains the root.
Hence, neglecting overhead, the total cost is proportional to (N-1)/log(N). The values of this ratio are, starting from N=2:
1.44, 1.82, 2.16, 2.49, 2.79...
and higher.
Hence the theoretical optimum is with N=2. This is why trisection is not used.
Related
I am performing an activity, which is based on the recursive function, but I cannot understand how to put a part in code format. The code is this :
#include <stdio.h>
int count;
int g( int x ) {
count++;
if ( x == 0 )
return 2;
if ( x <= 2 )
return x * x;
if ( x <= 5 )
return x * g( x - 1 );
return (g( x - 3 ) + g( x - 2 ) );
}
int main () {
int k = 15;// random number
count = 0;
printf( " g(%d) = %d\n", k, g( k ) );
printf( " count: %d\n", count);
printf( " g(9) appeared : %d time(s) inside g(%d)",...,k);// in this space would be the other variable
return 0;
}
What I don't understand is how to calculate how many times, for example g(9), appears inside g(k). I've already made some attempts, but it never arrives at what is really requested. Thanks in advance
I am trying to compute the integral of the function f(x)=(1-x^2)^(1/2) from x=0 to x=1. The answer should be approximately pi/4. I am currently getting 2.
My current implementation of the trapezoidal rule is the following:
double
def_integral(double *f, double *x, int n)
{
double F;
for (int i = 0 ; i < n ; i++) {
F += 0.5 * ( x[i+1] - x[i] ) * ( f[i] + f[i+1] );
}
return F;
}
I'm creating N divisions to approximate the area under the curve between x_1=0 and x_N=1 by looping through i to N with x_i = i / N.
int
main(int argc, char **argv)
{
int N = 1000;
double f_x[N];
double x[N];
for (int i = 0 ; i <= N ; i++) {
double x = i * 1. / N;
f_x[i] = sqrt(1. - pow(x, 2.));
//printf("%.2f %.5f\n", x, f_x[i]); //uncomment if you wanna see function values
}
double F_x = def_integral(f_x, x, N);
printf("The integral is %g\n", F_x);
}
The result of 2 that I am currently getting should be dependent on the number of N division, however, no matter if I make N=10000 or N=100, I still get 2.
Any suggestions?
In this for loop, you forgot updatin array x as well.
for (int i = 0 ; i <= N ; i++) {
double x = i * 1. / N;
f_x[i] = sqrt(1. - pow(x, 2.));
//printf("%.2f %.5f\n", x, f_x[i]); //uncomment if you wanna see function values
}
So, for loop should be replaced by
for (int i = 0 ; i <= N ; i++) {
double xi = i * 1. / N;
x[i] = xi;
f_x[i] = sqrt(1. - pow(xi , 2.));
//printf("%.2f %.5f\n", x, f_x[i]); //uncomment if you wanna see function values
}
In your main code, you call def_integral with a double (x) and in the function an array of x (double * x) is expected. Perhaps (it is what I suppose), the problem comes from the fact you formula needs x(i+1)-x(i) but you use a constant step. Indeed, x(i+1)-x(i)=step_x is constant so you do not need each x(i) but only value : 1./N
Other remark, with a constant step, your formula could be simplified to : F_x=step_x* ( 0.5*f_x(x0)+ f_x(x1)+...+f_x(xn-1)+ 0.5*f_x(xn) ) . It helps to simplify the code and to write a better efficient one.
Everything is commented in the code above. I hope it could help you. Best regards.
#include <stdio.h>
#include <math.h>
double
def_integral(double *f, double step_x, int n)
{
double F;
for (int i = 0 ; i < n ; i++) {
F += 0.5 * ( step_x ) * ( f[i] + f[i+1] );
}
return F;
}
int main()
{
int N = 1001; // 1001 abscissas means 1000 intervalls (see comment on array size and indices)
double f_x[N]; // not needed for the simplified algorithm
double step_x = 1. / N; // x(i+1)-x(i) is constant
for (int i = 0 ; i < N ; i++) { // Note : i<N and not i<=N
double xi = i * step_x; // abscissa calculation
f_x[i] = sqrt((1. - xi )*(1. + xi )); // cf chux comment
}
double F_x = def_integral(f_x, step_x, N);
printf("The integral is %.10g\n", F_x);
// simplified algorithm
// F_x=step_x*( 0.5*f_x(x0)+f_x(x1)+...+f_x(xn-1)+0.5f_x(xn) )
double xi;
xi=0; // x(0)
F_x=0.5*sqrt((1. - xi )*(1. + xi ));
for (int i=1 ; i<=N-1 ; i++) {
xi=step_x*i;
F_x+=sqrt((1. - xi )*(1. + xi ));
}
xi=step_x*N;
F_x+=0.5*sqrt((1. - xi )*(1. + xi ));
F_x=step_x*F_x;
printf("The integral is %.10g\n", F_x);
}
Here is the question:
Write a program to find the real roots of the following equation using the Secant method:
f(x) = 23x^4 -13x^3 + 3x^2 - 5x + 38.
Let h = r(i) - r(i-1), where r(i) is the root computed in iteration i of your program. Your program should continue to refine its answer until h < 10 - 3. This value is known as the convergence criterion. Your program should print the final value of the root and the number of iterations required to compute it.
Here is my program:
#include <stdio.h>
double function ( double i );
int main ()
{
double x_1, x_2, h_x, temp, a, b;
int count = 0;
printf("Enter first approximation: ");
scanf("%lf", &x_1);
printf("Enter second approximation: ");
scanf("%lf", &x_2);
a = function ( x_1 );
b = function ( x_2 );
h_x = x_2 - x_1;
if ( h_x < 0 )
{
h_x = ( h_x < 0 )? - h_x: h_x;
}
while ( h_x >= ( 1.E-3 ) && count <= 999999999 )
{
a = function ( x_1 );
b = function ( x_2 );
temp = ( ( x_1 * b ) - ( x_2 * a ) / ( b - a ) );
x_1 = x_2;
x_2 = temp;
printf("%lf\n", x_1);
printf("%lf\n", x_2);
count += 1;
h_x = x_2 - x_1;
if ( h_x < 0 )
{
h_x = ( h_x < 0 )? - h_x: h_x;
}
}
printf("Final value of root is %lf\n", x_2);
printf("%d iterations were required to compute it\n", count);
return 0;
}
double function ( double i )
{
double result;
result = ( 23 * i * i * i * i ) - ( 13 * i * i * i ) + ( 3 * i * i ) - ( 5 * i ) + 38;
return result;
}
The problem with my code is that it doesn't work for any other input that is different from 0 and 1.
I don't see what's the problem with my code though, I have added a code for absolute error, and the formula looks right to me. It isn't logical to me that with different initial guesses, the root returned is different, and just loops forever.
I would just like to know if it is a coding mistake, or a math error. Or is it that the roots oscillate? I just don't know what's wrong with my program. Can anyone help me out? Thanks.
Your midpoint formula is off. It seems to be a mix of two variants of the formula. The formula close to the derivation as root of the secant line
y = f(x_2)+(x-x2) * (f(x2)-f(x1))/(x2-x1)
gives
x3 = x2 - f(x2)*(x2-x1)/(f(x2)-f(x1)).
Bringing everything into one fraction gives the equivalent
x3 = (x1*f(x2) -x2*f(x1)) / (f(x2)-f(x1)).
You get a shorter formula for the polynomial using the Newton-Horner trick,
f(x) = ((( 23 * x - 13 ) * x + 3 ) * x - 5 ) * x + 38.
You should avoid the repeated computation of the same quantity, even if it does not have any noticeable effect here. Declare variables for the values f(x1) and f(x2).
Use the absolute value function to compute absolute values. hx = fabs(x2-x1). Or use hx=(hx<0)?-hx:hx; to avoid including math.h.
I want to implement the pseudo-random number generator in xv6. I am trying to implement Linear congruential generator algorithm, but I am not getting how to seed it. Here is the piece of my code. I know this code won't work because X is not changing globally. I am not getting how doing that.
static int X = 1;
int random_g(int M)
{
int a = 1103515245, c = 12345;
X = (a * X + c) % M;
return X;
}
Incorrect code.
Do not use % on X, the random state variable, to update the state. Use % to form the return value.
Use unsigned types to avoid signed integer overflow (UB) - Perhaps unsigned, unsigned long, unsigned long long. Wider affords a longer sequence.
To match a = 1103515245, c = 12345, we want m = 31.
static unsigned long X = 1;
int random_g(int M) {
const unsigned long a = 1103515245, c = 12345;
#define m 0x80000000
int r = (X % M) + 1; // [1 ... M]
X = (a * X + c) % m;
return r;
}
Additional code needed to remove the typical M bias. Many SO post on that.
Ref: Why 1103515245 is used in rand? and http://wiki.osdev.org/Random_Number_Generator
I don't know how much that helps you, but if you have an Intel Ivy Bridge or later generation processor, you could try to use the RDRAND instruction. Something along these lines:
static int X;
int
random_g (int M)
{
asm volatile("byte $0x48; byte $0x0F; byte $0xC7; byte $0xF0"); // RDRAND AX
asm volatile("mov %%ax, %0": "=r"(X)); // X = rdrand_val
int a = 1103515245, c = 12345;
X = (a * X + c) % M;
return X;
}
I haven't tested the above code, as I can't build xv6 right now, but it should give you a hint as to how you can work; utilising your processor's rng.
In the following code, random_g is a self-seeding random number generator that returns values between 1 and M. The main function tests the function for the specific case where M is 8.
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include <time.h>
int random_g( int M )
{
static uint32_t X = 1;
static bool ready = false;
if ( !ready )
{
X = (uint32_t)time(NULL);
ready = true;
}
static const uint32_t a = 1103515245;
static const uint32_t c = 12345;
X = (a * X + c);
uint64_t temp = (uint64_t)X * (uint64_t)M;
temp >>= 32;
temp++;
return (int)temp;
}
int main(void)
{
int i, r;
int M = 8;
int *histogram = calloc( M+1, sizeof(int) );
for ( i = 0; i < 1000000; i++ )
{
r = random_g( M );
if ( i < 10 )
printf( "%d\n", r );
if ( r < 1 || r > M )
{
printf( "bad number: %d\n", r );
break;
}
histogram[r]++;
}
printf( "\n" );
for ( i = 1; i <= M; i++ )
printf( "%d %6d\n", i, histogram[i] );
free( histogram );
}
This is the code I have so far, which is a little messy since I am still trying to figure out how to set it up, but I cannot figure out how to get the output. This code is supposed to take a Taylor Series polynomial of an exponential, and check the amount of iterations it takes to get the approximation.
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
/*Prototype for functions used*/
double factorial (int);
int main()
{
double input = 0;
double exp_val;
double delta = 1;
int f =0;
int n = 0;
double taylor;
int total;
printf("Plese enter the exponent to check for convergence:\n");
scanf("%lf", &input);
exp_val = exp(input);
printf(" # Iter e^X Sum Diff\n");
printf("---- ------ ------- ----- --------");
while(delta > 0.00001)
{
f = factorial(n);
taylor = ((pow(input,n))/ f);
delta = (exp_val - taylor);
printf("%d %f %f %f/n", (n+1), exp_val, taylor, delta);
n++;
}
system("pause");
}
double factorial (int n)
{
int r = 0;
int sum = 1;
int total = 0;
if (n == 0)
return total =1;
else
{
for(r; r<n; r++)
{
sum = sum * r;
total = sum + 1;
}
return total;
}
}
Here, I have fixed it, without changing your approach, except for the parts I really had to. One thing we have to clarify before the code is how Taylor Polynomials are made. It is not the first term plus the nth term, rather the sum of all terms from the first term till the nth term. So you definitely have to increase the taylor variable by the current nth term instead of the other way.
Here's the code, with brief comments in it as the explanation:
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
/*Prototype for functions used*/
unsigned long long factorial( int ); // <-- made it return unsigned long long
int main( )
{
double input = 0;
double exp_val;
double delta = 1;
unsigned long long f = 0; // <-- changed its type
int n = 0;
double taylor = 0; // <-- initialized with 0
printf( "Plese enter the exponent to check for convergence:\n" );
scanf( "%lf", &input );
exp_val = exp( input );
printf( " # e^X Sum Diff\n" ); // <-- made some cosmetic changes
printf( "--- --------- --------- ---------\n" ); // <-- added \n
while ( delta > 0.00001 )
{
f = factorial( n );
taylor += ( ( pow( input, n ) ) / f ); // += instead of =
delta = ( exp_val - taylor );
printf( "%2d %12f %12f %12f\n", ( n + 1 ), exp_val, taylor, delta ); // <-- replaced / with \ before the n
n++; // and made some edits to make it look better
}
system( "pause" );
return 0; // <-- better add this
}
unsigned long long factorial( int n ) // <-- made it return unsigned long long
{
int r = 0;
unsigned long long sum = 1; // <-- changed its type
if ( n == 0 )
return sum; // <-- this
else
{
for ( r; r<n; r++ )
{
sum *= r + 1; // <-- changed this
}
return sum; // <-- and this
}
}
You have to keep in mind that you may not input too high values to it. Anything higher than input == 4 kind of breaks it, because, you see, even with 4, it can reduce the error delta beneath the threshold first only with the 19th cycle. The programme seemingly fails with n == 5 due to inaccurate calculation of pow( 5, 21 ) / factorial( 21 ) when n reaches 21:
0.000034 // the result this programme finds
0.0000093331055943447405008542892329719 // the result Calculator finds
So, yeah... If you want this programme to work with bigger input values, you'll need a better approach. Not calculating the nth term from scratch and calculating it from the (n - 1)th term instead could help until somewhat bigger input values, as the others had said.
A couple issue:
Change int r = 0; ... for(r; r<n; r++) to int r; ... for(r=1; r<=n; r++) or int r = 1; ... for(; r<=n; r++)
Change printf("%d %f %f %f/n" to printf("%d %f %f %f\n" Add \n
Change "... --------" to "... --------\n"
Change delta = (exp_val - taylor); to delta = fabs(exp_val - taylor);
Change to double taylor = 0.0; Initialize it.
Change to taylor += ((pow(input,n))/ f); Note: +=
Minor: "Please" not "Plese".
Minor: Drop int total;