数据结构:泰勒展开式:霍纳法则(Horner‘s Rule)

目录

🔍 若用递归计算每一项,会发生什么?

Horner's Rule(霍纳法则)

 第一步:我们从最原始的泰勒公式出发

第二步:从形式上重新观察展开式 

🌟 第三步:引出霍纳法则(Horner’s Rule)

 第四步:如何用这个结构重写泰勒展开式? 

完整代码

 从迭代转换成递归逻辑

“迭代”和“循环” 


当前递归方法的结构回顾:

double num = 1, den = 1;double taylor(int n, double x) {if (n == 0) return 1;double res = taylor(n - 1, x);  // 一次函数调用num *= x;                       // 分子:一次乘法den *= n;                       // 分母:一次乘法return res + num / den;
}

这一版已经做了初步优化:我们通过 累计 num 和 den 来避免重复调用 pow(x,n)factorial(n)

但这只是相对优化,我们现在要分析:

🔍 若用递归计算每一项,会发生什么?

我们从第 0 项到第 n 项共计算 n+1项,每一项的乘法成本如下:

第 k 项乘法次数
k = 00
k = 11
k = 22
......
k = nn

所以总乘法次数为:

✅ 因此,乘法总次数为 O(n^2)!

🚨 问题在哪里?

  • 你对每一项都重新计算幂和阶乘

  • 导致重复计算,浪费大量 CPU 时间

  • 如果你希望 n = 50n = 100,程序变慢得很明显

🤔 有没有更好的方式?

是的。你就引出了今天的主角:

Horner's Rule(霍纳法则)

我们可以尝试换一种展开方式,让我们不必每次都分别去计算幂和阶乘。

我们将展开式进行重写:

也就是一种嵌套式计算结构。

这个就是 Horner 法则的思路 —— 逐层乘进去、逐层加出来,避免重复乘法和幂展开。


 第一步:我们从最原始的泰勒公式出发

以 e^x 为例,泰勒展开是:

 

这表达得很清晰,每一项结构都类似:

  • 分子是 x^k

  • 分母是 k!

所以直觉上,我们就写了:

double num = 1;  // 分子
double den = 1;  // 分母double taylor(int n, double x) {if (n == 0) return 1;               // 1️⃣ 锚点:停止递归double res = taylor(n - 1, x);      // 2️⃣ 先构造前面所有项的和num *= x;                           // 3️⃣ 然后再更新状态den *= n;return res + num / den;             // 4️⃣ 当前项加进去
}

 递归方法的思路解析,可以看我之前发表的文章:

数据结构:递归:泰勒展开式(Taylor Series Expansion)-CSDN博客

 但是整个算法需要 O(n^2) 次的乘法。

于是我们问自己:

❓有没有一种办法,我们不显式地计算幂和阶乘,而是用一种更聪明的方式重写它?

第二步:从形式上重新观察展开式 

我们把:

我们尝试反向收敛:

从最后一项往前看。 

设:

假设我们已知最后一项是:

我们问:有没有可能构造出一个结构:

 

我们知道这种结构是逐层“乘进去再加”,是一种“嵌套结构”。

这时候,你会自然联想到:


🌟 第三步:引出霍纳法则(Horner’s Rule)

Horner's Rule 是一种重写多项式的方式,使其变成嵌套乘加结构,从而节省乘法次数。

给你一个多项式:

它可以等价重写成:

 

第一步:从最后一项开始反向思考 

先写出最后一项:

但我们不这么直接展开,而是尝试合并每一项,构建嵌套结构。我们回顾一下: 

第二步:尝试因式提取,构造嵌套结构 

我们从最后一项往回包,先只保留最后一项:

向前一项加:

 再加:

 最后加 1 项:

第三步:得出结论(形式)

最终你得到的就是:

 

这就是 Horner 法则在泰勒展开中的精确结构! 

 


 第四步:如何用这个结构重写泰勒展开式? 

霍纳法则告诉我们:

如果你有一个嵌套表达式,它从最深处开始乘加,就可以从最后一项反向累积计算

我们的目标是以某种结构计算它,让乘法次数最少。 

观察这个嵌套结构你会发现:

  • 每一层都包含一个 “1 + x / k * (之前的结果)”

  • 最里面的是 1

  • 然后不断被 x/k 包裹

它是一个“逐层包裹”的结构,每一层是:

 这说明我们可以从“最深的那一层”开始往外展开。

于是你意识到这就是一种“右折叠(right fold)”,即:

result = 1;
result = 1 + x * result / 4;
result = 1 + x * result / 3;
result = 1 + x * result / 2;
result = 1 + x * result / 1;

 从后往前包,每次乘进去一个 x / i,再加 1。

所谓「右折叠」就是我们从表达式的最右边开始构建,逐层包起来。 

1 + x/4              ← 第 1 层(最里面)
1 + x/3 * (1 + x/4)  ← 第 2 层
1 + x/2 * (...)      ← 第 3 层
1 + x/1 * (...)      ← 第 4 层

你看到一种非常明显的重复:

每次的操作是:

result = 1 + x * result / i;

从哪个 i 开始?

  • 最深一层是对应最大项 n

  • 然后是 n - 1

  • 最后是 i = 1

所以你会写一个反向的循环:

for (int i = n; i > 0; --i)

初始值设置为:

double result = 1.0;  // 最里层的恒定值

为什么是 1.0?

因为你可以认为最内层就是 1 + 0,也就是我们从最后一项 x^n / n! 折叠得到的值是最深的那个 1,逐层向外构建。

完整代码

double horner_taylor(int n, double x) {double result = 1.0;                  // 最深嵌套项for (; n > 0; n--) {                 // 从内往外迭代result = 1 + x * result / n;     // 每次构造一层}return result;
}

 从迭代转换成递归逻辑

递归的本质是:

用一个函数在每一层调用自己,把循环变成函数调用链。

从上面的迭代式:

你可以直接转换成递归表达式:

double horner_recursive(int n, double x) {static double result = 1.0;if (n == 0) return result;       // 最深嵌套项(base case)result = 1 + x / n * result;  return horner_recursive(n - 1, x);   
}

循环版结构递归版结构
从 n 逐步降到 1从 n 递归到 0(递归终止条件)
每次更新 result = ...每次返回 1 + x * 下层 / n
初始 result = 1.0horner_recursive(0, x) = 1.0

两者实际上是完全等价的计算结构。


“迭代”“循环” 

什么是“循环”(loop)?

  • 循环是语法结构

  • 是编程语言提供的控制流语句(forwhiledo-while

  • 它的作用是:重复执行某段代码

比如:

for (int i = 0; i < 10; ++i) {// 执行 10 次
}

什么是“迭代”(iteration)?

  • 迭代是算法行为

  • 意思是:基于前一个结果,不断构造下一个结果

  • 它不依赖一定要用循环语法,也可以用递归实现!

举例说明:

✅ 迭代行为 + 循环实现

double result = 1;
for (int i = n; i > 0; --i)result = 1 + x * result / i;
  • 每一轮基于上一轮的 result

  • 所以这是一个迭代算法

  • 同时用了 for,所以也是一个循环结构

 ✅ 迭代行为 + 递归实现

double horner_recursive(int n, double x) {static double result = 1.0;if (n == 0) return result;       // 最深嵌套项(base case)result = 1 + x / n * result;  return horner_recursive(n - 1, x);   
}
  • 每一层基于“下一层”的结果

  • 所以也是一种迭代算法

  • 但这次用的是递归结构

 🚫 循环 ≠ 迭代(反例)

for (int i = 0; i < 10; ++i) {cout << "Hello\n";
}
  • 这使用了循环,但没有迭代行为(没有前后状态依赖)

  • 所以它是循环,但不是迭代

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.pswp.cn/diannao/85876.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

从Java的Jvm的角度解释一下为什么String不可变?

从Java的Jvm的角度解释一下为什么String不可变&#xff1f; 从 JVM 的角度看&#xff0c;Java 中 String 的不可变性是由多层次的机制共同保障的&#xff0c;这些设计涉及内存管理、性能优化和安全保障&#xff1a; 1. JVM 内存模型与字符串常量池 字符串常量池&#xff08;St…

初识硬编码(x86指令描述)

硬编码 任何一个程序其实都可以看做两部分组成的&#xff0c;指令和数据 cpu并没有明确的规定哪些要当做数据&#xff0c;哪些要当做指令来执行&#xff0c;把数据给EIP只要是遵循了指定的格式&#xff08;x86 x64 ARM&#xff09;&#xff0c;cpu都会当做指令来执行 x86/x64…

3.RV1126-OPENCV 图像叠加

一.功能介绍 图像叠加&#xff1a;就是在一张图片上放上自己想要的图片&#xff0c;如LOGO&#xff0c;时间等。有点像之前提到的OSD原理一样。例如&#xff1a;下图一张图片&#xff0c;在左上角增加其他图片。 二.OPENCV中图像叠加常用的API 1. copyTo方法进行图像叠加 原理…

MySQL垂直分库(基于MyCat)

参考资料&#xff1a; 参考视频 参考博客 Mycat基本部署 视频参考资料&#xff1a;链接: https://pan.baidu.com/s/1xT_WokN_xlRv0h06b6F3yg 提取码: aag3 概要&#xff1a; 本文的垂直分库&#xff0c;全部是基于前文部署的基本架构进行的 垂直分库&#xff1a; 垂直分库…

Spitfire:Codigger 生态中的高性能、安全、分布式浏览器

Spitfire 是 Codigger 生态系统中的一款现代化浏览器&#xff0c;专为追求高效、隐私和分布式技术的用户设计。它结合了 Codigger 的分布式架构优势&#xff0c;在速度、安全性和开发者支持方面提供了独特的解决方案&#xff0c;同时确保用户对数据的完全控制。 1. 高性能浏览…

1-【源码剖析】kafka核心概念

从今天开始开始在csdn上记录学习的笔记&#xff0c;主要包括以下几个方面&#xff1a; kafkaflinkdoris 本系列笔记主要记录Kafka学习相关的内容。在进行kafka源码学习之前&#xff0c;先介绍一下Kafka的核心概念。 消息 消息是kafka中最基本的数据单元&#xff0c;由key和…

互联网大厂Java求职面试:云原生架构下的微服务网关与可观测性设计

互联网大厂Java求职面试&#xff1a;云原生架构下的微服务网关与可观测性设计 郑薪苦怀着忐忑的心情走进了会议室&#xff0c;对面坐着的是某大厂的技术总监张总&#xff0c;一位在云原生领域有着深厚积累的专家。 第一轮面试&#xff1a;微服务网关的设计挑战 张总&#xf…

【HarmonyOS 5】针对 Harmony-Cordova 性能优化,涵盖原生插件开发、线程管理和资源加载等关键场景

1. ‌原生图片处理插件&#xff08;Java&#xff09; package com.example.plugin; import ohos.media.image.ImageSource; import ohos.media.image.PixelMap; import ohos.app.Context; public class ImageProcessor { private final Context context; public ImagePro…

Java-IO流之缓冲流详解

Java-IO流之缓冲流详解 一、缓冲流概述1.1 什么是缓冲流1.2 缓冲流的工作原理1.3 缓冲流的优势 二、字节缓冲流详解2.1 BufferedInputStream2.1.1 构造函数2.1.2 核心方法2.1.3 使用示例 2.2 BufferedOutputStream2.2.1 构造函数2.2.2 核心方法2.2.3 使用示例 三、字符缓冲流详…

健康检查:在 .NET 微服务模板中优雅配置 Health Checks

&#x1f680; 健康检查&#xff1a;在 .NET 微服务模板中优雅配置 Health Checks &#x1f4da; 目录 &#x1f680; 健康检查&#xff1a;在 .NET 微服务模板中优雅配置 Health Checks一、背景与意义 &#x1f50d;二、核心配置 &#x1f527;2.1 引入必要的 NuGet 依赖 &…

关于akka官方quickstart示例程序(scala)的记录

参考资料 https://doc.akka.io/libraries/akka-core/current/typed/actors.html#first-example 关于scala语法的注意事项 extends App是个语法糖&#xff0c;等同于直接在伴生对象中编写main 方法对象是通过apply方法创建的&#xff0c;也可以通过对象的名称单独创建&#x…

基于vue3-elemenyui的页面加载及新建浏览页案例

1.参考链接&#xff1a; 基于创建vue3链接&#xff1a;Vue3前端项目创建_vscode创建vue3项目-CSDN博客 基于创建elementui链接&#xff1a;Vue3引入ElementPlus_vue引入element-plus-CSDN博客 2.案例内容 该案例实现了基本的app.vue的路由跳转、新建浏览页参数传入以及浏览…

板凳-------Mysql cookbook学习 (十)

5.6 改变字符串的字符集或字符排序 mysql> set s1 my string; Query OK, 0 rows affected (0.01 sec)mysql> set s2 convert(s1 using utf8); Query OK, 0 rows affected, 1 warning (0.00 sec)mysql> select charset(s1), charset(s2); -------------------------…

使用nginx配置反向代理,负载均衡

首先啥叫反向代理 咋配置呢&#xff0c;那当然是在nginx目录下改conf文件了 具体咋改呢&#xff0c;那就新增一个新的server配置&#xff0c;然后在location里新增你想代理的服务器 实际上负载均衡也就是根据反向代理的思路来的&#xff0c;如下所示 配置的话实际上也与上…

嵌入式开发之STM32学习笔记day20

STM32F103C8T6 PWR电源控制 1 PWR简介 PWR&#xff08;Power Control&#xff09;电源控制单元是STM32微控制器中一个重要的组成部分&#xff0c;它负责管理系统的电源管理功能&#xff0c;以优化功耗并提高效率。PWR负责管理STM32内部的电源供电部分&#xff0c;可以实现可编…

Spring AI(10)——STUDIO传输的MCP服务端

Spring AI MCP&#xff08;模型上下文协议&#xff09;服务器Starters提供了在 Spring Boot 应用程序中设置 MCP 服务器的自动配置。它支持将 MCP 服务器功能与 Spring Boot 的自动配置系统无缝集成。 本文主要演示支持STDIO传输的MCP服务器 仅支持STDIO传输的MCP服务器 导入j…

Java八股文——集合「Set篇」

Set集合有什么特点&#xff1f;如何实现key无重复的&#xff1f; 面试官您好&#xff0c;Set 集合是 Java 集合框架中的一个重要接口&#xff0c;它继承自 Collection 接口&#xff0c;其最显著的特点和设计目标就是存储一组不重复的元素。 一、Set集合的主要特点&#xff1a…

「数据分析 - NumPy 函数与方法全集」【数据分析全栈攻略:爬虫+处理+可视化+报告】

- 第 104 篇 - Date: 2025 - 06 - 05 Author: 郑龙浩/仟墨 NumPy 函数与方法全集 文章目录 NumPy 函数与方法全集1. 数组创建与初始化基础创建序列生成特殊数组 2. 数组操作形状操作合并与分割 3. 数学运算基础运算统计运算 4. 随机数生成基础随机分布函数 5. 文件IO文件读写 …

报表/报告组件(二)-实例与实现解释

上篇《报表/报告组件(一)-指标/属性组件设计》介绍了组件核心指标/属性设计&#xff0c;本文以实例介绍各个特性的实现和效果&#xff0c;实例是多个报告融合&#xff0c;显示所有的特性。 设计 指标/属性组件是报告/报表关键部分&#xff0c;上篇已介绍过&#xff0c;本节回顾…

Flutter嵌入式开发实战 ——从树莓派到智能家居控制面板,打造工业级交互终端

一、为何选择Flutter开发嵌入式设备&#xff1f; 1. 跨平台能力降维打击 特性传统方案Flutter方案开发效率需分别开发Android/Linux一套代码多端部署内存占用200MB (QtWeb引擎)<80MB (Release模式)热重载支持不支持支持 2. 工业级硬件支持实测 树莓派4B&#xff1a;1080…