2016-11-19 51 views
0

目前,我正在编写一个程序,通过使用梯形法则和它们的组合来计算方程的积分以存档更高的精度。现在您可以在下面看到,我已经对函数进行了硬编码。是否可以读入数学方程式并对其进行评估?我知道我可以在输入字符列表中读取,然后评估函数(就像char [i] =='+'做的那样),但是有没有更简单的方法? 提前谢谢!C从命令行读取数学公式

void Integral_TN (double* TN_ptr,double a,double b,int n,int my_rank,int p){ 
    int i; 
    double a_loc; 
    double b_loc;   
    double hN; 

    *TN_ptr = 0; 
    hN = (b-a)/n; 

    a_loc = a + my_rank*n/p*hN;  
    b_loc = a + (my_rank+1)*n/p*hN; 
    *TN_ptr += (function(a_loc)+function(b_loc))/2;  /*Evaluate f at the borders*/ 

    for(i = 1; i < n/p; i++){ 
      *TN_ptr += function(a_loc + i*hN);   /*Evaluate f at the inner nodes*/ 
    } 
    *TN_ptr = *TN_ptr*hN; 
} 

double function(double x){ 
     double y; 
     y = 1/(1+x*x); 
     return y; 
} 
+2

也许有另一个去格式化代码,并使其更具可读性。 PS什么_感谢你的优势_是什么意思? –

+0

完成。重要的部分是功能“功能”。现在这个函数是硬编码的,我想把它作为一个参数来读取,例如从命令行。 – Noveel

+0

听起来像你需要一个解析器。 – EOF

回答

1

skrtbhtngr已经回答了陈述的问题,但我想解决潜在的问题。

应用Unix philosophy。使用一个工具生成数据,另一个使用它 - 在这里,使用梯形法则计算积分。

您可以使用最简单的格式是使用Gnuplot支持的相同:

  • 空行被忽略
  • 线,#开始被忽略,可以用于注释
  • 每一行定义一个样品

本质上讲,你可以描述一个正弦曲线非常粗略地使用

#x sin(x) 
0.000 0.000000000 
0.100 0.099833417 
0.200 0.198669331 
0.300 0.295520207 
0.400 0.389418342 
0.500 0.479425539 
0.600 0.564642473 
0.700 0.644217687 
0.800 0.717356091 
0.900 0.783326910 
1.000 0.841470985 
1.100 0.891207360 
1.200 0.932039086 
1.300 0.963558185 
1.400 0.985449730 
1.500 0.997494987 
1.600 0.999573603 
1.700 0.991664810 
1.800 0.973847631 
1.900 0.946300088 
2.000 0.909297427 
2.100 0.863209367 
2.200 0.808496404 
2.300 0.745705212 
2.400 0.675463181 
2.500 0.598472144 
2.600 0.515501372 
2.700 0.427379880 
2.800 0.334988150 
2.900 0.239249329 
3.000 0.141120008 
3.100 0.041580662 
3.200 -0.058374143 
3.300 -0.157745694 
3.400 -0.255541102 
3.500 -0.350783228 
3.600 -0.442520443 
3.700 -0.529836141 
3.800 -0.611857891 
3.900 -0.687766159 
4.000 -0.756802495 
4.100 -0.818277111 
4.200 -0.871575772 
4.300 -0.916165937 
4.400 -0.951602074 
4.500 -0.977530118 
4.600 -0.993691004 
4.700 -0.999923258 
4.800 -0.996164609 
4.900 -0.982452613 
5.000 -0.958924275 
5.100 -0.925814682 
5.200 -0.883454656 
5.300 -0.832267442 
5.400 -0.772764488 
5.500 -0.705540326 
5.600 -0.631266638 
5.700 -0.550685543 
5.800 -0.464602179 
5.900 -0.373876665 
6.000 -0.279415498 
6.100 -0.182162504 
6.200 -0.083089403 

你可以使用例如。 awk来产生,像我一样:

awk 'BEGIN { printf "#x sin(x)\n" ; for (x=0.0; x<6.3; x+=0.1) printf "%.3f %11.9f\n", x, sin(x) }' 

如果您保存到一个文件中(追加> data.txt上述命令),你可以在gnuplot的使用

plot "data.txt" using 1:2 notitle with lines 

这些数据绘制很容易阅读C程序。因为我只使用POSIX.1系统(Linux,BSD和macOS),而POSIX.1提供了非常有用的getline()函数 - 它可以让你读取任意长度的行,动态分配足够大的缓冲区 - 这个特定的实现还需要POSIX.1支持。换句话说,除了Windows以外,它基本上都可以工作。

#define _POSIX_C_SOURCE 200809L 
#include <stdlib.h> 
#include <string.h> 
#include <stdio.h> 
#include <errno.h> 

/* Read x y(x) values, one pair per line, from a stream. 
    The arrays are dynamically allocated, and pointers stored 
    to *xptr and *yptr. The size of the arrays is stored at *nptr. 
    They are initially cleared to NULL/zero. 
    The function returns 0 if success, an errno error code otherwise: 
     EINVAL: Invalid function parameters 
     EIO:  Read error 
     ENOMEM: Out of memory 
     EBADMSG: Malformed line 
*/ 
int read_xy(double **xptr, double **yptr, size_t *nptr, FILE *in) 
{ 
    /* Line input buffer variables. */ 
    char *line = NULL; 
    size_t size = 0; 
    ssize_t len; 

    /* Data array variables. */ 
    double *x = NULL; 
    double *y = NULL; 
    size_t n = 0; /* Entries in x[] and y[] */ 
    size_t nmax = 0; /* Entries allocated */ 

    /* Temporary variables. */ 
    double xval, yval, *newx, *newy; 

    /* We clear the output parameters to NULL or zero, 
     in case the caller is careless and does not check 
     the return value. Clearing them ensures they do 
     not contain garbage in such a case. */ 
    if (xptr) 
     *xptr = NULL; 
    if (yptr) 
     *yptr = NULL; 
    if (nptr) 
     *nptr = 0; 

    /* We need in and nptr, and at least one of xptr and yptr. */ 
    if (!in || !nptr || (!xptr && !yptr)) 
     return errno = EINVAL; 

    /* If an error has already occurred in 'in', 
     we do not even try to read from it. */ 
    if (ferror(in)) 
     return EIO; 

    while (1) { 

     /* Read next input line. */ 
     len = getline(&line, &size, in); 

     /* End of input or error? */ 
     if (len < 0) 
      break; 

     /* Skip empty and comment lines. */ 
     if (len == 0 || 
      line[0] == '\n' || (line[0] == '\r' && line[1] == '\n') || 
      line[0] == '#') 
      continue; 

     /* Parse the line. */ 
     if (sscanf(line, " %lf %lf", &xval, &yval) != 2) 
      break; 

     /* Need to grow the dynamically allocated arrays? */ 
     if (n >= nmax) { 

      /* Allocation policy. 
       We allocate room for at least 16 doubles, 
       then double the size up to 1048576 (=2^20), 
       then adjust to the next full multiple of 1048576. 
       This is not 'the best', but it is robust, 
       and not too wasteful. 
      */ 
      if (n < 16) 
       nmax = 16; 
      else 
      if (n < 1048576) 
       nmax = n * 2; 
      else 
       nmax = (n | 1048575) + 1048576; 

      /* Note: realloc(NULL, size) is equivalent to malloc(size). 
       If the realloc() call fails, it returns NULL, 
       but the original array is still valid. 
       Also note that free(NULL) is safe, and does nothing. 
      */ 
      newx = realloc(x, nmax * sizeof x[0]); 
      newy = realloc(y, nmax * sizeof y[0]); 
      if (newx) 
       x = newx; 
      if (newy) 
       y = newy; 
      if (!newx || !newy) { 
       /* One or both of the allocations failed. */ 
       free(line); 
       free(x); 
       free(y); 
       return ENOMEM; 
      } 
     } 

     /* Save the parsed values to the arrays. */ 
     x[n] = xval; 
     y[n] = yval; 
     n++; 
    } 

    /* We no longer need the line buffer. */ 
    free(line); 

    /* Did a read error occur? */ 
    if (ferror(in)) { 
     free(x); 
     free(y); 
     return EIO; 
    } 

    /* Was there no data to read? */ 
    if (n < 1) { 
     free(x); 
     free(y); 
     return 0; 
    } 

    /* Reallocate the arrays to their exact sizes 
     (actually, allow for one extra double at the end, 
     because it is often useful to copy the initial 
     ones there if the data is considered cyclic). 
    */ 
    nmax = n + 1; /* One extra just because it is so often useful. */ 
    newx = realloc(x, nmax * sizeof x[0]); 
    newy = realloc(y, nmax * sizeof y[0]); 
    if (newx) 
     x = newx; 
    if (newy) 
     y = newy; 
    if (!newx || !newy) { 
     free(x); 
     free(y); 
     return ENOMEM; 
    } 

    /* Save the array pointers. */ 
    if (xptr) 
     *xptr = x; 
    else 
     free(x); 

    if (yptr) 
     *yptr = y; 
    else 
     free(y); 

    /* Save the number of samples read. */ 
    *nptr = n; 

    /* If feof(in) is true, then we read everything 
     up to end of input. Otherwise, we stopped at 
     a line we could not parse. 
    */ 
    if (!feof(in)) 
     return EBADMSG; 

    return 0; 
} 

该函数或类似的东西应该在每个数值计算课程的课程材料中。他们只是非常有用。除了系统管理员为每个进程设置的可能的内存分配限制以外,这个特定的内存对它可以读取的数据的大小没有固有的限制。我知道一个事实,即如果你只有足够的内存,它可以成功读取数十亿行数据。

使用该功能非常简单。以下是一个示例main(),它只是从标准输入中读取这些数据 - 请记住,您可以通过在运行时将< file附加到命令中来从文件中读取数据,并将数据输出。

int main(void) 
{ 
    double *x, *y; 
    size_t i, n; 
    int  result; 

    result = read_xy(&x, &y, &n, stdin); 

    switch (result) { 
    case 0: /* No errors */ 
     break; 

    case EBADMSG: 
     if (n > 1) 
      fprintf(stderr, "Invalid line after %zu data samples.\n", n); 
     else 
      fprintf(stderr, "Cannot parse first input line.\n"); 
     return EXIT_FAILURE; 

    case ENOMEM: 
     fprintf(stderr, "Out of memory.\n"); 
     return EXIT_FAILURE; 

    case EIO: 
     fprintf(stderr, "Read error.\n"); 
     return EXIT_FAILURE; 

    case EINVAL: 
     fprintf(stderr, "Invalid parameters to the read_xy() function!\n"); 
     return EXIT_FAILURE; 

    default: 
     fprintf(stderr, "%s.\n", strerror(result)); 
     return EXIT_FAILURE; 
    } 

    printf("Read %zu samples:\n", n); 
    for (i = 0; i < n; i++) 
     printf("%.9f %.9f\n", x[i], y[i]); 

    return EXIT_SUCCESS; 
} 

注意,大部分是switch (result) { .. }错误报告代码。这个例子非常小心的告诉你,如果发生错误,原因在于,作为用户,你需要知道什么时候知道他们可能会喷出垃圾,并且在现实生活中会倾向于程序放弃而不是静静地扔出垃圾 - 也许会让你相信它是有效的。

虽然可以修改上面的代码,甚至在Windows上运行(你getline()fgets()取代,例如,希望你使用它的缓冲区大小就足够了;而且可能还需要改变一些错误代码errno)。然而,在Top 500超级计算机列表中没有Windows机器是有原因的:POSIXy系统(Unix和Linux)更适合科学计算。所以,如果你打算开展一些科学计算,那么你可能只需要建立一个Linux或BSD虚拟机,然后在那里进行开发。

+0

谢谢你的依靠帮助我作为一个起点。由于我使用Kubuntu无论如何,我不必更改代码。 – Noveel

1

没有简单的方法来实现您的目标。如果您想要将特定公式应用于某些值,则必须为其定义一个函数并输入值。

如果您希望输入整个表达式(包含值和运算符)作为输入并获得所需的结果作为输出,则必须超出基本编程范围。你需要创建一个解析器。

举例来说,如果你提供3+2*4作为输入,你可能期望11为输出而没有单独的值324阅读。这可以通过在解析器生成器(如YACC)中实现自定义解析器来实现。基本上,您将创建并定义关于如何解释输入的新规则。

+0

...你需要定义你的语言来表达数学的单行。 –

+0

不一定@PaulOgilvie。 – skrtbhtngr