活动介绍

函数最小化与最大化方法全解析

立即解锁
发布时间: 2025-08-21 02:03:49 阅读量: 1 订阅数: 6
PDF

C语言中的数值计算艺术:科学计算必备指南

### 函数最小化与最大化方法全解析 #### 1. 引言 在实际应用中,我们常常会遇到这样的问题:给定一个依赖于一个或多个自变量的函数 \(f\),需要找出这些自变量取何值时,函数 \(f\) 达到最大值或最小值,进而计算出该最大值或最小值。其实,最大化和最小化问题紧密相关,因为一个人的函数 \(f\) 可能是另一个人的 \(-f\)。在计算时,我们通常希望快速、低成本且占用较少内存。 函数的极值可以分为全局极值(函数在整个定义域内的最高或最低值)和局部极值(在有限邻域内的最高或最低值,且不在该邻域的边界上)。寻找全局极值通常是一个极具挑战性的问题,不过有两种常用的启发式方法:一是从不同的自变量初始值出发寻找局部极值,然后挑选出其中最极端的值;二是对局部极值进行有限幅度的扰动,观察是否能得到更好的点。 近年来,模拟退火方法在解决各种全局极值问题上取得了显著成效。此外,经济学家和一些工程师特别关注约束优化问题,其中线性规划是约束优化中发展较为成熟的一个领域,它涉及的函数和约束条件恰好都是自变量的线性函数。 #### 2. 一维黄金分割搜索 在一维情况下,我们可以回顾二分法求函数根的过程。类似地,在函数最小化问题中,我们需要明确“包围”最小值的含义。对于函数的根,当两个点处函数值异号时,我们知道根被这两个点包围;而对于最小值,需要三个点 \(a < b < c\)(或 \(c < b < a\)),使得 \(f(b)\) 小于 \(f(a)\) 和 \(f(c)\),此时我们知道函数在区间 \((a, c)\) 内有最小值。 接下来,我们要选择一个新的点 \(x\),可以在 \(a\) 和 \(b\) 之间,也可以在 \(b\) 和 \(c\) 之间。通过评估 \(f(x)\) 的值,我们可以得到一个新的包围三元组。这个过程会一直持续,直到包围区间足够小。 那么,多小才算“足够小”呢?一般来说,由于函数在最小值附近的形状可以用泰勒定理近似表示,我们很难要求包围区间的宽度小于其中心值的 \(\sqrt{\epsilon}\) 倍(\(\epsilon\) 是计算机的浮点精度)。因此,在实际应用中,我们通常将用户提供的参数 \(tol\) 设置为不小于机器浮点精度的平方根。 为了选择新的点 \(x\),我们希望最小化最坏情况下的可能性。通过一系列的推导,我们发现最优的选择是让 \(x\) 位于较大区间中,且与 \(b\) 的距离满足黄金分割比例(约为 0.38197)。这种方法被称为黄金分割搜索,它保证每次新的函数评估都会将最小值包围在一个是前一个区间 0.61803 倍大小的区间内。 以下是初始包围最小值的代码: ```c #include <math.h> #include "nrutil.h" #define GOLD 1.618034 #define GLIMIT 100.0 #define TINY 1.0e-20 #define SHFT(a,b,c,d) (a)=(b);(b)=(c);(c)=(d); void mnbrak(float *ax, float *bx, float *cx, float *fa, float *fb, float *fc, float (*func)(float)) { float ulim,u,r,q,fu,dum; *fa=(*func)(*ax); *fb=(*func)(*bx); if (*fb > *fa) { SHFT(dum,*ax,*bx,dum) SHFT(dum,*fb,*fa,dum) } *cx=(*bx)+GOLD*(*bx-*ax); *fc=(*func)(*cx); while (*fb > *fc) { r=(*bx-*ax)*(*fb-*fc); q=(*bx-*cx)*(*fb-*fa); u=(*bx)-((*bx-*cx)*q-(*bx-*ax)*r)/ (2.0*SIGN(FMAX(fabs(q-r),TINY),q-r)); ulim=(*bx)+GLIMIT*(*cx-*bx); if ((*bx-u)*(u-*cx) > 0.0) { fu=(*func)(u); if (fu < *fc) { *ax=(*bx); *bx=u; *fa=(*fb); *fb=fu; return; } else if (fu > *fb) { *cx=u; *fc=fu; return; } u=(*cx)+GOLD*(*cx-*bx); fu=(*func)(u); } else if ((*cx-u)*(u-ulim) > 0.0) { fu=(*func)(u); if (fu < *fc) { SHFT(*bx,*cx,u,*cx+GOLD*(*cx-*bx)) SHFT(*fb,*fc,fu,(*func)(u)) } } else if ((u-ulim)*(ulim-*cx) >= 0.0) { u=ulim; fu=(*func)(u); } else { u=(*cx)+GOLD*(*cx-*bx); fu=(*func)(u); } SHFT(*ax,*bx,*cx,u) SHFT(*fa,*fb,*fc,fu) } } ``` 以下是黄金分割搜索的代码: ```c #include <math.h> #define R 0.61803399 #define C (1.0-R) #define SHFT2(a,b,c) (a)=(b);(b)=(c); #define SHFT3(a,b,c,d) (a)=(b);(b)=(c);(c)=(d); float golden(float ax, float bx, float cx, float (*f)(float), float tol, float *xmin) { float f1,f2,x0,x1,x2,x3; x0=ax; x3=cx; if (fabs(cx-bx) > fabs(bx-ax)) { x1=bx; x2=bx+C*(cx-bx); } else { x2=bx; x1=bx-C*(bx-ax); } f1=(*f)(x1); f2=(*f)(x2); while (fabs(x3-x0) > tol*(fabs(x1)+fabs(x2))) { if (f2 < f1) { SHFT3(x0,x1,x2,R*x1+C*x3) SHFT2(f1,f2,(*f)(x2)) } else { SHFT3(x3,x2,x1,R*x2+C*x0) SHFT2(f2,f1,(*f)(x1)) } } if (f1 < f2) { *xmin=x1; return f1; } else { *xmin=x2; return f2; } } ``` #### 3. 一维抛物线插值与布伦特方法 黄金分割搜索主要是为了应对函数最小化问题中最不利的情况。然而,如果函数在最小值附近呈现出良好的抛物线形状,那么通过三个点拟合的抛物线应该能够让我们一步到位,或者至少非常接近最小值。这种通过抛物线拟合来寻找最小值横坐标的方法被称为逆抛物线插值。 逆抛物线插值的公式为: \[x = b - \frac{1}{2} \frac{(b - a)^2[f(b) - f(c)] - (b - c)^2[f(b) - f(a)]}{(b - a)[f(b) - f(c)] - (b - c)[f(b) - f(a)]}\] 不过,这个公式在三个点共线时会失效,而且它既可能跳到抛物线的最大值,也可能跳到最小值,因此单纯依赖这个公式的最小化方案在实际中很难成功。 布伦特方法巧妙地解决了这个问题。在任何特定阶段,它会跟踪六个函数点 \(a\)、\(b\)、\(u\)、\(v\)、\(w\) 和 \(x\)。其中,最小值被 \(a\) 和 \(b\) 包围,\(x\) 是到目前为止函数值最小的点,\(w\) 是函数值第二小的点,\(v\) 是 \(w\) 的前一个值,\(u\) 是最近一次评估函数的点。 布伦特方法会尝试进行抛物线插值,通过 \(x\)、\(v\) 和 \(w\) 这三个点拟合抛物线。为了使抛物线步骤可接受,它必须满足两个条件:一是落在包围区间 \((a, b)\) 内;二是从当前最佳值 \(x\) 移动的距离小于上一步移动距离的一半。在最坏的情况下,该方法会在抛物线步骤和黄金分割步骤之间交替进行,最终通过黄金分割步骤收敛。 以下是布伦特方法的代码: ```c #include <math.h> #include "nrutil.h" #define ITMAX 100 #define CGOLD 0.3819660 #define ZEPS 1.0e-10 #define SHFT(a,b,c,d) (a)=(b);(b)=(c);(c)=(d); float brent(float ax, float bx, float cx, float (*f)(float), float tol, float *xmin) { int iter; float a,b,d,etemp,fu,fv,fw,fx,p,q,r,tol1,tol2,u,v,w,x,xm; float e=0.0; a=(ax < cx ? ax : cx); b=(ax > cx ? ax : cx); x=w=v=bx; fw=fv=fx=(*f)(x); for (iter=1;iter<=ITMAX;iter++) { xm=0.5*(a+b); tol2=2.0*(tol1=tol*fabs(x)+ZEPS); if (fabs(x-xm) <= (tol2-0.5*(b-a))) { *xmin=x; return fx; } if (fabs(e) > tol1) { r=(x-w)*(fx-fv); q=(x-v)*(fx-fw); p=(x-v)*q-(x-w)*r; q=2.0*(q-r); if (q > 0.0) p = -p; q=fabs(q); etemp=e; e=d; if (fabs(p) >= fabs(0.5*q*etemp) || p <= q*(a-x) || p >= q*(b-x)) d=CGOLD*(e=(x >= xm ? a-x : b-x)); else { d=p/q; u=x+d; if (u-a < tol2 || b-u < tol2) d=SIGN(tol1,xm-x); } } else { d=CGOLD*(e=(x >= xm ? a-x : b-x)); } u=(fabs(d) >= tol1 ? x+d : x+SIGN(tol1,d)); fu=(*f)(u); if (fu <= fx) { if (u >= x) a=x; else b=x; SHFT(v,w,x,u) SHFT(fv,fw,fx,fu) } else { if (u < x) a=u; else b=u; if (fu <= fw || w == x) { v=w; w=u; fv=fw; fw=fu; } else if (fu <= fv || v == x || v == w) { v=u; fv=fu; } } } nrerror("Too many iterations in brent"); *xmin=x; return fx; } ``` #### 4. 带一阶导数的一维搜索 在这部分,我们的目标与前面相同,即找出被三个横坐标 \((a, b, c)\) 包围的函数最小值,但我们可以利用函数的一阶导数信息。 我们不能简单地通过寻找导数的零点来确定最小值,因为这样无法区分最大值和最小值,也无法处理初始条件下导数指示“下坡”方向在包围区间之外的情况。因此,导数的作用只能是帮助我们在包围区间内选择新的试验点。 一种保守的方法是根据包围三元组中心点处导数的符号,确定下一个试验点应在区间 \((a, b)\) 还是 \((b, c)\) 内。然后,通过割线法(逆线性插值)将该点处的导数和第二好点处的导数外推到零。同时,我们会对这个新的试验点施加与布伦特方法类似的限制,如果试验点不符合要求,我们会对当前考察的区间进行二分。 以下是带一阶导数的一维搜索代码: ```c #include <math.h> #include "nrutil.h" #define ITMAX 100 #define ZEPS 1.0e-10 #define MOV3(a,b,c, d,e,f) (a)=(d);(b)=(e);(c)=(f); float dbrent(float ax, float bx, float cx, float (*f)(float), float (*df)(float), float tol, float *xmin) { int iter,ok1,ok2; float a,b,d,d1,d2,du,dv,dw,dx,e=0.0; float fu,fv,fw,fx,olde,tol1,tol2,u,u1,u2,v,w,x,xm; a=(ax < cx ? ax : cx); b=(ax > cx ? ax : cx); x=w=v=bx; fw=fv=fx=(*f)(x); dw=dv=dx=(*df)(x); for (iter=1;iter<=ITMAX;iter++) { xm=0.5*(a+b); tol1=tol*fabs(x)+ZEPS; tol2=2.0*tol1; if (fabs(x-xm) <= (tol2-0.5*(b-a))) { *xmin=x; return fx; } if (fabs(e) > tol1) { d1=2.0*(b-a); d2=d1; if (dw != dx) d1=(w-x)*dx/(dx-dw); if (dv != dx) d2=(v-x)*dx/(dx-dv); u1=x+d1; u2=x+d2; ok1 = (a-u1)*(u1-b) > 0.0 && dx*d1 <= 0.0; ok2 = (a-u2)*(u2-b) > 0.0 && dx*d2 <= 0.0; olde=e; e=d; if (ok1 || ok2) { if (ok1 && ok2) d=(fabs(d1) < fabs(d2) ? d1 : d2); else if (ok1) d=d1; else d=d2; if (fabs(d) <= fabs(0.5*olde)) { u=x+d; if (u-a < tol2 || b-u < tol2) d=SIGN(tol1,xm-x); } else { d=0.5*(e=(dx >= 0.0 ? a-x : b-x)); } } else { d=0.5*(e=(dx >= 0.0 ? a-x : b-x)); } } else { d=0.5*(e=(dx >= 0.0 ? a-x : b-x)); } if (fabs(d) >= tol1) { u=x+d; fu=(*f)(u); } else { u=x+SIGN(tol1,d); fu=(*f)(u); if (fu > fx) { *xmin=x; return fx; } } du=(*df)(u); if (fu <= fx) { if (u >= x) a=x; else b=x; MOV3(v,fv,dv, w,fw,dw) MOV3(w,fw,dw, x,fx,dx) MOV3(x,fx,dx, u,fu,du) } else { if (u < x) a=u; else b=u; if (fu <= fw || w == x) { MOV3(v,fv,dv, w,fw,dw) MOV3(w,fw,dw, u,fu,du) } else if (fu < fv || v == x || v == w) { MOV3(v,fv,dv, u,fu,du) } } } nrerror("Too many iterations in routine dbrent"); return 0.0; } ``` #### 5. 多维下山单纯形法 从这部分开始,我们将考虑多维函数的最小化问题。下山单纯形法由 Nelder 和 Mead 提出,该方法只需要函数评估,不需要导数信息。虽然它在函数评估次数方面效率不高,但在计算负担较小的问题中,它可能是快速解决问题的最佳方法。 在 \(N\) 维空间中,单纯形是由 \(N + 1\) 个点(或顶点)及其所有相互连接的线段、多边形面等组成的几何图形。例如,在二维空间中,单纯形是一个三角形;在三维空间中,它是一个四面体。 下山单纯形法需要从 \(N + 1\) 个点开始,定义一个初始单纯形。通常,我们可以选择一个初始点 \(P_0\),然后将其他 \(N\) 个点定义为 \(P_i = P_0 + \lambda e_i\),其中 \(e_i\) 是 \(N\) 个单位向量,\(\lambda\) 是我们对问题特征长度尺度的猜测。 该方法通过一系列步骤来寻找最小值。大多数步骤是将单纯形中函数值最大的点(“最高点”)通过对面移动到一个较低的点,这种操作称为反射。当条件允许时,方法会在一个或多个方向上扩展单纯形以采取更大的步骤;当到达“谷底”时,方法会在横向方向上收缩并尝试沿着山谷向下移动;如果单纯形试图“穿过针眼”,它会在所有方向上收缩,围绕其最低点(最佳点)收紧。 以下是下山单纯形法的代码: ```c #include <math.h> #include "nrutil.h" #define TINY 1.0e-10 #define NMAX 5000 #define GET_PSUM \ for (j=1;j<=ndim;j++) {\ for (sum=0.0,i=1;i<=mpts;i++) sum += p[i][j];\ psum[j]=sum;} #define SWAP(a,b) {swap=(a);(a)=(b);(b)=swap;} void amoeba(float **p, float y[], int ndim, float ftol, float (*funk)(float []), int *nfunk) { float amotry(float **p, float y[], float psum[], int ndim, float (*funk)(float []), i ```
corwn 最低0.47元/天 解锁专栏
赠100次下载
继续阅读 点击查看下一篇
profit 400次 会员资源下载次数
profit 300万+ 优质博客文章
profit 1000万+ 优质下载资源
profit 1000万+ 优质文库回答
复制全文

相关推荐

李_涛

知名公司架构师
拥有多年在大型科技公司的工作经验,曾在多个大厂担任技术主管和架构师一职。擅长设计和开发高效稳定的后端系统,熟练掌握多种后端开发语言和框架,包括Java、Python、Spring、Django等。精通关系型数据库和NoSQL数据库的设计和优化,能够有效地处理海量数据和复杂查询。
最低0.47元/天 解锁专栏
赠100次下载
百万级 高质量VIP文章无限畅学
千万级 优质资源任意下载
千万级 优质文库回答免费看
立即解锁

专栏目录

最新推荐

英语学习工具开发总结:C#实现功能与性能的平衡

# 摘要 本文探讨了C#在英语学习工具中的应用,首先介绍了C#的基本概念及在英语学习工具中的作用。随后,详细分析了C#的核心特性,包括面向对象编程和基础类型系统,并探讨了开发环境的搭建,如Visual Studio的配置和.NET框架的安装。在关键技术部分,本文着重论述了用户界面设计、语言学习模块的开发以及多媒体交互设计。性能优化方面,文章分析了性能瓶颈并提出了相应的解决策略,同时分享了实际案例分析。最后,对英语学习工具市场进行了未来展望,包括市场趋势、云计算和人工智能技术在英语学习工具中的应用和创新方向。 # 关键字 C#;英语学习工具;面向对象编程;用户界面设计;性能优化;人工智能技术

【STM32f107vc TCP_IP实战】:构建高效稳定的TCP_IP通信环境

![【STM32f107vc TCP_IP实战】:构建高效稳定的TCP_IP通信环境](https://learn.microsoft.com/en-us/troubleshoot/azure/azure-storage/blobs/alerts/media/storage-monitoring-diagnosing-troubleshooting/wireshark-expert-information.png) # 摘要 随着物联网和嵌入式系统的不断发展,STM32F107VC微控制器在实现网络通信方面变得越来越重要。本文旨在探讨STM32F107VC与TCP/IP协议栈的集成与配置,从

【管理策略探讨】:掌握ISO 8608标准在路面不平度控制中的关键

![【管理策略探讨】:掌握ISO 8608标准在路面不平度控制中的关键](https://assets.isu.pub/document-structure/221120190714-fc57240e57aae44b8ba910280e02df35/v1/a6d0e4888ce5e1ea00b7cdc2d1b3d5bf.jpeg) # 摘要 本文全面概述了ISO 8608标准及其在路面不平度测量与管理中的重要性。通过深入讨论路面不平度的定义、分类、测量技术以及数据处理方法,本文强调了该标准在确保路面质量控制和提高车辆行驶安全性方面的作用。文章还分析了ISO 8608标准在路面设计、养护和管理

Shopee上架工具性能革命:代码层面的极致优化技巧

![shopee上架工具.rar](https://down-sg.img.susercontent.com/sg-11134141-7rcce-ltp1o6dtz7hs86) # 摘要 在电子商务平台,如Shopee,上架工具的性能直接关系到用户体验与平台效率。随着商品数量和交易量的增加,性能挑战日益凸显,对工具进行持续的优化显得至关重要。本文首先分析了性能优化的理论基础,包括性能优化的目标、性能瓶颈的定位,以及代码优化的基本原则。接着,文章通过具体实例详细探讨了Shopee上架工具在代码层面,如数据结构和算法的优化,以及系统层面的优化,包括I/O操作、内存管理和硬件利用。此外,本文还强调

【Swing资源管理】:避免内存泄漏的实用技巧

![【Swing资源管理】:避免内存泄漏的实用技巧](https://opengraph.githubassets.com/a6710ff2c86c331c13363554d00aab3dd898536c00e1344fa99ef3cd2923e717/daggerok/findbugs-example) # 摘要 Swing资源管理对于提高Java桌面应用程序的性能和稳定性至关重要。本文首先阐述了Swing资源管理的重要性,紧接着深入探讨了内存泄漏的成因和原理,包括组件和事件模型以及不恰当的事件监听器和长期引用所导致的问题。本文还对JVM的垃圾回收机制进行了概述,介绍了Swing内存泄漏检

FRET实验的高通量分析:自动化处理与高精度数据解读的十个技巧

![FRET实验的高通量分析:自动化处理与高精度数据解读的十个技巧](https://www.bmglabtech.com/hubfs/1_Webseite/5_Resources/Blogs/kinase-assays-fig4.webp) # 摘要 FRET( Förster共振能量转移)实验是生物物理和生物化学研究中一种广泛应用的技术,尤其在高通量分析中具有重要地位。本文从FRET实验的背景讲起,详细探讨了高通量自动化处理技巧、高精度数据解读的理论与实践,以及高级自动化与数据分析方法。文中分析了高通量实验设计、自动化工具的应用、数据采集和管理,以及解读数据分析的关键技术。进阶内容包括机

ESP8266小电视性能测试与调优秘籍:稳定运行的关键步骤(专家版)

![ESP8266小电视性能测试与调优秘籍:稳定运行的关键步骤(专家版)](https://www.espboards.dev/img/lFyodylsbP-900.png) # 摘要 本文全面探讨了ESP8266小电视的基本概念、原理、性能测试、问题诊断与解决以及性能调优技巧。首先,介绍了ESP8266小电视的基本概念和工作原理,随后阐述了性能测试的理论基础和实际测试方法,包括测试环境的搭建和性能测试结果的分析。文章第三章重点描述了性能问题的诊断方法和常见问题的解决策略,包括内存泄漏和网络延迟的优化。在第四章中,详细讨论了性能调优的理论和实践,包括软件和硬件优化技巧。最后,第五章着重探讨了

SSD加密技术:确保数据安全的关键实现

![固态硬盘SSD原理详细介绍,固态硬盘原理详解,C,C++源码.zip](https://pansci.asia/wp-content/uploads/2022/11/%E5%9C%96%E8%A7%A3%E5%8D%8A%E5%B0%8E%E9%AB%94%EF%BC%9A%E5%BE%9E%E8%A8%AD%E8%A8%88%E3%80%81%E8%A3%BD%E7%A8%8B%E3%80%81%E6%87%89%E7%94%A8%E4%B8%80%E7%AA%BA%E7%94%A2%E6%A5%AD%E7%8F%BE%E6%B3%81%E8%88%87%E5%B1%95%E6%9C%9

【OGG跨平台数据同步】:Oracle 11g环境下的跨平台同步绝技

# 摘要 本文详细介绍了跨平台数据同步技术,并以Oracle GoldenGate(OGG)为例进行深入探讨。首先,概述了Oracle 11g下的数据同步基础,包括数据同步的定义、重要性以及Oracle 11g支持的数据同步类型。随后,介绍了Oracle 11g的数据复制技术,并详细分析了OGG的软件架构和核心组件。在实战演练章节,文章指导读者完成单向和双向数据同步的配置与实施,并提供了常见问题的故障排除方法。最后,重点讨论了OGG同步性能优化策略、日常管理与监控,以及在不同平台应用的案例研究,旨在提升数据同步效率,确保数据一致性及系统的稳定性。 # 关键字 数据同步;Oracle Gold

【智能调度系统的构建】:基于矢量数据的地铁调度优化方案,效率提升50%

# 摘要 随着城市地铁系统的迅速发展,智能调度系统成为提升地铁运营效率与安全的关键技术。本文首先概述了智能调度系统的概念及其在地铁调度中的重要性。随后,文章深入探讨了矢量数据在地铁调度中的应用及其挑战,并回顾了传统调度算法,同时提出矢量数据驱动下的调度算法创新。在方法论章节中,本文讨论了数据收集、处理、调度算法设计与实现以及模拟测试与验证的方法。在实践应用部分,文章分析了智能调度系统的部署、运行和优化案例,并探讨了系统面临的挑战与应对策略。最后,本文展望了人工智能、大数据技术与边缘计算在智能调度系统中的应用前景,并对未来研究方向进行了展望。 # 关键字 智能调度系统;矢量数据;调度算法;数据