I wrote the C program below using VSCode on my old 64 bit Windows 10 Enterprise computer. It worked perfectly when I ran it on there, and it also works perfectly when I run it on this online C compiler. However, when I try to run it on VSCode on my new 64 bit Windows 11 Home computer, it exits with code=3221225477. Does anyone know what I should do about this? Thanks.
What I've tried so far:
I've tried writing programs to test each of the functions in it and they all ran perfectly fine on VSCode on my new computer; I'm only getting problems when I run the entire program
Because my new computer has Norton installed on it, it likes to think that some of my C programs are viruses and stops them from being executed. However, I made a folder that I have told it not to interfere with and copied my program there. I still got code=3221225477 when I ran it.
/*
File name : triangulation.c
Author : kene02
Last modified : 20/12/2021
License : All rights reserved
Description:
Uses triangulation to find where a point is.
*/
#define _USE_MATH_DEFINES
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
// Function declarations
float distance_between(float *point_1, float *point_2);
float find_gradient(float *point_1, float *point_2);
char *bearing(float *point_1, float *point_2);
float *triangulate(float *a, float *b, float *c, float *d);
int main()
{
// Allocating memory for coordinates
float *point_a = (float *)malloc(2*sizeof(float));
float *point_b = (float *)malloc(2*sizeof(float));
float *point_c = (float *)malloc(2*sizeof(float));
float *point_d = (float *)malloc(2*sizeof(float));
point_a[0] = 328;
point_a[1] = -1445;
point_b[0] = 325;
point_b[1] = -1455;
point_c[0] = 286;
point_c[1] = -1567;
point_d[0] = 292;
point_d[1] = -1575;
// Print coordinates
printf("\nA(%.1f, %.1f)\t", point_a[0], point_a[1]);
printf("B(%.1f, %.1f)\n", point_b[0], point_b[1]);
printf("C(%.1f, %.1f)\t", point_c[0], point_c[1]);
printf("D(%.1f, %.1f)\n\n", point_d[0], point_d[1]);
// Evaluate point of intersection
float *point_x = triangulate(point_a, point_b, point_c, point_d);
// Evaluate bearings
char *bng_ba = bearing(point_b, point_a);
char *bng_dc = bearing(point_d, point_c);
char *bng_xa = bearing(point_x, point_a);
char *bng_xc = bearing(point_x, point_c);
if (strcmp(bng_ba, bng_xa) != 0 || strcmp(bng_dc, bng_xc) != 0) {
printf("Lines do not intersect.\n\n");
} else {
float length_xa = distance_between(point_x, point_a);
float length_xc = distance_between(point_x, point_c);
printf("Point of intersection\t\t\t: ");
printf("X(%.1f, %.1f)\n", point_x[0], point_x[1]);
printf("Distance and bearing of X from A\t: ");
printf("%.1f units, %s\n", length_xa, bng_xa);
printf("Distance and bearing of X from C\t: ");
printf("%.1f units, %s\n\n", length_xc, bng_xc);
}
return 0;
}
/**
* #brief distance_between function: finds the distance between two points.
*
* #param point_1 the (x, y) coordinates of the 1st point
* #param point_2 the (x, y) coordinates of the 2nd point
* #return the distance between the two points
*/
float distance_between(float *point_1, float *point_2)
{
float x_diff = point_2[0] - point_1[0];
float y_diff = point_2[1] - point_1[1];
float distance = sqrt(pow(x_diff, 2) + pow(y_diff, 2));
return distance;
}
/**
* #brief find_gradient function: finds the gradient of the line between two
* points.
*
* #param point_1 the (x, y) coordinates of the 1st point
* #param point_2 the (x, y) coordinates of the 2nd point
* #return the gradient of the line between the two points
*/
float find_gradient(float *point_1, float *point_2)
{
float x_diff = point_2[0] - point_1[0];
float y_diff = point_2[1] - point_1[1];
float gradient = y_diff/x_diff;
return gradient;
}
/**
* #brief traingulate function: finds the point of intersection of the line
* passing through points A & B and the line passing though points C & D.
*
* #param a the coordinates of point A
* #param b the coordinates of point B
* #param c the coordinates of point C
* #param d the coordinates of point D
* #return the point at which the lines intersect
*/
float *triangulate(float *a, float *b, float *c, float *d)
{
float *intersect = (float *)malloc(2*sizeof(float));
float g1 = find_gradient(a, b);
float g2 = find_gradient(c, d);
if (a[0] == b[0]) {
intersect[0] = a[0];
intersect[1] = c[1] + (a[0]-c[0])*g2;
} else if (c[0] == d[0]) {
intersect[0] = c[0];
intersect[1] = a[1] - (a[0]-c[0])*g1;
} else {
intersect[0] = ((a[1]-c[1]) + c[0]*g2 - a[0]*g1)/(g2-g1);
intersect[1] = (a[1]*g2 - c[1]*g1 + (c[0]-a[0])*g2*g1)/(g2-g1);
}
return intersect;
}
/**
* #brief bearing function: finds the compass bearing of point_1 from point_2
*
* #param point_1 the coordinates of the point to find the compass bearing of
* relative to point_2
* #param point_2 the coordinates of the reference point from which the compass
* bearing of point_1 will be measured
* #return the compass bearing of point_1 from point_2
*/
char *bearing(float *point_1, float *point_2)
{
const float RAD_DEG_RATIO = 180/M_PI;
float x_diff = point_1[0] - point_2[0];
float y_diff = point_1[1] - point_2[1];
float gradient = y_diff/x_diff;
char *bearing = (char *)malloc(7*sizeof(char));
if (x_diff == 0 && y_diff > 0) {
sprintf(bearing, "N");
} else if (x_diff == 0 && y_diff < 0) {
sprintf(bearing, "S");
} else if (x_diff > 0 && y_diff == 0) {
sprintf(bearing, "E");
} else if (x_diff < 0 && y_diff == 0) {
sprintf(bearing, "W");
} else if (x_diff > 0 && y_diff > 0) {
float angle = 90 - RAD_DEG_RATIO*atan(gradient);
sprintf(bearing, "N %.2f E", angle);
} else if (x_diff < 0 && y_diff > 0) {
float angle = 90 + RAD_DEG_RATIO*atan(gradient);
sprintf(bearing, "N %.2f W", angle);
} else if (x_diff < 0 && y_diff < 0) {
float angle = 90 - RAD_DEG_RATIO*atan(gradient);
sprintf(bearing, "S %.2f W", angle);
} else if (x_diff > 0 && y_diff < 0) {
float angle = 90 + RAD_DEG_RATIO*atan(gradient);
sprintf(bearing, "S %.2f E", angle);
}
return bearing;
}
3221225477 decimal is gibberish. Translated to hex you get 0xC0000005, which is Windows' error code for access violation (also known as "seg fault"). The most likely cause is array out of bounds access.
The bug is here: malloc(7*sizeof(char)); creates too little memory to contain what your sprintf might produce. This can only hold 6 characters + the mandatory null terminator. Memory is cheap, so change this for something with margins like:
char *bearing = malloc(20);
Why you keep using malloc all over the place to allocate tiny amounts of memory, I have no idea. All you achieve with that is to slow down everything and create potential for memory leaks.
It's better practice to have your function work with caller allocated memory whenever possible. It is not good design to mix up memory allocation and the actual algorithm in the same function.
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 4 years ago.
Improve this question
I need to estimate for my internship the firing rate of neurons that follows an ODE. I code at first in python and it goes pretty well but for better performance, my supervisor told to write the same code in C. However, I never coded in C so I am a very beginner and the file in which i want to have the values for the firing rate is full of zero... Can someone help me ?
Thank you very much
So here is my python code :
import numpy as np
import matplotlib.pyplot as plt
from math import cos, sin, sqrt, pi, exp as cos, sin, sqrt, pi, exp
#parameters
eps = 0.05
f = 0.215
mu = 1.1
D = 0.001
DeltaT = 0.01
timewindow = 40
num_points = int(timewindow/DeltaT)
T = np.linspace(0, timewindow, num_points)
#signal
s = [sin(2*3.14*f*t) for t in T]
N=30000
compteur=np.zeros(num_points)
v = np.zeros((num_points,N))
samples = np.random.normal(0, 1, (num_points,N))
for i in range(1,num_points):
for j in range(N):
v[i,j] = v[i-1,j] + DeltaT *(-v[i-1,j]+ mu + eps*s[i-1]) + \
sqrt(2*D*DeltaT)*samples[i,j]
if v[i,j]>1:
v[i,j]=0
compteur[i]+=1/DeltaT/N
plt.plot(T,compteur)
plt.show()
and here is my "translation" in C :
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define PI 3.1415926536
float s(float x, float);
double AWGN_generator();
FILE* fopen(const char* nomDuFichier, const char* modeOuverture);
int fclose(FILE* pointeurSurFichier);
int main(int argc, char *argv[])
{
//parameters
double eps = 0.05;
float f = 0.215 ;
double mu = 1.1 ;
double D = 0.001 ;
int time_window = 90;
int num_points = 1000;
long num_neurons = 1000;
double deltaT = time_window/num_points;
int i ;
//time
double Time[num_points] ;
Time[0]= 0.0;
for (i = 1 ; i < num_points ; i++ )
{ Time[i] = Time[i-1] + deltaT;
}
//opening file for saving data
FILE* fichier = NULL;
fichier = fopen("challala.txt", "w");
if (fichier != NULL)
{
double v[num_points][num_neurons] ;
memset(v, 0, num_points*num_neurons*sizeof(long) );
long compteur[num_points];
memset( compteur, 0, num_points*sizeof(long) );
int pos_1 ;
int pos_2 ;
//Euler's method
for (pos_1 = 1 ; pos_1 < num_points ; pos_1 ++)
{
for (pos_2 = 0 ; pos_2<num_neurons ; pos_2 ++)
{
float t = Time[pos_1-1] ;
v[pos_1][pos_2] = v[pos_1-1][pos_2] + deltaT *(-v[pos_1-1]
[pos_2]+ mu + eps*s(t, f))+ sqrt(2*D*deltaT)*AWGN_generator();
if (v[pos_1][pos_2]>1)
{
v[pos_1][pos_2]=0 ;
compteur[pos_1]+=1/deltaT/num_neurons ;
}
}
fprintf(fichier, "%ld",compteur[pos_1]);
}
fclose(fichier);
printf("ca a marche test.txt");
}
else
{
// On affiche un message d'erreur si on veut
printf("Impossible d'ouvrir le fichier test.txt");
}
return 0;
}
float s(float x, float f)
{
return sin(2*M_PI*f*x);
}
double AWGN_generator()
{/* Generates additive white Gaussian Noise samples with zero mean and a
standard deviation of 1. */
double temp1;
double temp2;
double result;
int p;
p = 1;
while( p > 0 )
{
temp2 = ( rand() / ( (double)RAND_MAX ) ); /* rand() function generates an
integer between 0 and
RAND_MAX,
which is defined in
stdlib.h.
*/
if ( temp2 == 0 )
{// temp2 is >= (RAND_MAX / 2)
p = 1;
}// end if
else
{// temp2 is < (RAND_MAX / 2)
p = -1;
}// end else
}// end while()
temp1 = cos( ( 2.0 * (double)PI ) * rand() / ( (double)RAND_MAX ) );
result = sqrt( -2.0 * log( temp2 ) ) * temp1;
return result; // return the generated random sample to the caller
}
Code repaired as able with old code commented out. See comments for change details.
Need to see challala.txt for a definitive test.
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// Why use a coarse approximation?
//#define PI 3.1415926536
#define PI 3.1415926535897932384626433832795
// Let us stick to double only.
//float s(float x, float);
double s(double x, double);
// Add void, else declaration does not check the parameters.
//double AWGN_generator();
double AWGN_generator(void);
// These should have already been declared in <stdio.h>
// FILE* fopen(const char* nomDuFichier, const char* modeOuverture);
// int fclose(FILE* pointeurSurFichier);
int main(int argc, char *argv[]) {
//parameters
double eps = 0.05;
// float f = 0.215;
double f = 0.215;
double mu = 1.1;
double D = 0.001;
int time_window = 90;
// Unclear why `int/long` used here. size_t would be idiomatic for array sizing.
int num_points = 1000;
long num_neurons = 1000;
// Avoid integer division when a FP quotinet is desired
// double deltaT = time_window / num_points;
double deltaT = 1.0*time_window / num_points;
int i;
//time
double Time[num_points];
Time[0] = 0.0;
for (i = 1; i < num_points; i++)
{
Time[i] = Time[i - 1] + deltaT;
}
//opening file for saving data
FILE* fichier = NULL;
fichier = fopen("challala.txt", "w");
if (fichier != NULL) {
double v[num_points][num_neurons];
// zero fill use wrong type in sizeof.
// Avoid type in sizeof, better to use sizeof object
// memset(v, 0, num_points * num_neurons * sizeof(long));
memset(v, 0, sizeof v);
// Let us use FP here.
//long compteur[num_points];
double compteur[num_points];
memset(compteur, 0, sizeof compteur);
int pos_1;
int pos_2;
//Euler's method
for (pos_1 = 1; pos_1 < num_points; pos_1++)
{
for (pos_2 = 0; pos_2 < num_neurons; pos_2++) {
// float t = Time[pos_1 - 1];
double t = Time[pos_1 - 1];
v[pos_1][pos_2] = v[pos_1 - 1][pos_2]
+ deltaT * (-v[pos_1 - 1][pos_2] + mu + eps * s(t, f))
+ sqrt(2 * D * deltaT) * AWGN_generator();
if (v[pos_1][pos_2] > 1) {
v[pos_1][pos_2] = 0;
compteur[pos_1] += 1 / deltaT / num_neurons;
}
}
// Change of type
// fprintf(fichier, "%ld", compteur[pos_1]);
fprintf(fichier, " %g", compteur[pos_1]);
}
fclose(fichier);
printf("ca a marche test.txt");
} else {
// Was not the file another name?
// printf("Impossible d'ouvrir le fichier test.txt");
printf("Impossible d'ouvrir le fichier \"%s\"\n", challala.txt);
}
return 0;
}
//float s(float x, float f) {
double s(double x, double f) {
// M_PI is not defined in the standard C library, although common in extensions.
//return sin(2 * M_PI * f * x);
return sin(2 * PI * f * x);
}
double AWGN_generator() {
double temp1;
double temp2;
double result;
int p;
p = 1;
while (p > 0) {
temp2 = (rand() / ((double) RAND_MAX));
if (temp2 == 0) { // temp2 is >= (RAND_MAX / 2)
p = 1;
} // end if
else { // temp2 is < (RAND_MAX / 2)
p = -1;
} // end else
} // end while()
temp1 = cos((2.0 * (double) PI) * rand() / ((double) RAND_MAX));
result = sqrt(-2.0 * log(temp2)) * temp1;
return result; // return the generated random sample to the caller
}
Minor and advanced numeric issue:
Realize that quotient 1.0*time_window / num_points maybe be a little different than mathematical expected due to finite precision of double. This is, at worst, expected to be a very small amount, maybe about 0.5 parts in 253.
Yet the repetitive additions accumulate the error.
double deltaT = 1.0*time_window / num_points;
int i;
double Time[num_points];
Time[0] = 0.0;
for (i = 1; i < num_points; i++) {
Time[i] = Time[i - 1] + deltaT;
}
To avoid that accumulated error, code can re-calculate Time[i] anew on each iteration.
double deltaT = 1.0*time_window / num_points;
double Time[num_points];
for (int i = 0; i < num_points; i++) {
Time[i] = deltaT*i;
}
Of course, such small errors are often ignorable, but mayne not when num_points is large enough. This happens when your good code is applied to ever larger tasks.
regarding the following 3 statements
int time_window = 90;
int num_points = 1000;
double deltaT = time_window/num_points;
Since time_window and num_points are integers, the division is performed as an integer divide.
In an integer divide, all fractions are truncated.
the expression: time_window/num_points is actually:
90 / 1000
the resulting fraction has everything right of the decimal point truncated, so the result is 0
so: Time[0] + 0 results in 0.0.
The same (calculated) value: 0.0 is then propagated thorough the whole array
suggest changing:
int time_window = 90;
int num_points = 1000;
to
double time_window = 90.0;
double num_points = 1000.0;
regarding:
memset(v, 0, num_points*num_neurons*sizeof(long) );
this statement may (or may not) perform the desired functionality. It depends on if the size of double is the same as the size of long
Suggest using:
memset( v, 0, sizeof( v ) );
This question already has answers here:
Exchange 1000s digit with 10s digit (C)
(3 answers)
Closed 5 years ago.
What I want to do in C is swap two digits in a double.
For example, if the input is 54321.987 and I want to swap the 2nd with the 4th digit, the output should be 52341.987.
Example when too small: 12.34 would output 1002.34.
Using stringification approach:
There are more elegant ways, but you can see the steps (and improve on) this pseudo code to stringify, move values, and convert back to number.
char buf1[20];
char buf2[20];
char *dummy;
double val = 54321.987;
sprintf(buf1, "%9.3f", val );
//Now the number is in string form: "54321.987". Just move the two elements
buf2[0]=buf1[0];
buf2[1]=buf1[3];
buf2[2]=buf1[2];
buf2[3]=buf1[1]; //and so on
//now convert back:
val = strtod(buf2, &dummy);
printf("%9.3f\n", val);
Or, a function could be used to do essentially the same thing: (still stringification)
double swap_num_char(double num, int precision, int c1, int c2); //zero index for c1 and c2
int main(void)
{
double val = 54321.987;
printf("%9.3f\n", swap_num_char(val, 3, 1, 3));
return 0;
}
double swap_num_char(double num, int precision, int c1, int c2)
{
char buf[25];
char format[10];
char *dummy;
char c;
sprintf(format, "%s0.%df", "%", precision);
sprintf(buf, format, num);
c = buf[c1];
buf[c1] = buf[c2];
buf[c2] = c;
num = strtod(buf, &dummy);
return num;
}
You can get the two digits you're interested in with simple operations:
You can do so with
double x = 54321.987;
double tens = ((int)(x / 10)) % 10; // Result is 2
double thousands = ((int)(x / 1000)) % 10; // Result is 4
Then you can subtract out the digits from their original place,
and add them back in a new place:
x = x - (tens * 10.0) - (thousands * 1000.0); // result is 50301.987
x = x + (tens * 1000.0) + (thousands * 10.0); // result is 52341.987
Now just reduce the expression:
x = x + tens * (1000.0 - 10.0) - thousands * (1000.0 - 10.0);
This leaves you with a final expression:
x += (tens - thousands) * 990.0;
Or, if you don't want the intermediate variables:
x += (((int)(x/10))%10 - ((int)(x/1000))%10) * 990;
One solution would be to extract the digits, then swap them.
You extract the digits (from positive numbers, at least) by using floor():
int place1 = 1; /* 0-based*/
double desiredPowerOf10 = powersOf10[place1];
double nextPowerOf10 = powersOf10[place1 + 1];
double digit1 = floor(number / desiredPowerOf10) - floor(number/nextPowerOf10) * 10;
You can then subtract the digits and add them back with the different powers:
double digitsRemoved = number - (digit1 * power1 + digit2 * power2);
double digitsSwapped = digitsRemoved + digit1 * power2 + digit2 * power1;
This may be susceptible to loss of precision with very large numbers, though.
1 - Use modf() to break the number into whole and fractional parts.
double modf(double value, double *iptr);
The modf functions break the argument value into integral and fractional parts, C11 §7.12.6.12
2 - Print the whole number part as a string and do the swap.
3 - Reconstruct
#include <float.h>
#include <math.h>
#include <stdio.h>
double swap2_4digit(double x) {
if (signbit(x)) {
return -swap2_4digit(-x);
}
printf("Before %f\n", x);
double ipart;
double fpart = modf(x, &ipart);
// ms_digit digits '.' '\0' min_size
char buf[1 + DBL_MAX_10_EXP + 1 + 1 + 4]; // Insure buffer is big enough
strcpy(buf, "0000"); // Handle small numbers
sprintf(buf + strlen(buf), "%.0f", ipart);
size_t len = strlen(buf);
char ch = buf[len - 2];
buf[len - 2] = buf[len - 4];
buf[len - 4] = ch;
x = atof(buf) + fpart;
printf("After %f\n", x);
return x;
}
int main(void) {
swap2_4digit(54321.987);
swap2_4digit(12.34);
}
Output
Before 54321.987000
After 52341.987000
Before 12.340000
After 1002.340000
Something left for OP. Make general for other digit positions.
If you want input number to be double then you can do something like this:
#include <stdio.h>
#include <stdlib.h>
int main()
{
double numbergiven = 56789.1234;
double dummy;
double _4th_digit = (10*modf(numbergiven/10000, &dummy)) - modf(numbergiven/1000, &dummy);
double _2th_digit = (10*modf(numbergiven/100, &dummy)) - modf(numbergiven/10, &dummy);
numbergiven = numbergiven - (_4th_digit * 1000) + (_2th_digit * 1000);
numbergiven = numbergiven + (_4th_digit * 10) - (_2th_digit * 10);
printf("%lf",numbergiven);
return 0;
}
If you are not familiar with modf then you can simply do it this way:
#include <stdio.h>
#include <stdlib.h>
int main()
{
double numbergiven = 56789.1234;
int number = numbergiven;
int _4th_digit = (number/1000) - (10*(number/10000));
int _2th_digit = (number/10) - (10*(number/100));
numbergiven = numbergiven - (_4th_digit * 1000) + (_2th_digit * 1000);
numbergiven = numbergiven + (_4th_digit * 10) - (_2th_digit * 10);
printf("%lf",numbergiven);
return 0;
}
or use fmod() #John Bollinger.
The fmod functions compute the floating-point remainder of x/y.
Extract the 2 digits each with the difference of modding with 10place and modding with 10place-1.
Subtract the 2 digits and then add them back swapped.
double swap_digit(double x, unsigned a, unsigned b) {
printf("Before %f\n", x);
double a_place = pow(10.0, a);
double b_place = pow(10.0, b);
double scaled_digit_a = fmod(x, a_place) - fmod(x, a_place/10);
double scaled_digit_b = fmod(x, b_place) - fmod(x, b_place/10);
x -= scaled_digit_a + scaled_digit_b;
x += scaled_digit_a/a_place*b_place + scaled_digit_b/b_place*a_place;
printf("After %f\n", x);
return x;
}
int main(void) {
swap_digit(54321.987,2,4);
swap_digit(12.34,2,4);
}
Output
Before 54321.987000
After 52341.987000
Before 12.340000
After 1002.340000
Double is stored in a memory as a sequence of bits, but you want to operate with decimal digits. Doing this with double variable you may not receive the original digits because of floating-point arithmetic.
Therefore, you should manipulate with string representation of double. The main aspect is how many digits string will contain. But it's obvious that you get number from input. Scan it as string, not as double.
There is a working code:
#include <stdio.h>
#include <stddef.h>
#define BUFSIZE 255
void swap_digits(char *str, int n, int m) {
char *digit1 = NULL;
char *digit2 = NULL;
int count = 0;
while (*str && (!digit1 || !digit2)) {
if (*str != '.') {
count++;
if (count == n) {
digit1 = str;
}
if (count == m) {
digit2 = str;
}
}
str++;
}
if (digit1 && digit2) {
char tmp = *digit1;
*digit1 = *digit2;
*digit2 = tmp;
}
}
int main(void) {
char buffer[BUFSIZE];
scanf("%s", buffer);
// it is preferably to validate input
swap_digits(buffer, 2, 4);
printf(buffer);
return 0;
}
Having trouble with program that calculates the perimeter of a polygon from an input of (x,y) coordinates. I need to use arrays and I'm not very confident with them. Mostly having trouble with reading the values into the array from a .txt file (using <) and also how to use the last point and first point to close off the polygon.
The input is:
3 1.0 2.0 1.0 5.0 4.0 5.0
5 1.0 2.0 4.0 5.0 7.8 3.5 5.0 0.4 1.0 0.4
4 1.0 0.4 0.4 0.4 0.4 3.6 1.0 3.6
0
Where the first number in each row indicates the number of points (npoints) and then followed by the coordinates themselves which are in clear groups of two i.e. (x,y).
Each new row indicates a new polygon needing to be read.
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#define MAX_PTS 100
#define MAX_POLYS 100
#define END_INPUT 0
double
getDistance(int npoints, double x[], double y[]) {
double distance = 0.0;
for (int i = 0; i < npoints; ++i) {
int j =; // I'm stuck here
distance = sqrt((x[i]-x[j]) * (x[i]-x[j]) + (y[i]-y[j]) *(y[i]-y[j]));
}
return distance;
}
int
main(int argc, char *argv[]) {
int npoints, iteration = 0;
double x[MAX_PTS], y[MAX_PTS];
double perimeter = 0.0;
if (npoints == END_INPUT){
scanf("%d", &npoints);
// start with 0 value of parameter.
for (iteration = 0; iteration < npoints; ++iteration) {
scanf("%lf %lf", &(x[iteration]), &(y[iteration]));
// take input for new-point.
// for next iteration, new-point would be first-point in getDistance
}
perimeter += getDistance(npoints, x, y);
perimeter += getDistance(); // stuck here
// need to add the perimeter
// need to complete the polygon with last-edge joining the last-point
// with initial-point but not sure how to access array
printf("perimeter = %2.2f m\n", perimeter);
}
return 0;
}
The output for the first polygon should be 10.24m
If anyone could give me a hand that would be great, I'm quite stumped
You were almost there. After adding the file input, just a few tweaks here and there. Most importantly, how to make the array point wrap back to the front.
#include <stdio.h>
#include <math.h>
#define MAX_PTS 100
double getDistance(int npoints, double x[], double y[]) {
double distance = 0.0, dx, dy;
int i;
for (i = 0; i < npoints; ++i) {
dx = x[(i+1) % npoints] - x[i]; // wrap the index
dy = y[(i+1) % npoints] - y[i];
distance += sqrt(dx * dx + dy * dy); // accumaulate
}
return distance;
}
int main(int argc, char *argv[]) {
int npoints, point;
double x[MAX_PTS], y[MAX_PTS];
double perimeter;
FILE *fp;
if (argc < 2) {
printf("No file name supplied\n");
return 1;
}
if ((fp = fopen(argv[1], "rt")) == NULL) {
printf("Error opening file\n");
return 1;
}
while (1) {
if (1 != fscanf(fp, "%d", &npoints)) { // check result
printf("Error reading number of sides\n");
return 1;
}
if (npoints == 0)
break; // end of data
if (npoints < 3 || npoints > MAX_PTS) { // check range
printf("Illegal number of sides %d\n", npoints);
return 1;
}
for (point = 0; point < npoints; ++point) {
if (2 != fscanf(fp, "%lf %lf", &x[point], &y[point])) { // check result
printf("Error reading coordinates of %d-sided polygon\n", npoints);
return 1;
}
}
perimeter = getDistance(npoints, x, y); // include args
printf("perimeter = %2.2f\n", perimeter);
}
fclose(fp);
return 0;
}
Program output:
>test polydata.txt
perimeter = 10.24
perimeter = 18.11
perimeter = 7.60
Get rid of the test of npoints == END_INPUT in main(). It serves no purpose and, since npoints is uninitialised, gives undefined behaviour.
As to the question you asked, assuming the points represent a polygon with no edges crossing over, the distance will be the sum of distances between adjacent points (0->1, 1->2, ....., npoint-2 -> npoints-1, npoints-1 -> 0).
The only special one of these is the last. Other than the last, the distance is being computed between point i to point i + 1.
That suggests j is i+1 for i = 0 to npoints-2. Then, for i == npoints-1, j will be equal to 0.
Writing code to implement that is trivial.
I am trying to implement a linear least squares fit onto 2 arrays of data: time vs amplitude. The only technique I know so far is to test all of the possible m and b points in (y = m*x+b) and then find out which combination fits my data best so that it has the least error. However, I think iterating so many combinations is sometimes useless because it tests out everything. Are there any techniques to speed up the process that I don't know about? Thanks.
Try this code. It fits y = mx + b to your (x,y) data.
The arguments to linreg are
linreg(int n, REAL x[], REAL y[], REAL* b, REAL* m, REAL* r)
n = number of data points
x,y = arrays of data
*b = output intercept
*m = output slope
*r = output correlation coefficient (can be NULL if you don't want it)
The return value is 0 on success, !=0 on failure.
Here's the code
#include "linreg.h"
#include <stdlib.h>
#include <math.h> /* math functions */
//#define REAL float
#define REAL double
inline static REAL sqr(REAL x) {
return x*x;
}
int linreg(int n, const REAL x[], const REAL y[], REAL* m, REAL* b, REAL* r){
REAL sumx = 0.0; /* sum of x */
REAL sumx2 = 0.0; /* sum of x**2 */
REAL sumxy = 0.0; /* sum of x * y */
REAL sumy = 0.0; /* sum of y */
REAL sumy2 = 0.0; /* sum of y**2 */
for (int i=0;i<n;i++){
sumx += x[i];
sumx2 += sqr(x[i]);
sumxy += x[i] * y[i];
sumy += y[i];
sumy2 += sqr(y[i]);
}
REAL denom = (n * sumx2 - sqr(sumx));
if (denom == 0) {
// singular matrix. can't solve the problem.
*m = 0;
*b = 0;
if (r) *r = 0;
return 1;
}
*m = (n * sumxy - sumx * sumy) / denom;
*b = (sumy * sumx2 - sumx * sumxy) / denom;
if (r!=NULL) {
*r = (sumxy - sumx * sumy / n) / /* compute correlation coeff */
sqrt((sumx2 - sqr(sumx)/n) *
(sumy2 - sqr(sumy)/n));
}
return 0;
}
Example
You can run this example online.
int main()
{
int n = 6;
REAL x[6]= {1, 2, 4, 5, 10, 20};
REAL y[6]= {4, 6, 12, 15, 34, 68};
REAL m,b,r;
linreg(n,x,y,&m,&b,&r);
printf("m=%g b=%g r=%g\n",m,b,r);
return 0;
}
Here is the output
m=3.43651 b=-0.888889 r=0.999192
Here is the Excel plot and linear fit (for verification).
All values agree exactly with the C code above (note C code returns r while Excel returns R**2).
There are efficient algorithms for least-squares fitting; see Wikipedia for details. There are also libraries that implement the algorithms for you, likely more efficiently than a naive implementation would do; the GNU Scientific Library is one example, but there are others under more lenient licenses as well.
From Numerical Recipes: The Art of Scientific Computing in (15.2) Fitting Data to a Straight Line:
Linear Regression:
Consider the problem of fitting a set of N data points (xi, yi) to a straight-line model:
Assume that the uncertainty: sigmai associated with each yi and that the xi’s (values of the dependent variable) are known exactly. To measure how well the model agrees with the data, we use the chi-square function, which in this case is:
The above equation is minimized to determine a and b. This is done by finding the derivative of the above equation with respect to a and b, equate them to zero and solve for a and b. Then we estimate the probable uncertainties in the estimates of a and b, since obviously the measurement errors in the data must introduce some uncertainty in the determination of those parameters. Additionally, we must estimate the goodness-of-fit of the data to the
model. Absent this estimate, we have not the slightest indication that the parameters a and b in the model have any meaning at all.
The below struct performs the mentioned calculations:
struct Fitab {
// Object for fitting a straight line y = a + b*x to a set of
// points (xi, yi), with or without available
// errors sigma i . Call one of the two constructors to calculate the fit.
// The answers are then available as the variables:
// a, b, siga, sigb, chi2, and either q or sigdat.
int ndata;
double a, b, siga, sigb, chi2, q, sigdat; // Answers.
vector<double> &x, &y, &sig;
// Constructor.
Fitab(vector<double> &xx, vector<double> &yy, vector<double> &ssig)
: ndata(xx.size()), x(xx), y(yy), sig(ssig), chi2(0.), q(1.), sigdat(0.)
{
// Given a set of data points x[0..ndata-1], y[0..ndata-1]
// with individual standard deviations sig[0..ndata-1],
// sets a,b and their respective probable uncertainties
// siga and sigb, the chi-square: chi2, and the goodness-of-fit
// probability: q
Gamma gam;
int i;
double ss=0., sx=0., sy=0., st2=0., t, wt, sxoss; b=0.0;
for (i=0;i < ndata; i++) { // Accumulate sums ...
wt = 1.0 / SQR(sig[i]); //...with weights
ss += wt;
sx += x[i]*wt;
sy += y[i]*wt;
}
sxoss = sx/ss;
for (i=0; i < ndata; i++) {
t = (x[i]-sxoss) / sig[i];
st2 += t*t;
b += t*y[i]/sig[i];
}
b /= st2; // Solve for a, b, sigma-a, and simga-b.
a = (sy-sx*b) / ss;
siga = sqrt((1.0+sx*sx/(ss*st2))/ss);
sigb = sqrt(1.0/st2); // Calculate chi2.
for (i=0;i<ndata;i++) chi2 += SQR((y[i]-a-b*x[i])/sig[i]);
if (ndata>2) q=gam.gammq(0.5*(ndata-2),0.5*chi2); // goodness of fit
}
// Constructor.
Fitab(vector<double> &xx, vector<double> &yy)
: ndata(xx.size()), x(xx), y(yy), sig(xx), chi2(0.), q(1.), sigdat(0.)
{
// As above, but without known errors (sig is not used).
// The uncertainties siga and sigb are estimated by assuming
// equal errors for all points, and that a straight line is
// a good fit. q is returned as 1.0, the normalization of chi2
// is to unit standard deviation on all points, and sigdat
// is set to the estimated error of each point.
int i;
double ss,sx=0.,sy=0.,st2=0.,t,sxoss;
b=0.0; // Accumulate sums ...
for (i=0; i < ndata; i++) {
sx += x[i]; // ...without weights.
sy += y[i];
}
ss = ndata;
sxoss = sx/ss;
for (i=0;i < ndata; i++) {
t = x[i]-sxoss;
st2 += t*t;
b += t*y[i];
}
b /= st2; // Solve for a, b, sigma-a, and sigma-b.
a = (sy-sx*b)/ss;
siga=sqrt((1.0+sx*sx/(ss*st2))/ss);
sigb=sqrt(1.0/st2); // Calculate chi2.
for (i=0;i<ndata;i++) chi2 += SQR(y[i]-a-b*x[i]);
if (ndata > 2) sigdat=sqrt(chi2/(ndata-2));
// For unweighted data evaluate typical
// sig using chi2, and adjust
// the standard deviations.
siga *= sigdat;
sigb *= sigdat;
}
};
where struct Gamma:
struct Gamma : Gauleg18 {
// Object for incomplete gamma function.
// Gauleg18 provides coefficients for Gauss-Legendre quadrature.
static const Int ASWITCH=100; When to switch to quadrature method.
static const double EPS; // See end of struct for initializations.
static const double FPMIN;
double gln;
double gammp(const double a, const double x) {
// Returns the incomplete gamma function P(a,x)
if (x < 0.0 || a <= 0.0) throw("bad args in gammp");
if (x == 0.0) return 0.0;
else if ((Int)a >= ASWITCH) return gammpapprox(a,x,1); // Quadrature.
else if (x < a+1.0) return gser(a,x); // Use the series representation.
else return 1.0-gcf(a,x); // Use the continued fraction representation.
}
double gammq(const double a, const double x) {
// Returns the incomplete gamma function Q(a,x) = 1 - P(a,x)
if (x < 0.0 || a <= 0.0) throw("bad args in gammq");
if (x == 0.0) return 1.0;
else if ((Int)a >= ASWITCH) return gammpapprox(a,x,0); // Quadrature.
else if (x < a+1.0) return 1.0-gser(a,x); // Use the series representation.
else return gcf(a,x); // Use the continued fraction representation.
}
double gser(const Doub a, const Doub x) {
// Returns the incomplete gamma function P(a,x) evaluated by its series representation.
// Also sets ln (gamma) as gln. User should not call directly.
double sum,del,ap;
gln=gammln(a);
ap=a;
del=sum=1.0/a;
for (;;) {
++ap;
del *= x/ap;
sum += del;
if (fabs(del) < fabs(sum)*EPS) {
return sum*exp(-x+a*log(x)-gln);
}
}
}
double gcf(const Doub a, const Doub x) {
// Returns the incomplete gamma function Q(a, x) evaluated
// by its continued fraction representation.
// Also sets ln (gamma) as gln. User should not call directly.
int i;
double an,b,c,d,del,h;
gln=gammln(a);
b=x+1.0-a; // Set up for evaluating continued fraction
// by modified Lentz’s method with with b0 = 0.
c=1.0/FPMIN;
d=1.0/b;
h=d;
for (i=1;;i++) {
// Iterate to convergence.
an = -i*(i-a);
b += 2.0;
d=an*d+b;
if (fabs(d) < FPMIN) d=FPMIN;
c=b+an/c;
if (fabs(c) < FPMIN) c=FPMIN;
d=1.0/d;
del=d*c;
h *= del;
if (fabs(del-1.0) <= EPS) break;
}
return exp(-x+a*log(x)-gln)*h; Put factors in front.
}
double gammpapprox(double a, double x, int psig) {
// Incomplete gamma by quadrature. Returns P(a,x) or Q(a, x),
// when psig is 1 or 0, respectively. User should not call directly.
int j;
double xu,t,sum,ans;
double a1 = a-1.0, lna1 = log(a1), sqrta1 = sqrt(a1);
gln = gammln(a);
// Set how far to integrate into the tail:
if (x > a1) xu = MAX(a1 + 11.5*sqrta1, x + 6.0*sqrta1);
else xu = MAX(0.,MIN(a1 - 7.5*sqrta1, x - 5.0*sqrta1));
sum = 0;
for (j=0;j<ngau;j++) { // Gauss-Legendre.
t = x + (xu-x)*y[j];
sum += w[j]*exp(-(t-a1)+a1*(log(t)-lna1));
}
ans = sum*(xu-x)*exp(a1*(lna1-1.)-gln);
return (psig?(ans>0.0? 1.0-ans:-ans):(ans>=0.0? ans:1.0+ans));
}
double invgammp(Doub p, Doub a);
// Inverse function on x of P(a,x) .
};
const Doub Gamma::EPS = numeric_limits<Doub>::epsilon();
const Doub Gamma::FPMIN = numeric_limits<Doub>::min()/EPS
and stuct Gauleg18:
struct Gauleg18 {
// Abscissas and weights for Gauss-Legendre quadrature.
static const Int ngau = 18;
static const Doub y[18];
static const Doub w[18];
};
const Doub Gauleg18::y[18] = {0.0021695375159141994,
0.011413521097787704,0.027972308950302116,0.051727015600492421,
0.082502225484340941, 0.12007019910960293,0.16415283300752470,
0.21442376986779355, 0.27051082840644336, 0.33199876341447887,
0.39843234186401943, 0.46931971407375483, 0.54413605556657973,
0.62232745288031077, 0.70331500465597174, 0.78649910768313447,
0.87126389619061517, 0.95698180152629142};
const Doub Gauleg18::w[18] = {0.0055657196642445571,
0.012915947284065419,0.020181515297735382,0.027298621498568734,
0.034213810770299537,0.040875750923643261,0.047235083490265582,
0.053244713977759692,0.058860144245324798,0.064039797355015485
0.068745323835736408,0.072941885005653087,0.076598410645870640,
0.079687828912071670,0.082187266704339706,0.084078218979661945,
0.085346685739338721,0.085983275670394821};
and, finally fuinction Gamma::invgamp():
double Gamma::invgammp(double p, double a) {
// Returns x such that P(a,x) = p for an argument p between 0 and 1.
int j;
double x,err,t,u,pp,lna1,afac,a1=a-1;
const double EPS=1.e-8; // Accuracy is the square of EPS.
gln=gammln(a);
if (a <= 0.) throw("a must be pos in invgammap");
if (p >= 1.) return MAX(100.,a + 100.*sqrt(a));
if (p <= 0.) return 0.0;
if (a > 1.) {
lna1=log(a1);
afac = exp(a1*(lna1-1.)-gln);
pp = (p < 0.5)? p : 1. - p;
t = sqrt(-2.*log(pp));
x = (2.30753+t*0.27061)/(1.+t*(0.99229+t*0.04481)) - t;
if (p < 0.5) x = -x;
x = MAX(1.e-3,a*pow(1.-1./(9.*a)-x/(3.*sqrt(a)),3));
} else {
t = 1.0 - a*(0.253+a*0.12); and (6.2.9).
if (p < t) x = pow(p/t,1./a);
else x = 1.-log(1.-(p-t)/(1.-t));
}
for (j=0;j<12;j++) {
if (x <= 0.0) return 0.0; // x too small to compute accurately.
err = gammp(a,x) - p;
if (a > 1.) t = afac*exp(-(x-a1)+a1*(log(x)-lna1));
else t = exp(-x+a1*log(x)-gln);
u = err/t;
// Halley’s method.
x -= (t = u/(1.-0.5*MIN(1.,u*((a-1.)/x - 1))));
// Halve old value if x tries to go negative.
if (x <= 0.) x = 0.5*(x + t);
if (fabs(t) < EPS*x ) break;
}
return x;
}
Here is my version of a C/C++ function that does simple linear regression. The calculations follow the wikipedia article on simple linear regression. This is published as a single-header public-domain (MIT) library on github: simple_linear_regression. The library (.h file) is tested to work on Linux and Windows, and from C and C++ using -Wall -Werror and all -std versions supported by clang/gcc.
#define SIMPLE_LINEAR_REGRESSION_ERROR_INPUT_VALUE -2
#define SIMPLE_LINEAR_REGRESSION_ERROR_NUMERIC -3
int simple_linear_regression(const double * x, const double * y, const int n, double * slope_out, double * intercept_out, double * r2_out) {
double sum_x = 0.0;
double sum_xx = 0.0;
double sum_xy = 0.0;
double sum_y = 0.0;
double sum_yy = 0.0;
double n_real = (double)(n);
int i = 0;
double slope = 0.0;
double denominator = 0.0;
if (x == NULL || y == NULL || n < 2) {
return SIMPLE_LINEAR_REGRESSION_ERROR_INPUT_VALUE;
}
for (i = 0; i < n; ++i) {
sum_x += x[i];
sum_xx += x[i] * x[i];
sum_xy += x[i] * y[i];
sum_y += y[i];
sum_yy += y[i] * y[i];
}
denominator = n_real * sum_xx - sum_x * sum_x;
if (denominator == 0.0) {
return SIMPLE_LINEAR_REGRESSION_ERROR_NUMERIC;
}
slope = (n_real * sum_xy - sum_x * sum_y) / denominator;
if (slope_out != NULL) {
*slope_out = slope;
}
if (intercept_out != NULL) {
*intercept_out = (sum_y - slope * sum_x) / n_real;
}
if (r2_out != NULL) {
denominator = ((n_real * sum_xx) - (sum_x * sum_x)) * ((n_real * sum_yy) - (sum_y * sum_y));
if (denominator == 0.0) {
return SIMPLE_LINEAR_REGRESSION_ERROR_NUMERIC;
}
*r2_out = ((n_real * sum_xy) - (sum_x * sum_y)) * ((n_real * sum_xy) - (sum_x * sum_y)) / denominator;
}
return 0;
}
Usage example:
#define SIMPLE_LINEAR_REGRESSION_IMPLEMENTATION
#include "simple_linear_regression.h"
#include <stdio.h>
/* Some data that we want to find the slope, intercept and r2 for */
static const double x[] = { 1.47, 1.50, 1.52, 1.55, 1.57, 1.60, 1.63, 1.65, 1.68, 1.70, 1.73, 1.75, 1.78, 1.80, 1.83 };
static const double y[] = { 52.21, 53.12, 54.48, 55.84, 57.20, 58.57, 59.93, 61.29, 63.11, 64.47, 66.28, 68.10, 69.92, 72.19, 74.46 };
int main() {
double slope = 0.0;
double intercept = 0.0;
double r2 = 0.0;
int res = 0;
res = simple_linear_regression(x, y, sizeof(x) / sizeof(x[0]), &slope, &intercept, &r2);
if (res < 0) {
printf("Error: %s\n", simple_linear_regression_error_string(res));
return res;
}
printf("slope: %f\n", slope);
printf("intercept: %f\n", intercept);
printf("r2: %f\n", r2);
return 0;
}
The original example above worked well for me with slope and offset but I had a hard time with the corr coef. Maybe I don't have my parenthesis working the same as the assumed precedence? Anyway, with some help of other web pages I finally got values that match the linear trend-line in Excel. Thought I would share my code using Mark Lakata's variable names. Hope this helps.
double slope = ((n * sumxy) - (sumx * sumy )) / denom;
double intercept = ((sumy * sumx2) - (sumx * sumxy)) / denom;
double term1 = ((n * sumxy) - (sumx * sumy));
double term2 = ((n * sumx2) - (sumx * sumx));
double term3 = ((n * sumy2) - (sumy * sumy));
double term23 = (term2 * term3);
double r2 = 1.0;
if (fabs(term23) > MIN_DOUBLE) // Define MIN_DOUBLE somewhere as 1e-9 or similar
r2 = (term1 * term1) / term23;
as an assignment I had to code in C a simple linear regression using RMSE loss function. The program is dynamic and you can enter your own values and choose your own loss function which is for now limited to Root Mean Square Error. But first here are the algorithms I used:
now the code... you need gnuplot to display the chart, sudo apt install gnuplot
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <sys/types.h>
#define BUFFSIZE 64
#define MAXSIZE 100
static double vector_x[MAXSIZE] = {0};
static double vector_y[MAXSIZE] = {0};
static double vector_predict[MAXSIZE] = {0};
static double max_x;
static double max_y;
static double mean_x;
static double mean_y;
static double teta_0_intercept;
static double teta_1_grad;
static double RMSE;
static double r_square;
static double prediction;
static char intercept[BUFFSIZE];
static char grad[BUFFSIZE];
static char xrange[BUFFSIZE];
static char yrange[BUFFSIZE];
static char lossname_RMSE[BUFFSIZE] = "Simple Linear Regression using RMSE'";
static char cmd_gnu_0[BUFFSIZE] = "set title '";
static char cmd_gnu_1[BUFFSIZE] = "intercept = ";
static char cmd_gnu_2[BUFFSIZE] = "grad = ";
static char cmd_gnu_3[BUFFSIZE] = "set xrange [0:";
static char cmd_gnu_4[BUFFSIZE] = "set yrange [0:";
static char cmd_gnu_5[BUFFSIZE] = "f(x) = (grad * x) + intercept";
static char cmd_gnu_6[BUFFSIZE] = "plot f(x), 'data.temp' with points pointtype 7";
static char const *commands_gnuplot[] = {
cmd_gnu_0,
cmd_gnu_1,
cmd_gnu_2,
cmd_gnu_3,
cmd_gnu_4,
cmd_gnu_5,
cmd_gnu_6,
};
static size_t size;
static void user_input()
{
printf("Enter x,y vector size, MAX = 100\n");
scanf("%lu", &size);
if (size > MAXSIZE) {
printf("Wrong input size is too big\n");
user_input();
}
printf("vector's size is %lu\n", size);
size_t i;
for (i = 0; i < size; i++) {
printf("Enter vector_x[%ld] values\n", i);
scanf("%lf", &vector_x[i]);
}
for (i = 0; i < size; i++) {
printf("Enter vector_y[%ld] values\n", i);
scanf("%lf", &vector_y[i]);
}
}
static void display_vector()
{
size_t i;
for (i = 0; i < size; i++){
printf("vector_x[%lu] = %lf\t", i, vector_x[i]);
printf("vector_y[%lu] = %lf\n", i, vector_y[i]);
}
}
static void concatenate(char p[], char q[]) {
int c;
int d;
c = 0;
while (p[c] != '\0') {
c++;
}
d = 0;
while (q[d] != '\0') {
p[c] = q[d];
d++;
c++;
}
p[c] = '\0';
}
static void compute_mean_x_y()
{
size_t i;
double tmp_x = 0.0;
double tmp_y = 0.0;
for (i = 0; i < size; i++) {
tmp_x += vector_x[i];
tmp_y += vector_y[i];
}
mean_x = tmp_x / size;
mean_y = tmp_y / size;
printf("mean_x = %lf\n", mean_x);
printf("mean_y = %lf\n", mean_y);
}
static void compute_teta_1_grad()
{
double numerator = 0.0;
double denominator = 0.0;
double tmp1 = 0.0;
double tmp2 = 0.0;
size_t i;
for (i = 0; i < size; i++) {
numerator += (vector_x[i] - mean_x) * (vector_y[i] - mean_y);
}
for (i = 0; i < size; i++) {
tmp1 = vector_x[i] - mean_x;
tmp2 = tmp1 * tmp1;
denominator += tmp2;
}
teta_1_grad = numerator / denominator;
printf("teta_1_grad = %lf\n", teta_1_grad);
}
static void compute_teta_0_intercept()
{
teta_0_intercept = mean_y - (teta_1_grad * mean_x);
printf("teta_0_intercept = %lf\n", teta_0_intercept);
}
static void compute_prediction()
{
size_t i;
for (i = 0; i < size; i++) {
vector_predict[i] = teta_0_intercept + (teta_1_grad * vector_x[i]);
printf("y^[%ld] = %lf\n", i, vector_predict[i]);
}
printf("\n");
}
static void compute_RMSE()
{
compute_prediction();
double error = 0;
size_t i;
for (i = 0; i < size; i++) {
error = (vector_predict[i] - vector_y[i]) * (vector_predict[i] - vector_y[i]);
printf("error y^[%ld] = %lf\n", i, error);
RMSE += error;
}
/* mean */
RMSE = RMSE / size;
/* square root mean */
RMSE = sqrt(RMSE);
printf("\nRMSE = %lf\n", RMSE);
}
static void compute_loss_function()
{
int input = 0;
printf("Which loss function do you want to use?\n");
printf(" 1 - RMSE\n");
scanf("%d", &input);
switch(input) {
case 1:
concatenate(cmd_gnu_0, lossname_RMSE);
compute_RMSE();
printf("\n");
break;
default:
printf("Wrong input try again\n");
compute_loss_function(size);
}
}
static void compute_r_square(size_t size)
{
double num_err = 0.0;
double den_err = 0.0;
size_t i;
for (i = 0; i < size; i++) {
num_err += (vector_y[i] - vector_predict[i]) * (vector_y[i] - vector_predict[i]);
den_err += (vector_y[i] - mean_y) * (vector_y[i] - mean_y);
}
r_square = 1 - (num_err/den_err);
printf("R_square = %lf\n", r_square);
}
static void compute_predict_for_x()
{
double x = 0.0;
printf("Please enter x value\n");
scanf("%lf", &x);
prediction = teta_0_intercept + (teta_1_grad * x);
printf("y^ if x = %lf -> %lf\n",x, prediction);
}
static void compute_max_x_y()
{
size_t i;
double tmp1= 0.0;
double tmp2= 0.0;
for (i = 0; i < size; i++) {
if (vector_x[i] > tmp1) {
tmp1 = vector_x[i];
max_x = vector_x[i];
}
if (vector_y[i] > tmp2) {
tmp2 = vector_y[i];
max_y = vector_y[i];
}
}
printf("vector_x max value %lf\n", max_x);
printf("vector_y max value %lf\n", max_y);
}
static void display_model_line()
{
sprintf(intercept, "%0.7lf", teta_0_intercept);
sprintf(grad, "%0.7lf", teta_1_grad);
sprintf(xrange, "%0.7lf", max_x + 1);
sprintf(yrange, "%0.7lf", max_y + 1);
concatenate(cmd_gnu_1, intercept);
concatenate(cmd_gnu_2, grad);
concatenate(cmd_gnu_3, xrange);
concatenate(cmd_gnu_3, "]");
concatenate(cmd_gnu_4, yrange);
concatenate(cmd_gnu_4, "]");
printf("grad = %s\n", grad);
printf("intercept = %s\n", intercept);
printf("xrange = %s\n", xrange);
printf("yrange = %s\n", yrange);
printf("cmd_gnu_0: %s\n", cmd_gnu_0);
printf("cmd_gnu_1: %s\n", cmd_gnu_1);
printf("cmd_gnu_2: %s\n", cmd_gnu_2);
printf("cmd_gnu_3: %s\n", cmd_gnu_3);
printf("cmd_gnu_4: %s\n", cmd_gnu_4);
printf("cmd_gnu_5: %s\n", cmd_gnu_5);
printf("cmd_gnu_6: %s\n", cmd_gnu_6);
/* print plot */
FILE *gnuplot_pipe = (FILE*)popen("gnuplot -persistent", "w");
FILE *temp = (FILE*)fopen("data.temp", "w");
/* create data.temp */
size_t i;
for (i = 0; i < size; i++)
{
fprintf(temp, "%f %f \n", vector_x[i], vector_y[i]);
}
/* display gnuplot */
for (i = 0; i < 7; i++)
{
fprintf(gnuplot_pipe, "%s \n", commands_gnuplot[i]);
}
}
int main(void)
{
printf("===========================================\n");
printf("INPUT DATA\n");
printf("===========================================\n");
user_input();
display_vector();
printf("\n");
printf("===========================================\n");
printf("COMPUTE MEAN X:Y, TETA_1 TETA_0\n");
printf("===========================================\n");
compute_mean_x_y();
compute_max_x_y();
compute_teta_1_grad();
compute_teta_0_intercept();
printf("\n");
printf("===========================================\n");
printf("COMPUTE LOSS FUNCTION\n");
printf("===========================================\n");
compute_loss_function();
printf("===========================================\n");
printf("COMPUTE R_square\n");
printf("===========================================\n");
compute_r_square(size);
printf("\n");
printf("===========================================\n");
printf("COMPUTE y^ according to x\n");
printf("===========================================\n");
compute_predict_for_x();
printf("\n");
printf("===========================================\n");
printf("DISPLAY LINEAR REGRESSION\n");
printf("===========================================\n");
display_model_line();
printf("\n");
return 0;
}
Look at Section 1 of this paper. This section expresses a 2D linear regression as a matrix multiplication exercise. As long as your data is well-behaved, this technique should permit you to develop a quick least squares fit.
Depending on the size of your data, it might be worthwhile to algebraically reduce the matrix multiplication to simple set of equations, thereby avoiding the need to write a matmult() function. (Be forewarned, this is completely impractical for more than 4 or 5 data points!)
The fastest, most efficient way to solve least squares, as far as I am aware, is to subtract (the gradient)/(the 2nd order gradient) from your parameter vector. (2nd order gradient = i.e. the diagonal of the Hessian.)
Here is the intuition:
Let's say you want to optimize least squares over a single parameter. This is equivalent to finding the vertex of a parabola. Then, for any random initial parameter, x0, the vertex of the loss function is located at x0 - f(1) / f(2). That's because adding - f(1) / f(2) to x will always zero out the derivative, f(1).
Side note: Implementing this in Tensorflow, the solution appeared at w0 - f(1) / f(2) / (number of weights), but I'm not sure if that's due to Tensorflow or if it's due to something else..