用python裝飾器來優化算法

     摘要:最近coding時用到了Python裝飾器,它的作用太強大了,而且使用也簡單,解決了我代碼中大量重複計算的瓶頸,下面以計算Fibonacci數列爲例來說明問題:

C語言版:

#include <stdio.h>
//fib.c
int fib(int n)
{
	if(n < 3)
	{
		return 1;
	}
	else
	{
		return fib(n-1) + fib(n-2);
	}
}

int main(int argc, char* argv[])
{
	if(argc == 2)
		printf("%d\n", fib(atoi(argv[1])));
	else
		printf("Please enter a parameter\n");
}

編譯:gcc -O3 -o fib fib.c

運行:
用時3.28s。

來看下純Python版的:

#!/usr/bin/env python
#fib.py
import sys

def fib(n):
    if n < 2:
        return n
    else:
        return fib(n-2) + fib(n-1)

if len(sys.argv) == 2:
	print fib(int(sys.argv[1]))
else:
	print "Please enter a parameter"

運行:

用時40.19s,本來是想算45的,無奈遲遲不出結果。

和上面的C版用時相差幾百倍多,因爲此處計算的是40,當數據越大,則fib的調用呈指數級上升,所以此處預估爲幾百倍不算誇張,下面會分析此處fib重複調用次數。從這裏可以看出Python的確不適合做這種大量重複計算,所以我們就想到了用C/C++來擴展Python,提高計算速度,用Python使用bitey調用C模塊中的方法來重寫。

Python+ctypes版的:

#!/usr/bin/env python
#fib_ctypes.py

import sys
import ctypes

lib = ctypes.CDLL("./fib.so")
if len(sys.argv) == 2:
	print lib.fib(int(sys.argv[1]))
else:
	print "Please enter a parameter"

運行:

用時9.88s,明顯比純Python版快,但還是和純C版的相差三倍左右。

再來看下Python+bitey版的:

#!/usr/bin/env python
#fib_bitey.py

import sys
import bitey
import fib

if len(sys.argv) == 2:
	print fib.fib(int(sys.argv[1]))
else:
	print "Please enter a parameter"

運行:

用時6.58s,可以看出比ctypes版的要好點,但比純C版還是要差些,不過已經很不錯了,應該是clang+llvm的功勞。

現在回過頭來分析下純Python版的爲什麼會這麼慢,運行:

發現對fib調用了331160281次,不慢都不正常了,基於Fibonacci數列的特點,我們可以想辦法記錄上一次計算後的結果,比如計算f(10),可以把計算過的f(9)和f(8)的結果直接返回,而不用遞歸下去。剛好Python裝飾器可以用來記錄上次計算的結果,關於Python裝飾器的使用請看Python裝飾器與面向切面編程

Python裝飾器版:

#!/usr/bin/env python
#fib_cache.py
import sys

def cache(fib):
    temp = {}
    def _cache(n):
        if n not in temp:
            temp[n] = fib(n)
        return temp[n]
    return _cache
@cache
def fib(n):
    if n < 2:
        return n
    else:
        return fib(n-2) + fib(n-1)

if len(sys.argv) == 2:
    print fib(int(sys.argv[1]))
else:
    print "Please enter a parameter"

運行:

用時0.02s,比純C版的都快150多倍!這就是經過裝飾器處理後的效果,其實C也能做到緩存結果,但比起Python這種簡單明瞭的方式來說它太複雜了。

      總結:在提升python的處理速度時,我們可以通過boost/ctypes/bitey調用c/c++寫的模塊,也可以用cython來編譯,但最終利用語言自身的特性來優化算法纔是王道。

發佈了30 篇原創文章 · 獲贊 6 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章