这一章节主要介绍我们在进行数值分析常用的二分、三分和一个近似求解区间积分的辛普森法。

首先介绍二分。

其实二分的思想很好理解并且笔者在之前的一些文章中也有所渗透,对于二次函数甚至单元高次函数的零点求解、线段树还有《algorithm puzzle》当中的“切割钢条”问题,都是基于二分思想。

下面我们通过具体的问题来应用二分这种数值分析的策略。

Ex1:按揭贷款

以P%的年利率借贷N元后,在M个月内,以每月还C元的方式还贷。贷款期限内,按照如下形式计算贷款余额。

(1)    贷款余额从余额N元开始。

(2)    美国一个月,贷款余额会增加一个月的利息,月利率为(P/12)%。

(3)    增加利息后,从贷款余额中扣除当月的还款项。

分析:我们从较为抽象化的角度来建立一个数学模型,这道问题M,P,N,C等输入数据相当于是自变量,而还贷过程相当于是个函数机制,M月后的借贷余额便是因变量。

对于函数的这三要素,我们都是知道的,那么我们可以将问题转化成在满足因变量≤0的情况下,求解C的最小值。

如果我们想尝试利用数学方法,会发现我们虽然了解函数机制,但是我们难以列出其表达式,因此这里我们基于自变量C的解区间,进行二分搜索,而这个过程恰恰基于函数机制和因变量(函数余额)的范围限制。

简单的参考代码如下:

  #include<cstdio>

using namespace std;

double balance(double amount , int duration , double rates , double monthlypayment)//求解每月还贷monthlypayment元,duration个月后的贷款余额。

{

    double balance = amount;//贷款余额初始化

    for(int i = ;i < duration;i++)

    {

        balance *= (1.0 + (rates/12.0)/100.0);//这里涉及简单的还贷机制,我们将其理解为每月底涨息后还贷

        balance -= monthlypayment;

    }

    return balance;

}

double payment(double amount , int duration , double rates)

{

    double l = ;//每月还贷可行解的区间,基于这道问题的特殊情景(区间右端点一定可行,这与这个函数最后return的变量呼应),解区间分布情况是(,]

    double r = amount*(1.0 + (rates/12.0)/100.0);

    double mid;

    for(int i = ;i < ;i++)

    {

         mid = (r + l)/2.0;

        if(balance(amount , duration , rates , mid) <= )

                r = mid;

        else

                l = mid;

    }

    return r;

}

int main()

{

    double amount , rates;

    int duration;

    printf("Input amount,rates and duration:");

    scanf("%lf %lf %d",&amount,&rates,&duration);

    printf("the lowest monthlypayment: %llf",payment(amount,duration,rates));

}

下面我们开始介绍三分。

其实所谓三分,如果百度一下,会发现在百度百科里它是一种“哲学形态”出现的,并且被称为典型的中国思维,即在解决问题的时候基于西方所谓“二分”的思维,将正反对立双方进行统一,形成事物的“第三面”。

但是我们这里并不从哲学的角度去看待三分,而是从数值分析的角度去看待三分,即将其看成一种筛选解的搜索方式。

对于一个函数f(x),考察其在[lo,hi]上的性质。

零点:这个我们都很熟悉,利用二分法可以快速求解,但是为什么要在这里介绍呢?是为了将其与三分进行区别和联系。

极值点:

为了讨论的方便和算法的简便,我们统一一下讨论的函数,它在区间[lo,hi]上先严格递增后严格递减,存在一个极大值点。

想一想,如果想用这种基于解区间进行搜索的办法,利用二分能否能够实现呢?不能。因为对于零点,我们所基于的解空间的两端仿佛就是走了两个极端,一个大于0一个小于0,这导致零点必然会落在这之间,并且二分区间之后依然能够容易得得到更小的解区间,而对于极值的情况,显然解区间的两个端点要么都小于极值点的,因此这样你二分是无法选择下一个更下的解区间,这就使得搜索过程变得没了意义。

那应该怎么处理呢?这就很自然的引出了今天的主角——三分法。我们将区间[lo,hi]三等分(三等分点怎么求就不必我多说了),无非会出现如下的两种情况:

(1)左三等分点对应的函数值较大,那么我们可以很肯定的说,三等分区间之中最右边的区间一定不会有极值,因为假设最右边的区间上还有极值点,按照我们先前的定义,极值点的左侧是严格递增的,这与事实相悖。

(2)同理,如果右三等分点对应的函数值较大,我们能够排除三等分区间的最左侧的区间。

而类似于二分搜索法,对于在实数区间上的三分搜索,我们默认进行100次三分之后终止,得到近似解。

简单的函数代码如下。

double f(double x);//求解函数值f(x)

double ternary(double lo , double hi)

{

    double a  = (*lo + hi)/;//左三等分点

    double b  = (lo + *hi)/;//右三等分点

    for(int i = ;i < ;i++)//三分100次终止

    {

      if(f(a) < f(b))   lo = a;

      else              hi = b;

    }

    return (lo + hi)/;

}

辛普森法:

所谓辛普森法,即是在求解函数f(x)在[a,b]区间上的积分是,将x = a、b、(a + b)/2三点视作抛物线上的三个点,这样我们通过简单的定积分运算,可以得到一个公式,即:

但是,上面提到的“将三点视为抛物线上的三个点”这种近似处理显然是要付出相应的代价的,这里在微积分的层面就要用到极限的思维方法,但是在这里我们从计算机编程的角度来看,我们基于二分来设计一个递归过程来“偿还”这种代价。

假设我们求得了[a,b]上的积分,我们分别二分区间得到[a,(a+b)/2]、[(a+b)/2,b]并用相同的办法求出这两个区间上的积分值,限制二者的和与先前求得的积分误差在1e-10以内,则表明认为符合了我们上文提到的“近似”处理,否自,我们继续二分区间,形成一个递归过程,知道满足误差的限定为止。

我们通过一个具体的题目来实现这个算法流程。(Problem source : hdu 1724)

给出一个椭圆方程,的两个参数a、b和一个区间[c,d]的左右端点,请求解直线x = c,x=d和椭圆围成的面积。

简单的参考代码如下:

#include <iostream>
#include <math.h>
#include<cstdio>
using namespace std; const double esp = 1e-;
double a,b; double f(double x)//被积函数
{
return b * sqrt(1.0-(x*x)/(a*a));
} double simpson(double l,double r)
{
return (f(l)+*f((l+r)/2.0)+f(r))/6.0*(r-l);
} double integral(double l,double r)
{
double mid = (l+r)/2.0;
double res = simpson(l,r);
if (fabs(res-simpson(l,mid)-simpson(mid,r)) < esp)
return res;
else
return integral(l,mid) + integral(mid,r);
}
int main()
{
int T;
double l,r;
cin >> T;
while (T--)
{
cin >> a >> b >> l >> r;
printf("%.3lf\n",*integral(l,r));
}
return ;
}

《算法问题实战策略》-chaper13-数值分析的更多相关文章

  1. 算法问题实战策略 PICNIC

    下面是另一道搜索题目的解答过程题目是<算法问题实战策略>中的一题oj地址是韩国网站 连接比较慢 https://algospot.com/judge/problem/read/PICNIC ...

  2. 《算法问题实战策略》-chaper7-穷举法

    关于这一章节<算法实战策略>有一段概述问题,我认为对于编程人员来说非常有价值,故在这里进行如下的摘抄: 构想算法是很艰难的工作.相比大家都经历过,面对复杂的要求只是傻乎乎地盯着显示器,或者 ...

  3. 《算法问题实战策略》-chaper32-网络流

    基本的网络流模型: 在图论这一块初步的应用领域中,两个最常见的关注点,其一时图中的路径长度,也就是我们常说的的最短路径问题,另一个则是所谓的“流问题”. 流问题的基本概念: 首先给出一张图. 其实所谓 ...

  4. 《算法问题实战策略》——chaper9——动态规划法技巧

    Q1: 数字游戏: 两个人(A.B)用n个整数排成的一排棋盘玩游戏,游戏从A开始,每个人有如下操作: (1)    拿走棋盘最右侧或者最左侧的棋子,被拿走的数字从棋盘中抹掉. (2)    棋盘中还剩 ...

  5. 《算法问题实战策略》-chaper8-动态规划法

    Q1:偶尔在电视上看到一些被称为“神童”的孩子们背诵小数点以后几万位的圆周率.背诵这么长的数字,可利用分割数字的方法.我们用这种方法将数字按照位数不等的大小分割后再背诵. 分割形式如下: 所有数字都相 ...

  6. 《算法问题实战策略》-chaper21-树的实现和遍历

    这一章节开始介绍一个数据结构中的一个基本概念——树. 我们从数据结构的解读来解释树结构的重要性,现实世界的数据除了最基本的线性结构(我们常用队列.数组和链表等结构表征),还有一个重要的特性——层级结构 ...

  7. 算法问题实战策略 QUADTREE

    地址 https://algospot.com/judge/problem/read/QUADTREE 将压缩字符串还原后翻转再次压缩的朴素做法 在数据量庞大的情况下是不可取的 所以需要在压缩的情况下 ...

  8. 算法问题实战策略 DICTIONARY

    地址 https://algospot.com/judge/problem/read/DICTIONARY 解法 构造一个26字母的有向图 判断无回路后 就可以输出判断出来的字符序了 比较各个字母的先 ...

  9. 算法问题实战策略 MEETINGROOM 附一份tarjan模板

    地址 https://algospot.com/judge/problem/read/MEETINGROOM 解答  2-sat 代码样例过了 没有ac. 我又没有正确代码对拍..... 已确认是输出 ...

随机推荐

  1. C#解leetcode 16. 3Sum Closest

    Given an array S of n integers, find three integers in S such that the sum is closest to a given num ...

  2. MySQL存储过程详解 mysql 存储过程(二)

    mysql存储过程详解 1.      存储过程简介 我们常用的操作数据库语言SQL语句在执行的时候需要要先编译,然后执行,而存储过程(Stored Procedure)是一组为了完成特定功能的SQL ...

  3. 一位ACM过来人的心得

    刻苦的训练我打算最后稍微提一下.主要说后者:什么是有效地训练? 我想说下我的理解. 很多ACMer入门的时候,都被告知:要多做题,做个500多道就变牛了.其实,这既不是充分条件.也不会是必要条件. 我 ...

  4. Ajax+Node分页

    思路: ajax分页:1.服务器server.js,写布局;2.写接口文档(不管客户端,只写服务器端接口)3.写客户端,绑定数据,分页,优化点击过了,就不再请求:4.给每个li绑定点击事件,跳转详情页 ...

  5. java编程思想-泛型思维导图

  6. mybatis 一对一关联

    首先建表: CREATE TABLE teacher( t_id INT PRIMARY KEY AUTO_INCREMENT, t_name VARCHAR(20) ); CREATE TABLE ...

  7. MPICH2在两台Ubuntu上安装(用mpd做进程管理)

    本文在经过大量的实验终于不负众望成功的在两台Ubuntu 12.04上部署MPI的一个小型集群,MPICH2所用版本为mpich2-1.4.1,下载地址:http://www.mcs.anl.gov/ ...

  8. python运维开发之第六天

    Python面向对象 python从设计之初就已经是一门面向对象的语言,在python中创建一个类和对象很容易. 面向对象简介:类(class),类变量,object(基类),实例变量,构造函数,封装 ...

  9. tornado项目

    tornado项目之基于领域驱动模型架构设计的京东用户管理后台 本博文将一步步揭秘京东等大型网站的领域驱动模型,致力于让读者完全掌握这种网络架构中的“高富帅”. 一.预备知识: 1.接口: pytho ...

  10. 针对上一篇文章中的代码,想出的重构方案(python实现)

    #!/usr/bin/env python class Processor: def __init__(self, processor): self.processor = processor def ...