在考慮 python 程式的時間開銷時,有乙個問題特別需要注意:python 程式中的很多基本操作不是常量時間的。
下面是一些情況:
1)基本算術運算時常量時間操作【注:】,邏輯運算時常量時間運算。
2)組合物件的操作有些是常量時間的,有些不是,例如:
① 複製和切片操作通常需要線性時間(與長度有關,是 o(n))時間操作。
② list 和 tuple 的元素訪問和元素賦值,是常量時間的。
③ dict 操作的情況比較複雜【補充乙個鏈結位址】。
3)字串也應該看作組合物件,但許多操作不是常量時間的。
4)建立物件也需要付出空間和時間,空間和時間代價都與物件大小有關。
對於組合物件,這裡可能有需要構造的乙個個元素,元素有大小問題,
整體看還有元素個數問題。通常應看作線性時間和線性空間操作(以元素個數作為規模)。
python 結構和操作的效率問題:
1)構造新結構,如構造 list ,set 等。構造新的空結構(空表,空集合等)是常量時間操作,
而構造乙個包含 n 個元素的結構,則至少需要 o(n) 時間。統計說明,分配長度為 n 個
元素的儲存塊的時間代價是 o(n)。
2)一些 list 操作的效率:表元素訪問的元素修改是常量時間操作,但一般的加入、
刪除元素操作(即使只加入乙個元素)都是 o(n) 時間操作。
3) 字典 dict 操作的效率:主要操作是加入新的鍵值對和基於鍵查詢關聯值。
它們的最壞情況複雜度是 o(n) ,但平均複雜度是 o(l) 。
也就是說,一般而言字典操作的效率很高,但偶然也會出現效率低的情況。
在程式裡使用任何型別的物件,都需要付出空間的代價。建立乙個表或者元組,至少要占用元素個數那麼多的空間。
如果乙個表的元素個數與問題規模線性相關,建立它的空間付出至少為 o(n) (如果元素也是新建立的,還需考慮元素本身的儲存開銷)。
需要注意兩點:
1)python 的各種組合資料物件都沒有預設的最大元素個數。在實際使用中,這些結構能根據元素個數的增長自動擴充儲存空間。
從空間占用的角度看,其實際開銷在存續期間可能變大,但通常不會自動縮小(即使後來元素變得很少了)。
2)還應該注意 python 自動儲存管理系統的影響。舉個例子:如果在程式裡建立了乙個表,此後一直將其作為某個全域性變數的值,
這個物件就會始終存在並占用儲存空間。如果將其作為某個函式裡區域性變數的值,或者雖然作為全域性變數的值,但後來通過
賦值將其拋棄,這個表物件就可以被**。
乙個簡單的例子。假設需要把得到的一系列資料存入乙個表,其中得到乙個資料是 o(l) 常量時間操作。**如下:
data =前一程式段需要 o(n) 的時間才能完成工作,而後乙個程式段只需要 o(n) 時間。造成這種情況與 list 的實現方式有關。whille 還有資料:
x =下一資料
data.insert(0,x)
#把所有資料加在表的前面
或者寫為:
data =
while
還有資料:
x =下一資料
data.insert(len(data),x)
#
另乙個例子,建立乙個表,其中包含從 0 到 10000 x n - 1 的整數值:
#view code先編寫乙個計算時間的裝飾器
import
time
deftimer(test):
def inner(*args):
start =time.time()
test(*args)
end = time.time() -start
(end)
return
inner
@timer
deftest1(n):
lst =
for i in range(n*10000):
lst = lst +[i]
return
lsttest1(5) #
3.522050380706787
@timer
deftest2(n):
lst =
for i in range(n*10000):
return
lsttest2(5) #
0.004010200500488281
@timer
deftest3(n):
return [i for i in range(n*10000)]
test3(5) #
0.0020058155059814453
@timer
deftest4(n):
return list(range(n*10000))
test4(5) #
0.001033782958984375
測試環境為小公尺 pro 15.6 筆記本【i5處理器】。
試著嘗試變化 n 值檢視不同函式的增長趨勢。
設計乙個演算法,通過對它的分析可能得到抽象演算法的時間與空間複雜度。
進而,採用某個變成語言可以做出該演算法的實現。
那麼問題來了,演算法的實現(程式)的時間開銷與原演算法的時間複雜度之間有什麼關係?
理想情況是:
作為演算法的實現,相應程式的時間開銷增加趨勢應該達到原演算法的時間複雜度。但是,如果實現做得不好,其實現程式也可能比這差。
前一話題就有這樣的例子,例如函式 test1 :最常見的錯誤做法就是毫無必要地構造一些可能很大的複雜結構。
例如在遞迴定義的函式裡的遞迴呼叫中構造複雜的結構(如 list 等),而後只使用其中的個別元素。
從區域性看,這樣做使得常量時間的操作變成了線性時間操作。
但從遞迴演算法的全域性看,這種做法經常會使多項式時間演算法變成指數時間演算法。
也就是說,把原來有用的演算法變成了基本無用的演算法。
儲存技術複雜性的代價
如今,我們處於前所未有的變化的時代。其中大部分變化都是由資料驅動的。幸運的是,新的儲存解決方案正在幫助企業管理快速的資料增長,來自新的資料來源的輸入,以及使用資料的新途徑。這些技術可以滿足各種應用需求。雲儲存提供更高的靈活性,並節省成本,ssd固態硬碟和nvme快閃儲存器解決了快速響應時間的需求,網...
程式的時間複雜度計算
很多時候一眼就能看出程式的時間複雜度,但是遇到複雜的就需要將其過程推導出來,為此總結以下兩種形式 一 迴圈主體中的變數參與迴圈條件的判斷 找出主體語句中與t n 成 正比的迴圈變數,帶入進行計算,例如 int i 1 while i n i i 2 其中i 2的次數與t n 成正比,則2的t n 次...
python複雜程式的組織講座
當程式簡單時,將 寫進乙個檔案即可。但當專案的複雜度增加時,會出現 過多導致檔案過大的問題。因此,模組和包就能方便管理和維護 模組和包都是複雜程式組織的一種方式,一般來說,一般複雜度較低使用模組 module 複雜度較高使用包 package 來管理 乙個包 package 可以由多個模組組成,乙個...