數據結構:泰勒展開式:霍納法則(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
繁體地址,請注明出處:http://hk.pswp.cn/diannao/85876.shtml
英文地址,請注明出處:http://en.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…