〖数学算法〗积分算法(一)

当我上小学的时,就学习了球的体积公式V=(4/3)πR³,当时觉得它实在太神奇了,是不是求得这个公式的人把一个铁球熔成铁水,放在一个矩形容器中求的?直到大上学才知道是利用积分算得的,当然微积分这个东西对于包括我在内的广大同学们来说可能是恨大于爱,但不可否认是积分在几乎所有理工学科都有着无可替代的作用,所以博主就写一写求积分的算法,由于算法过多,为了避免篇幅过长,给读者造成疲劳感,我决定分4篇写积分算法。

唯一方便统一,本篇各算法均以这个最基本式子的来作例子


1.随机投点法(蒙特卡洛算法)


在求圆周率的文章中已经提及过一次此方法


在a到b和函数组成的矩形的范围内,随机投N个点,落到绿色阴影点的个数为M个,对于此图来说便可以容易得知积分的值(绿色阴影)为(M/N)*矩形面积。

代码清单:
public class JiFen {
	
	public static void main(String[] args){
	
		int N = 1000000;
		int count = 0;
		for(int i = 0; i < N; i++){
			double x = Math.random();
			double y = Math.random();
			if(f(x) >= y)
				count++;
		}
		System.out.println((double)count/N);
	}

	private static double f(double x) {
		
		return x*x;
	}
	
}
输出:0.333302

2.另一种蒙特卡洛法


第一种方法视乎情况比较特殊,如果是积分形式如下图:

对于普通情况,如果用第一种算法,就要判断随机点是投在了x轴的上方还是下方,对于矩形的选取还要分f(a),f(b)是否同号,以及f(a),f(b)的绝对值的大小,比较麻烦。 于是产生了另一种投点法:在a到b的范围内随机生成N个点x1~xn,则积分的值为(f(x1) + f(x2) + ...+ f(xn)) * (b-a)/m

代码清单:
public class JiFen_toudian2 {
	
	public static void main(String[] args){
	
		double a = 0;
		double b = 1;
		
		double area = getArea(a,b);
		
		System.out.println(area);
	}
	public static double getArea(double a, double b){
		int sign = 1;//正负号标志位
		if(a > b){
			double t = a;
			a = b;
			b = t;
			sign = -1;
		}
		int N = 10000;
		double sum = 0;
		for(int i = 0; i < N; i++){
			double x = a + (b-a)*Math.random();//随机生成1个在(a,b)范围内的点
			sum += f(x);
		}
		return (sum * (b-a)/N) * sign; 
	}
	private static double f(double x) {
		
		return x*x;
	}
	
}
输出:0.33966325465503505

3.定义求积分法


回忆一下最初是怎么求积分的,是把一块面积分解成N个小矩形,然后求面积和,最后求极限。我们就利用这种方式来实现它,但我们的N毕竟有限,为了结果更准确,把求矩形面积改成求梯形面积(当然矩形也是可以的),如下图:


把(a,b)分成N等分,积分值等于S1+S2+....+Sn,其中Si = (f(xi) + f(x(i+1))) * (b-a)/n / 2(矩形面积公式)

有了之前的基础,就可以比较容易的写程序了。

代码清单:
public class JiFen3 {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		
		double a;
		double b;
		double y;
		a = 0;
		b = 1;
		y = getArea(a, b);
		System.out.println(y);
	}
	static double f(double x){
		return x*x;
	}
	static double getArea(double a, double b){
		int sign = 1;
		if(a > b){
			double t = a;
			a = b;
			b = t;
			sign = -1;
		}
		double h;
		double x;
		double sum = 0;
		int n = 10000;
		h = Math.abs(a - b)/n;
		x = a;
		for(int i = 0; i < n; i++){
			
			sum += f(x) + f(x + h);
			x = x + h;
		}
		return sum * h / 2 * sign;
	}
}
输出:0.3333333349999429


4.变步长梯形求积分法


用定义求积分法,要确定积分的误差是很难的,我们无法找到一个正好的N值符合我们要求的误差值,所以就需对定义法进行改进,改进的方式就是把原来固定的步长改为变化的步长,利用二分法,如下图:

                                                      图4-1
                                                    图4-2

                                               图4-3

我们要分到什么时候呢? 分到 后一个面积和 - 前一个面积和 |  < 规定误差 时。这样我们就达到了精确的目的。

代码清单:
public class Jifen_bianchang {

	static double e = 0.00001;// 误差

	public static void main(String[] args) {
		double a = 0;// 积分下限
		double b = 1;// 积分上限
		double area = getArea(a, b);
		System.out.println(area);
	}

	public static double getArea(double a, double b) {

		int sign = 1;// 正负标志位
		if (a > b) {
			double t = a;
			a = b;
			b = t;
			sign = -1;
		}
		double s1 = 0;// 前一个面积和
		double s2 = 0;// 后一个面积和
		double h = b - a;
		s2 = getOneArea(a, b, h);

		for (int i = 2; Math.abs(s1 - s2) > e; i *= 2) {
			double t = h / i;// 每个梯形高
			double sum = 0;
			double x = a;
			for (int j = 0; j < i; j++) {// 求梯形和
				sum += getOneArea(x, x + t, t);
				x = x + t;
			}
			s1 = s2;
			s2 = sum;
		}
		return sign * s2;
	}

	public static double getOneArea(double a, double b, double h) {

		return (f(a) + f(b)) * h / 2;
	}

	public static double f(double x) {
		return x * x;
	}

}
输出:0.33333587646484375


积分算法(二) (未完待续)
积分算法(三) (未完待续)
积分算法(四) (未完待续)



==================================================================================================

  作者:nash_  欢迎转载,与人分享是进步的源泉!

  转载请保留原文地址http://blog.csdn.net/nash_/article/details/8560759

===================================================================================================

猜你喜欢

转载自blog.csdn.net/nash_/article/details/8560759