2009-06-19 106 views
23

我很好奇它爲什麼它乘法的速度比在python中獲取能力要快得多(儘管從我讀過的這個很可能在其他許多語言中也是如此)。例如它的速度更快做計算能力的速度(在python中)

x*x 

x**2 

我想**操作比較一般,也可以處理分數的權力。但是,如果這就是爲什麼它慢得多,爲什麼它不執行一個int指數檢查,然後只是做乘法?

編輯:下面是一些示例代碼我想...

def pow1(r, n): 
    for i in range(r): 
    p = i**n 

def pow2(r, n): 
    for i in range(r): 
    p = 1 
    for j in range(n): 
     p *= i 

現在,POW2僅僅是一個簡單的例子,並且顯然不是優化!
但即使如此,我發現使用n = 2和r = 1,000,000,那麼pow1需要約2500毫秒,pow2需要約1700毫秒。
我承認,對於大的n值,pow1確實比pow2快得多。但這並不令人感到意外。

+0

如果你改變你的循環由1/1000秒,而不是通過1S去,會發生什麼? – Nosredna 2009-06-19 20:42:11

+0

是的,在這種情況下,**比我瘋狂的循環更快!好吧,我想我只需要處理這樣一個事實,即對於小型指數來說,乘法和不使用**會更快。 – Christwo 2009-06-19 21:13:21

+3

我已經對此進行了博客(http://numericalrecipes.wordpress.com/2009/06/05/binary-exponentiation/),但通過平方搜索指數(http://en.wikipedia.org/wiki/ Exponentiation_by_squaring)也可以讓您更好地瞭解如何有效計算整數指數的權力,以及爲什麼它們對於小指數(<4)可能稍微慢一點。 – Jaime 2009-06-19 23:32:47

回答

19

基本上天真的乘法是一個非常低的常數因子O(n)。採取的權力是O(log n)具有較高的常數因子(有些特殊情況需要測試......分數指數,負指數等)。編輯:只需要清楚,那就是O(n),其中n是指數。

當然,對於小n來說,幼稚的方法會更快,你只是真的實現了指數數學的一個小子集,所以你的常數因子可以忽略不計。

0

x怎麼樣x x x x x? 它比x ** 5還快嗎?

隨着int指數變大,採取權力可能會比乘法更快。 但實際交叉發生的數量取決於各種條件,所以在我看來,這就是爲什麼在語言/庫級別沒有完成(或不能完成)優化的原因。但用戶仍然可以針對一些特殊情況進行優化:)

6

添加支票也是一項開支。你總是希望那裏的支票?編譯語言可以檢查一個常量指數,看它是否是一個相對較小的整數,因爲沒有運行時成本,只是編譯時成本。解釋型語言可能不會進行檢查。

這取決於特定的實現,除非該語言指定了這種細節。

Python不知道你要給它分配什麼指數。如果它將是99%的非整數值,你是否希望代碼每次檢查一個整數,使運行時更慢?

1

我懷疑沒有人會期待這一切都是那麼重要。通常情況下,如果你想做嚴肅的計算,你可以用Fortran或者C或者C++或者類似的東西來做(也許可以從Python中調用它們)。

在n不是整數或非常大的情況下,處理所有exp(n * log(x)),但對於小整數來說效率相對較低。檢查n是否足夠小會花費時間,並增加複雜性。

支票是否值得,取決於期望的指數,在這裏獲得最佳表現的重要性以及額外複雜性的成本。顯然,Guido和Python團伙的其他成員決定檢查不值得。

如果你喜歡,你可以編寫自己的重複乘法函數。

3

在指數檢查中這樣做會減慢並非兩個簡單的兩次冪的情況,所以不一定是贏。但是,在指數已知的情況下(例如使用文字2),可以通過簡單的窺孔優化來優化生成的字節碼。據推測,這根本不值得做(這是一個相當具體的案例)。

下面是做這樣的優化(可用作裝飾)的概念的快速證明。注意:您需要byteplay模塊來運行它。

import byteplay, timeit 

def optimise(func): 
    c = byteplay.Code.from_code(func.func_code) 
    prev=None 
    for i, (op, arg) in enumerate(c.code): 
     if op == byteplay.BINARY_POWER: 
      if c.code[i-1] == (byteplay.LOAD_CONST, 2): 
       c.code[i-1] = (byteplay.DUP_TOP, None) 
       c.code[i] = (byteplay.BINARY_MULTIPLY, None) 
    func.func_code = c.to_code() 
    return func 

def square(x): 
    return x**2 

print "Unoptimised :", timeit.Timer('square(10)','from __main__ import square').timeit(10000000) 
square = optimise(square) 
print "Optimised :", timeit.Timer('square(10)','from __main__ import square').timeit(10000000) 

這給計時:

Unoptimised : 6.42024898529 
Optimised : 4.52667593956 

[編輯] 其實,想多一點,有一個很好的理由,爲什麼這optimisaton沒有這樣做。無法保證有人不會創建覆蓋__mul____pow__方法的用戶定義的類,併爲每個方法執行不同的操作。要安全地做到這一點,唯一的方法是如果可以保證堆棧頂部的對象具有相同結果「x **2」和「x*x」,但是解決這個問題要困難得多。例如。在我的例子中是不可能的,因爲任何對象都可以傳遞給平方函數。

2

b^P的執行二進制冪

def power(b, p): 
    """ 
    Calculates b^p 
    Complexity O(log p) 
    b -> double 
    p -> integer 
    res -> double 
    """ 
    res = 1 

    while p: 
     if p & 0x1: res *= b 
     b *= b 
     p >>= 1 

    return res