2012-10-26 73 views
10

我正在用Python/numpy/scipy寫一個小光線跟蹤器。曲面被建模爲二維函數,給出高於正常平面的高度。我減少了找到射線和曲面之間交點的問題,以找到一個變量的函數根。這些功能是連續不斷的微分。用一個變量找出大量函數的根

有沒有比使用scipy root finders(也許使用多個進程)循環遍歷所有函數更有效的方法?

編輯:這些函數是表示光線的線性函數和表面函數之間的差異,限於交叉平面。

+2

什麼是功能?有可能它有一個分析解決方案? – mgilson

+1

表面功能可以任意選擇,我希望它是靈活的。對於一個特定函數(即切比雪夫多項式的疊加),存在一個分析解,但它可能涉及許多參數。通過求解一個線性方程組來求解交點應該可用於特定曲面。 – mikebravo

+0

有標準的方法來查找光線/平面,光線/球體,光線/三角形相交。你能模擬你的表面爲三角形網格嗎?如果沒有解析解或幾何逼近你的曲面函數,我不知道有什麼比通過函數更有效的方法。 – engineerC

回答

3

以下示例顯示使用平分方法並行計算函數x **(a + 1) - b(全部使用不同的a和b)的100萬份副本的根。在這裏約需要12秒。

import numpy 

def F(x, a, b): 
    return numpy.power(x, a+1.0) - b 

N = 1000000 

a = numpy.random.rand(N) 
b = numpy.random.rand(N) 

x0 = numpy.zeros(N) 
x1 = numpy.ones(N) * 1000.0 

max_step = 100 
for step in range(max_step): 
    x_mid = (x0 + x1)/2.0 
    F0 = F(x0, a, b) 
    F1 = F(x1, a, b) 
    F_mid = F(x_mid, a, b) 
    x0 = numpy.where(numpy.sign(F_mid) == numpy.sign(F0), x_mid, x0) 
    x1 = numpy.where(numpy.sign(F_mid) == numpy.sign(F1), x_mid, x1) 
    error_max = numpy.amax(numpy.abs(x1 - x0)) 
    print "step=%d error max=%f" % (step, error_max) 
    if error_max < 1e-6: break 

的基本思想是簡單地對變量的矢量運行求根的所有通常的步驟並行,使用能夠對參數的變量的矢量和等價矢量(S)進行評價的功能定義了各個組件的功能。條件被替換爲掩碼和numpy.where()的組合。這可以繼續下去,直到找到所有的根,直到找到所需的精度,或者直到發現足夠的根,以便將它們從問題中移除並繼續處理排除這些根的較小問題。

我選擇解決的功能是任意的,但是如果功能表現良好,它會有所幫助;在這種情況下,家庭中的所有功能都是單調的,並且只有一個正根。此外,對於平分法,我​​們需要對賦予函數不同符號的變量進行猜測,而且這裏碰巧也很容易想到(x0和x1的初始值)。

上面的代碼使用的可能是最簡單的根發現者(二分法),但同樣的技術可以很容易地應用到牛頓 - 拉夫遜,Ridder等等。根發現方法中的條件越少,越適合它對此。但是,您將不得不重新實現所需的任何算法,因此無法直接使用現有的庫根查找程序功能。

上面的代碼片段的寫法是清晰的,而不是速度。避免一些計算的重複,特別是評估函數每次迭代,而不是3次只有一次,這個速度最多9秒,如下:

... 
F0 = F(x0, a, b) 
F1 = F(x1, a, b) 

max_step = 100 
for step in range(max_step): 
    x_mid = (x0 + x1)/2.0 
    F_mid = F(x_mid, a, b) 
    mask0 = numpy.sign(F_mid) == numpy.sign(F0) 
    mask1 = numpy.sign(F_mid) == numpy.sign(F1) 
    x0 = numpy.where(mask0, x_mid, x0) 
    x1 = numpy.where(mask1, x_mid, x1) 
    F0 = numpy.where(mask0, F_mid, F0) 
    F1 = numpy.where(mask1, F_mid, F1) 
... 

爲了進行比較,使用scipy.bisect()來查找一個根在一次需要〜94秒:

for i in range(N): 
    x_root = scipy.optimize.bisect(lambda x: F(x, a[i], b[i]), x0[i], x1[i], xtol=1e-6) 
+0

我現在正在使用scipy根發現者...而我有點傻眼。對我來說,從來沒有發生過這種算法的實現可以比圖書館更快地完成這項工作。那是一個數量級。我已經做了所有我的矢量代數使用numpy廣播... 我一定會想到這一點,下次我使用一個圖書館實施的記錄良好的算法! – mikebravo