关于数学分析程序设计作业快速入门

数分骚骚函数作业快速入门

前言

我也不知道自己写的对不对, 也没有数据来测, 所以我欢迎一切质疑和讨论。

本人水平也有限, 很多地方讲的不到位, 所以说可以直接来找到我来讨论!

本篇由C语言编写 , 因为我上的是C语言程序设计 (

(毕竟有一个东西叫知识的诅咒qwq)

----by ska_0x08 2026 4 24

前置

什么是 typedef double (*func_t)(double x);

第一个 double 代表的是该函数返回的是一个double 数值

第二个 double 代表的是传入该函数的值是一个double 类型

我们称 “传入一个double 传出一个double” 这种类型的函数为 *func_t

在这个语句之后, func_t 就是一种声明

例如

func_t f;

意思就是 我定义了一个 func_t 类型的函数, 他的名字叫 \(f\) , 我在使用它的时候要传入一个double , 它吐出一个 double 出来。

下面是一个具体的例子

#include <stdio.h>
typedef double (*func_t) (double x);

double cube (double x){
	return x * x * x;
}

int main (){
	func_t p = cube;
	printf("%f\n", p(3));
}

事实上 类似于

double cube (double x){
	return x * x * x;
}

的部分应该在老师那里, 也就是说我们并不知道具体函数是什么, 但是我们可以使用 \(f(x_0)\) 这样的调用来获取在 \(x_0\) 处的值。 事实上, 对于本次的编程任务, 我们也不需要具体的函数, 只需要知道它在几点处的取值即可。

正片开始

Part.1 \(\space\) composite_trapezoid

具体要求

* 复化梯形公式
* @param f   被积函数
* @param a   积分下限
* @param b   积分上限
* @param m   等分数(子区间个数)
* @return    积分近似值

分析

\(h = \frac{b - a}{m}\)

\(T_m^{(1) }= \frac{h}{2}\sum\limits_{i = 1}^{m}[f(x_{i - 1}) + f(x_i)]\)

\(x_i = a + ih\)

我们要求解 \(Ans\) 就需要 \(x_i\) , \(x_i\) 需要 \(a, h\)\(h\) 只需要 \(b, a, m\) 即可求解。

为了增加代码可读性 , 我们用 数组来存储, \(x[i]\) 就是 \(x_i\) 的值

My coding

double composite_trapezoid(func_t f, double a, double b, int m) {
 	double x[114514191];
 	double h = (b - a)  / m;
	
 	for (int i = 0; i <= m; i ++) //这里一定从0开始
 		x[i] = a + i * h;
 		
 	double ans = 0;
 	
 	for (int i = 1; i <= m; i ++)
 		ans += (f(x[i - 1]) + f(x[i]));
 		
 	ans = ans * h / 2.0;
    return ans;
}

Part.2 \(\space\) composite_simpson

具体要求

* 复化 Simpson 公式
* @param f   被积函数
* @param a   积分下限  
* @param b   积分上限
* @param m   等分数(每段再 2 等分做 Simpson)
* @return    积分近似值

分析

由等式

\(T_m ^{(2)} = \frac{4T_{2m}^{(1)} - T_M^{(1)}}{4 - 1}\)

也就是说我们直接调用

第一题的答案即可。

double composite_simpson(func_t f, double a, double b, int m) {
    return (4.0 * composite_trapezoid(f, a, b, 2 * m) - composite_trapezoid(f, a, b, m)) / 3.0;
}

我不知道这样子能否通过本题, 所以我们还是从头好好写一遍吧!

\(T_m^{(2)} = \frac{h}{6} [f(a) + f(b) + 2 \sum\limits_{i = 1}^{m - 1}f(x_i) + 4 \sum\limits _{i = 1}^{m} f(x_{i - \frac{1}{2}})]\)

其中根据定义我们知道

\(x_{i - \frac{1}{2}} = \frac{x_{i - 1} + x_i}{2}\)

那么也就可以根据公式写出来啦

My coding

double composite_simpson(func_t f, double a, double b, int m) {
 	double x[114514191];
 	double h = (b - a)  / m;
	
 	for (int i = 0; i <= m; i ++) //这里一定从0开始
 		x[i] = a + i * h;
 		
 	double ans = 0;
 	
 	ans += f(a);
 	ans += f(b);
 	
 	for (int i = 1; i <= m - 1; i ++)
 		ans += 2.0 * f(x[i]);
 		
 	for (int i = 1; i <= m; i ++){
	 	double x_ = (x[i - 1] + x[i]) / 2.0;
	 	//这里x_ 对应于 下表为 i-(1/2) 的x的值
	 	ans += 4.0 * f(x_);
	}
	
	ans = ans * h / 6.0;
    return ans;
}

小注 : 前两种方法大抵可以不用数组把 \(x_i\) 存储下来, 我在此处这么写只是为了提高可读性。

Part.3 \(\space\) romberg

具体要求

 * Romberg 方法
 * @param f    被积函数
 * @param a    积分下限
 * @param b    积分上限
 * @param tol  收敛精度(对角线相邻元素之差的绝对值)
 * @return     积分近似值

分析

公式

\(T_m ^ {(k + 1)} = \frac{4^kT_{2m}^{(k)} - T_{m} ^ {k}}{4^k - 1}\)

这个实现起来有一定难度, 我们来逐步分解。

根据任务需求, 我们只有在 \(\vert T_m^{(k)} - T_m^{(k-1)}\vert < tol\) 的时候才计算结束。

我们考虑这么构建代码

  • 计算 \(T_m^{(k)}\)
  • \(\vert T_{m} ^ {(k)} - T_m ^ {(k - 1)}\vert < tol\) 结束循环, 返回 \(T_m ^{(k)}\)
  • 否则 \(k = k + 1\) 进入下一次循环。

下面的重头戏在于如何计算 \(T_m ^{(k)}\)

为了避免重复计算 我们将之前计算的值存储起来

我们记录二维数组 \(T\) 。 根据书上 \(P_{300}\) 公式我们会发现我们只会计算形如 \(T_{2^p m} ^{(k)}\) 这样的式子, 所以我们记录 \(T[k][p] = T_{2^pm}^{(k)}\)

根据书上的指示, 在求解 \(T_m^{(k)}\) 的时候, 我们需要 \(T_{m} ^{(k - 1)}\) 以及 \(T_{2m}^{(k - 1)}\)

注意 ! 这个时候 \(T_m ^ {(k - 1)}\) 我们之前已经求解过了! 也就是说问题转换成了 求解 \(T_{2m} ^{(k - 1)}\)

\(T_{2m} ^ {(k - 1)}\) 需要 \(T_{2m}^{(k - 2)}\)\(T_{4m} ^ {(k - 2)}\) , 注意 这个时候 \(T_{2m} ^ {(k - 2)}\) 已经是求解过的了。

反复上述过程我们发现 最终我们只需要求解 \(T_{2^{(k - 1)}m}^ {(1)}\) 即可

根据 \(T_{2^{k - 1}m}^ {(1)}\)\(T_{2^{k - 2}m}^ {(1)}\) 求解 \(T_{2^{k - 2}m}^ {(2)}\)

根据 \(T_{2^{k - 2}m}^ {(2)}\)\(T_{2^{k - 3}m}^ {(2)}\) 求解 \(T_{2^{k - 3}m}^ {(3)}\)

......

根据 \(T_{2^{k - i}m}^ {(i)}\)\(T_{2^{k - i - 1}m}^ {(i)}\) 求解 \(T_{2^{k - (i + 1)}m}^ {(i + 1)}\) (这个其实是我们编写程序的关键)

......

根据 \(T_{2^{1}m}^ {(k - 1)}\)\(T_{2^{0}m}^ {(k - 1)}\) 求解 \(T_{2^{0}m}^ {(k)}\)

直接循环处理即可

My Coding

double romberg(func_t f, double a, double b, double tol) {
	double T[1141][1141];
	T[0][0] = 0;
	int k = 1;
	int m = 91;
	double pow2 = 1.0;
	while (1){
		
		T[1][k - 1] = composite_trapezoid(f, a, b, pow2 * m);

		double pow4 = 4.0;
		for (int i = 1; i <= k - 1; i ++){
			T[i + 1][k - i - 1] = ((pow4 * T[i][k - i] - T[i][k - i - 1]) / (pow4 - 1));
			pow4 *= 4.0;
		}
		
		if (fabs(T[k][0] - T[k - 1][0]) < tol) break;
		k ++;
		pow2 *= 2.0;
	}
    
    return T[k][0];
}

当然这样做对我来说还是不完美, 仔细观察过程, 我们会发现有很多空间被浪费掉了, 于是我们有一个节省空间的写法。

缺点就是不直观, 看得明白就看看吧(感兴趣的可以直接来问我ovo)也不是很难的 trick

double romberg(func_t f, double a, double b, double tol) {
	double T[11451419];
	T[0] = 0;
	int k = 1;
	int m = 91;
	double pow2 = 1.0;
	while (1){
		double pre = composite_trapezoid(f, a, b, pow2 * m);
		double tmp;
		double pow4 = 4.0;
		for (int i = 1; i <= k - 1; i ++){
			tmp = pre;
			pre = ((pow4 * pre - T[i]) / (pow4 - 1));
			T[i] = tmp;
		}
		T[k] = pre;
		if (fabs(T[k] - T[k - 1]) < tol) break;
		k ++;
		pow2 *= 2.0;
	}
    
    return T[k];
}

Part.4 \(\space\) gauss_legendre

具体要求

* Gauss-Legendre 求积公式
* @param f   被积函数
* @param a   积分下限
* @param b   积分上限
* @param n   Gauss 点个数
* @return    积分近似值

分析

本质是

\(ans = \frac{b - a}{2}\sum\limits_{i = 1} ^ {n + 1} a^*_i f(\frac{b-a}{2}x^*_i + \frac{a+b}{2})\)

其中当 \(n\) 固定之后, 其对应的 \(x^*_i\)\(a^*_i\) 都是固定的。

具体见书 \(P_{302}\) 页。

在实现的时候, 我们通过打表把 \(n\) 所确定的 \(x^*_i\)\(a^*_i\) 数列预处理出来, 然后就直接根据上式求值即可。

My Coding

double gauss_legendre(func_t f, double a, double b, int n) {
	double x_1[3] = {0, 0.5773502692, -0.5773502692}, a_1[3] = {0, 1.0, 1.0};
	double x_2[4] = {0, 0.7745966692, -0.7745966692, 0}, a_2[4] = {0, 0.5555555556, 0.5555555556, 0.8888888889};
    double x_3[5] = {0, 0.3399810436, -0.3399810436, 0.8611363116, -0.8611363116},
			a_3[5] = {0, 0.6521451549, 0.6521451549, 0.3478548451, 0.3478548451};
	double x_4[6] = {0, 0.5384693101, -0.5384693101, 0.9061798459, -0.9061798459, 0},
			a_4[6] = {0, 0.4786286705, 0.4786286705, 0.2369268851, 0.2369268851, 0.5688888889};
	double ans = 0;
	double tmp1, tmp2;
	tmp1 = (b - a) / 2.0;
	tmp2 = (b + a) / 2.0;
	if (n == 1){
		for (int i = 1; i <= n + 1; i ++){
			x_ = x_1[i] * tmp1 + tmp2;
			ans += f(x_) * a_1[i];
		}
	}
	if (n == 2){
		for (int i = 1; i <= n + 1; i ++){
			x_ = x_2[i] * tmp1 + tmp2;
			ans += f(x_) * a_2[i];
		}
	}
	if (n == 3){
		for (int i = 1; i <= n + 1; i ++){
			x_ = x_3[i] * tmp1 + tmp2;
			ans += f(x_) * a_3[i];
		}
	}
	if (n == 4){
		for (int i = 1; i <= n + 1; i ++){
			x_ = x_4[i] * tmp1 + tmp2;
			ans += f(x_) * a_4[i];
		}
	}
	ans *= tmp1;
	return ans;
}

相信大家已经跃跃欲试了, 快点来试试吧!

posted @ 2026-04-24 20:38  ska_0x08  阅读(28)  评论(0)    收藏  举报