[英]How can natural numbers be represented to offer constant time addition?
Cirdec對一個基本無關的問題的回答使我想知道如何最好地表示自然數並進行恆定的加法,減一並測試零。
假設我們使用
data Nat = Z | S Nat
然后我們可以寫
Z + n = n
S m + n = S(m+n)
我們可以通過放置mr
借記項(對於某個常數r
),在O(1)時間內計算m+n
在添加到n
每個S
構造函數上加一個。 為了獲得O(1) isZero
,對於某個常數p
,我們需要確保每個S
構造函數最多具有p
借方。 如果我們計算a + (b + (c+...))
,那么效果很好,但是如果我們計算((...+b)+c)+d
,它就會崩潰。 問題在於借方堆積在前端。
簡單的方法是直接使用可鏈接的列表,例如Okasaki所描述的列表。 有兩個問題:
O(n)空間不是很理想。
尚不清楚(至少對我來說),當我們不關心列表的順序時,是否需要引導隊列的復雜性。
據我所知,Idris(一種依賴類型的純函數式語言,與Haskell非常接近)以一種非常直接的方式處理該問題。 編譯器了解Nat
和Fin
(上限Nat
),並在可能的情況下將它們替換為機器整數類型和運算,因此生成的代碼非常有效。 但是,對於自定義類型(甚至是同構類型)以及編譯階段,情況並非如此(有一些代碼示例使用Nat
進行類型檢查,這導致編譯時呈指數增長,我可以根據需要提供它們)。
對於Haskell,我認為可以實現類似的編譯器擴展。 另一種可能性是制作TH宏來轉換代碼。 當然,這兩種選擇都不容易。
我的理解是,在基本的計算機編程術語中,潛在的問題是您希望在固定時間內連接列表。 列表沒有前向引用之類的作弊手段,因此,例如,您不能在O(1)時間內跳到最后。
您可以改用環,無論使用a+(b+(c+...))
還是((...+c)+b)+a
邏輯,都可以在O(1)時間合並。 環中的節點不需要雙重鏈接,只需鏈接到下一個節點即可。
減法是除去任何節點O(1),測試零(或一個)是微不足道的。 但是,對於n > 1
測試為O(n)。
如果要減少空間,則可以在每次操作時在插入或刪除點處合並節點,並對其余節點進行加權。 您執行的操作越多,表示就越緊湊! 我認為最壞的情況仍然是O(n)。
我們知道,有兩種“極值”解決方案可以有效地相加自然數:
僅使用O(1)時間的CPU效率。 (請參見本書中的“結構抽象”一章。)但是,該解決方案使用O(n)內存,因為我們將自然數n表示為()
的n個副本的列表。
我尚未進行實際計算,但是我相信對於O(1)數值加法,我們將不需要O(1) FIFO隊列的全部功能,足以引導標准列表[]
(LIFO)以同樣的方式。 如果您有興趣,我可以嘗試詳細說明。
CPU高效解決方案的問題在於,我們需要為內存表示添加一些冗余,以便我們可以節省足夠的CPU時間。 在某些情況下,可以在不犧牲內存大小的情況下實現添加此類冗余的操作(例如O(1)遞增/遞減操作)。 而且,如果我們允許任意樹形,例如在具有自舉列表的CPU高效解決方案中, 樹形太多就無法在O(log n)內存中加以區分。
所以問題是:我們能否找到合適數量的冗余,以便亞線性內存足夠,並且可以實現O(1)加法? 我相信答案是否定的 :
讓我們有一個具有O(1)時間加法的表示形式+算法。 然后,我們得到m位的大小,我們將其計算為2 ^ k個數字的總和,每個數字都為(mk)位的大小。 為了表示每個求和數,我們需要(不考慮表示形式)最少(mk)位存儲器,因此,一開始,我們從(至少) (mk)2 ^ k位存儲器開始。 現在,在這2 ^ k個加法中的每個加法中,我們都可以執行恆定數量的操作,因此我們能夠處理(理想情況下,刪除)總共C 2 ^ k個比特。 因此,最后,我們需要表示結果的位數的下限是(mkC)2 ^ k位。 由於k可以任意選擇,我們的對手可以設置k = mC-1 ,這意味着總和將至少用2 ^(mC-1)= 2 ^ m / 2 ^(C + 1)∈O( 2 ^ m)位。 因此,自然數n總是需要O(n)位內存!
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.