《編程原本 》一3.3 程序變換

3.3 程序變換

power0是有關算法的一個令人滿意的實現,它適用于運算的代價高于函數遞歸調用開銷的情況.本節要推導出一個迭代算法,它執行運算的次數和power0一樣.這里將要做一系列程序變換,這些變換也可以用在其他許多情況中.5 在本書后面的部分,通常將只給出算法的最終版本或幾乎最終版本.
power0包含兩個相同的遞歸調用,它每次只執行其中一個.這使我們可能通過公共子表達式刪除技術來縮小代碼的規模:

template<typename I, typename Op> requires(Integer(I) && BinaryOperation(Op)) Domain(Op) power 1(Domain(Op) a, I n, Op op) 
{ 
//前條件:associative(op)∧n>0
if (n == I(1)) return a;Domain(Op) r = power 1(op(a, a), n / I(2), op);if (n % I(2) != I(0)) r = op(r, a);return r;
}

現在的目標是刪除遞歸調用,為此要做的第一步是把過程變換到尾遞歸形式(tail-recursiveform),其中在過程執行的最后一步是對自身的遞歸調用.完成該變換的一種技術是引入累積變量(accumulation-variableintroduction),用于在不同遞歸調用之間攜帶累積的結果:

template<typename I, typename Op> requires(Integer(I) && BinaryOperation(Op)) Domain(Op) power accumulate 0(Domain(Op) r, Domain(Op) a, I n, Op op) 
{ 
//前條件:associative(op)∧n.0
if (n == I(0)) return r;

5.只有在運算的語義和復雜性已知的情況下,編譯器才會對一些內部類型做類似變換.規范性概念是類型創建者的一個斷言,它保證程序員和編譯器可以安全地執行這些變換.

if (n % I(2) != I(0)) r = op(r, a); 
return power accumulate 0(r, op(a, a), n / I(2), op); }

設r0,a0和n0是r,a和n的原值,下面不變式(recursioninvariant)在每次遞歸調用時都成立:ran=r0an0 0 .這個版本還有另一優點,它不僅計算冪,還能計算乘以一個系數的冪.它也處理了指數為0的情況.但是在指數從1變到0時poweraccumulate0將多做一次平方.增加一種情況就可以消除它:

template<typename I, typename Op> requires(Integer(I) && BinaryOperation(Op)) Domain(Op) power accumulate 1(Domain(Op) r, Domain(Op) a, I n, Op op) 
{ 
//前條件:associative(op)∧n.0
if (n == I(0)) return r; 
if (n == I(1)) return op(r, a); 
if (n % I(2) != I(0)) r = op(r, a); 
return power accumulate 1(r, op(a, a), n / I(2), op); }

增加額外情況導致重復出現的子表達式,也使三個檢測不獨立了.通過仔細分析檢測之間的依賴性和順序,考慮它們的出現頻率,可以給出

template<typename I, typename Op> requires(Integer(I) && BinaryOperation(Op)) Domain(Op) power accumulate 2(Domain(Op) r, Domain(Op) a, I n, Op op) 
{ 
//前條件:associative(op)∧n.0
if (n % I(2) != I(0)) { 
r = op(r, a); 
if (n == I(1)) return r; 
} else if (n == I(0)) return r; return power accumulate 2(r, op(a, a), n / I(2), op); 
}

在一個尾遞歸過程里,如果所有遞歸調用中的過程形參都是對應的實參,它就是一個嚴格尾遞歸的(stricttail-recursive)過程:

template<typename I, typename Op> requires(Integer(I) && BinaryOperation(Op)) Domain(Op) power accumulate 3(Domain(Op) r, Domain(Op) a, I n, Op op) 
{ 
//前條件:associative(op)∧n.0
if (n % I(2) != I(0)) { 
r = op(r, a); 
if (n == I(1)) return r; 
} else if (n == I(0)) return r; 
a = op(a, a); 
n = n / I(2); 
return power accumulate 3(r, a, n, op); }

嚴格尾遞歸過程可以變換為一個迭代過程,方法是把每個遞歸調用代換為一個到過程開始的goto,也可以用一個等價的迭代結構:

template<typename I, typename Op> requires(Integer(I) && BinaryOperation(Op)) Domain(Op) power accumulate 4(Domain(Op) r, Domain(Op) a, I n, Op op) 
{ 
//前條件:associative(op)∧n.0
while (true) { 
if (n % I(2) != I(0)) { 
r = op(r, a); 
if (n == I(1)) return r; } else if (n == I(0)) return r;a = op(a, a);n = n / I(2);
} }

遞歸不變式變成了這里的循環不變式(loopinvariant).如果開始時n>0,在變成0前要先經過1.我們借用這種情況消去對0的檢查并加強前條件(strengthening`javascript
precondition):
template requires(Integer(I) && BinaryOperation(Op))
Domain(Op) power accumulate positive 0(Domain(Op) r, Domain(Op) a, I n, Op op)
{
//前條件:associative(op)∧n>0
while (true) {
if (n % I(2) != I(0)) {r = op(r, a);if (n == I(1)) return r;
}
a = op(a, a);n = n / I(2);
} }

知道了n>0會很有用.在開發組件的過程中經常會發現新的接口情況.現在放松前條件(relaxingprecondition):

template requires(Integer(I) && BinaryOperation(Op)) Domain(Op) power accumulate 5(Domain(Op) r, Domain(Op) a, I n, Op op)

{
//前條件:associative(op)∧n.0
if (n == I(0)) return r;return power accumulate positive 0(r, a, n, op);
}
通過一個簡單的等式,就可以用poweraccumulate實現power:
nn.1
a = aa

這一變換就是消去累積變量(accumulation-variableelimination):

template requires(Integer(I) && BinaryOperation(Op)) Domain(Op) power 2(Domain(Op) a, I n, Op op)
{
//前條件:associative(op)∧n>0
return power accumulate 5(a, a, n -I(1), op);
}

這個算法多做了一些不必要的運算.例如,當n是16時它要執行7次運算,其中只有4次是必要的.當n是奇數時這個算法很好.避免上述問題的方法是反復做a的平方,并不斷將指數折半直至它變成奇數:

template requires(Integer(I) && BinaryOperation(Op)) Domain(Op) power 3(Domain(Op) a, I n, Op op)
{
//前條件:associative(op)∧n>0
while (n % I(2) == I(0)) {a = op(a, a);n = n / I(2);
}
n = n / I(2);if (n == I(0)) return a;
return power accumulate positive 0(a, op(a, a), n, op);
}

練習3.1 請自己確認最后三行代碼是正確的. 

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/257848.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/257848.shtml
英文地址,請注明出處:http://en.pswp.cn/news/257848.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Effective C++ .07 virtual析構函數的提供

主要講了&#xff0c; 1. virtual析構函數的作用與調用順序 2. 使用時機&#xff0c;并不是使用了繼承就要把基類的析構函數變為虛函數&#xff08;virtual&#xff09;&#xff0c;只有當用于多態目的時才進行一個virtual析構函數的定義。 3. 不要繼承那些沒有將析構函數定義為…

OnClickListener沖突的問題

OnClickListener沖突的問題 (2011-11-26 15:28:27) 轉載▼標簽&#xff1a; 雜談 分類&#xff1a; android學習記錄 import anfroid.view.View.OnClickListenerimport anfroid.content.DialogInterface.OnClickListener 這兩個東西要同時用的話&#xff0c;要使用以下方式&…

html 響應式 同一行,一行CSS實現各種響應式元素 – Fluidity

一行CSS實現各種響應式元素 – Fluidity3月 31, 2014評論SponsorFLUIDITY是一個極微小的CSS樣式表&#xff0c;壓縮版只有一行代碼&#xff0c;大小只有115個字節&#xff0c;它能實現圖像、文本、Canvas、Table表格以及iFrame框架的響應式功能。好用且實用&#xff01;這個響應…

玩C一定用得到的19款Java開源Web爬蟲

網絡爬蟲(又被稱為網頁蜘蛛&#xff0c;網絡機器人&#xff0c;在FOAF社區中間&#xff0c;更經常的稱為網頁追逐者)&#xff0c;是一種按照一定的規則&#xff0c;自動地抓取萬維網信息的程序或者腳本。另外一些不常使用的名字還有螞蟻、自動索引、模擬程序或者蠕蟲。 今天將為…

一元二次方程

轉載于:https://www.cnblogs.com/569114a/p/4179164.html

cookie html5,HTML5——存儲(cookie、localStorage、sessionStorage)的區別

cookie本來用于客戶端和服務端通信&#xff0c;但是因為它有本地存儲的功能&#xff0c;于是被“借用”了。使用方法document.cookie 獲取和修改即可缺點存儲量太少&#xff0c;只有4kb所有http請求都帶著&#xff0c;會影響獲取資源的效率。API簡單&#xff0c;需要封裝才能使…

數據中心存在不當投資嗎?

不正當的投資是一種危害&#xff1a;在一些項目建設中&#xff0c;投入大量的資金是錯誤的&#xff0c;因為這些項目的需求是不可持續的或高估的。那么數據中心屬于這一類嗎? 投資不當的問題 不當投資會與經濟的繁榮與蕭條齊頭并進。例如&#xff0c;抑制按揭貸款利率可能會導…

問題:循環元素,被選中元素個數,全選

一段時間不寫js都有點忘記了&#xff0c;這里看幾個常見的js&#xff0c;涉及到循環&#xff0c;計算元素個數&#xff0c;checkbox選中等問題&#xff0c;首先是html元素 <div class"content border p05"><div><input type"checkbox" id&q…

LeetCodeOJ. String to Integer (atoi)

試題請參見: https://oj.leetcode.com/problems/string-to-integer-atoi/ 題目概述 Implement atoi to convert a string to an integer.Hint: Carefully consider all possible input cases. If you want a challenge, please do not see below and ask yourself what are the…

html如何設置滾動動畫,JavaScript 實現頁面滾動動畫

在做前端 UI 效果時&#xff0c;讓元素根據滾動位置實現動畫效果是一個非常流行的設計&#xff0c;通常我們會使用第三方插件或庫來實現。在本教程中&#xff0c;我將教大家使用純 JavaScript 和 CSS 來實現。先預覽一下實現的效果&#xff1a;我們使用 CSS 來實現動畫&#xf…

oraclenbsp;一個稍微大點數據庫

公司有個水電收費系統,在包頭試運行, 給了我一個dmp讓熟悉一下業務. dmp是壓縮過的.80多兆好像.解壓下300多兆好像.導入, 有幾個表是50萬行的,幾個30萬左右,200多個表(沒數).很多表是0行,設計居民用戶單位用戶一堆一堆的.用戶有幾萬個. 問題是這樣的: 收費系統每個月要結算一個…

絕非玩笑!人工智能或開創黑客新時代

專家稱&#xff0c;未來的網絡戰爭可能是機器對機器&#xff0c;這可能需要幾年甚至幾十年時間&#xff0c;但黑客并不一定總是人類。人工智能(AI)是可徹底改變網絡安全的技術&#xff0c;而它有一天可能成為最終的攻擊工具。 今年8月由美國國防部先進項目研究局(DARPA)贊助的C…

redis 筆記06 發布與訂閱、事務、慢查詢日志、監視器

發布與訂閱 1. 服務器狀態在pubsub_channels字典保存了所有頻道的訂閱關系&#xff1a;SUBSCRIBE命令負責將客戶端和被訂閱的頻道關聯到這個字典里面&#xff0c;而UNSUBSCRIBE命令則負責 解除客戶端和被退訂頻道之間的關聯。 2. 服務器狀態在pubsub_patterns鏈表保存了所有模式…

html中文段落,HTML 段落-JavaScript中文網-JavaScript教程資源分享門戶

HTML 可以將文檔分割為若干段落。HTML 段落段落是通過 標簽定義的。實例這是一個段落 這是另一個段落嘗試一下 注意&#xff1a;瀏覽器會自動地在段落的前后添加空行。( 是塊級元素)不要忘記結束標簽即使忘了使用結束標簽&#xff0c;大多數瀏覽器也會正確地將 HTML 顯示出來&a…

自學python系列14:映像,集合類型-集合類型

集合類型1.1如何創建集合類型和給集合賦值1.1.1 如何創建集合類型和給集合賦值集合的工廠方法set()和frozenset()>>> sset(abc)>>> sset([a, c, b])>>> tfrozenset(abc)>>> tfrozenset([a, c, b])len()計算的是集合的字母的個數1.1.2如何訪…

觀點:我們為什么需要威脅情報?

最近被談論的異常火熱的一個術語就是威脅情報&#xff0c;那么威脅情報到底是什么意思&#xff0c;它是一種什么概念或者機制呢?本文中我們就來親密接觸一下威脅情報&#xff0c;并了解它所具有的功能&#xff0c;然后給出幾個威脅情報的最佳實踐示例&#xff0c;最后分析威脅…

vijos 1942 [AH 2005] 小島

描述 西伯利亞北部的寒地&#xff0c;坐落著由 N 個小島組成的島嶼群&#xff0c;我們把這些小島依次編號為 1 到 N 。 起初&#xff0c;島嶼之間沒有任何的航線。后來隨著交通的發展&#xff0c;逐漸出現了一些連通兩座小島的航線。例如增加一條在 u 號小島與 v 號小島之間的航…

聊城大學計算機應用基礎函授,聊城大學試題計算機應用基礎試題

姓名 年級專業層次 教學單位密封線 第1頁 共3頁聊城大學《計算機應用基礎》函授試題一、判斷題(共10題&#xff0c;每題2分&#xff0c;共20分)1、信息按狀態劃分可以劃分為動態信息和靜態信息。( √ )2、操作系統不具有通用性。( )3、在Windows XP環境中&#xff0c;整個顯示…

Struts2中 Path (getContextPath與basePath)

struts2中的路徑問題是根據action的路徑而不是jsp路徑來確定&#xff0c;所以盡量不要使用相對路徑。 雖然可以用redirect方式解決&#xff0c;但redirect方式并非必要。解決辦法非常簡單&#xff0c;統一使用絕對路徑。&#xff08;在jsp中用request.getContextpath方式來拿到…

(七)SpringBoot+SpringCloud —— 集成斷路器

2019獨角獸企業重金招聘Python工程師標準>>> 斷路器簡介 在一個項目中&#xff0c;系統可能被拆分成多個服務&#xff0c;例如用戶、訂單和庫存等。 這里存在這服務調用服務的情況&#xff0c;例如&#xff0c;客戶端調用訂單服務&#xff0c;訂單服務又調用庫存服務…