一次有趣的面試(下)
續上:
周六發表了文章,一次有趣的面試(上) 就趕回寢室dota2了,最近又沉迷了起來。不過一些很精彩的評論讓我很想再把這個球面採樣的問題深入一些,所以原定的(下)是用來寫面試第三題的,現在繼續討論這個球面採樣的問題。
題解:
<一> 高斯分布:
在上一篇中,最後在面試官的解答下,我們完成了一個蒙特卡洛模擬高斯分布的方法,去完成了對立方體內單位球的均勻採樣,然後通過把球體內部的點映射回球面,即可完成對球面的均勻採樣。
def sphere_3(radius): x = radius y = radius z = radius while (x ** 2 + y ** 2 + z ** 2) > radius ** 2: x = (random.random() - 0.5) * 2 * radius y = (random.random() - 0.5) * 2 * radius z = (random.random() - 0.5) * 2 * radius scale = math.sqrt((radius ** 2) / (x ** 2 + y ** 2 + z ** 2)) return [int(scale * x), int(scale * y), int(scale * z)]
實際上這是一個很高效實現很簡潔的方法,其採樣效率為
當然如果你已經熟悉這個問題的本質,那你完全可以直接調用函數去生成三個零均值同sigma的高斯分布,然後疊加歸一化得到我們的結果(也就是評論中說的三個高斯分布疊加即可):
def sphere_4(radius): p = np.random.normal(0, 1, 3) scale = math.sqrt((radius ** 2) / (p[0] ** 2 + p[1] ** 2 + p[2] ** 2)) return [int(scale * p[0]), int(scale * p[1]), int(scale * p[2])]
原因也很簡單對於空間中的任意的點Z(x,y,z):
所以很明顯,對於空間的任意一個球面都是均勻的,我們可以限制落在某一個平面上,當然這樣效率會很低,所以我們歸一化就好了。可以在下圖右看到結果是相同的。
<二> sin(alpha)分布
上一篇文章的評論區另一種主流的解法是 萬里 @萬里 提到的抽樣一個sin(theta)分布,具體說來就是按照sin(theta)在z軸上的每一個環(r=R*sin(theta))截面。這樣每一個環的取到的概率和他們的周長是成比例的,然後在環內均勻分布取一角度即可完成對球面的均勻採樣。
接下來的問題變成利用均勻分布生成一個sin(theta)分布
實際上評論區的大神們都說了,任意分布都可以用均勻分布生成,這裡時間有限,直接說我們用了CDF(累計分布函數)的方法,去生成sin(alpha)分布。
對於:
其累計分布函數:
均勻分布:
然後計算得到
事實上,按照我們的推導我們並不需要計算出theta,得到cos(theta)既可完成我們的採樣,具體代碼如下:
def sphere_5(radius): u = random.random() alpha = random.random()*2*math.pi #cos(theta) = (1-2u), sin(theta) = (1-(1-2u)**2)**(1/2) z = radius*(1-2*u) r = radius*math.sqrt(1-(1-2*u)**2) x = r*math.sin(alpha) y = r*math.cos(alpha) return [int(x), int(y), int(z)]
當然,這個方法,我們也會放一個圖給大家看一下:
小結
這一篇小小的文章讓我發現和知友討論還是蠻有意思的,一方面能意識到自己的不足,一方面也確確實實能找到一些有意思的東西。
本來周日就想把這些東西寫一下,無奈大於瓢潑不想去實驗室,寢室又沒有工作環境,所以只能委屈(痛快)沉迷了一天的dota2~,由於11點多要去趕火車,早上來一個多小時,從寫代碼到畫圖到文章,實在有些匆忙,所以還望大家發現錯誤及時告知~
推薦閱讀:
※【小林的OpenCV基礎課 10】Canny邊緣檢測
※(三)計算機視覺其他應用(網路壓縮、視覺問答、可視化、風格遷移等)
※[CVPR2018筆記]Semi-parametric Image Synthesis
※有趣的圖像處理技術(二)
※cs131課程筆記(10)