時間複雜度最小的最短路徑演算法 由這個想到的

2021-07-08 14:19:13 字數 3342 閱讀 3235

最近看書發現了一段很有意思的東西,好像是谷歌的工程師發表在谷歌黑板報裡的:

有一次,我笨得忘記了該如何在乙個複雜的有向圖中找出兩點之間的最短路徑。身邊的一位工程師很鄭重地告訴我說:「你知道嗎?解決這個問題有兩種方法,聰明人的方法和笨人的方法。聰明人的方法是:照著演算法教科書的講解,實現那個時間複雜度相當大的名叫嘀嘀噠嘀噠的最短路徑演算法。笨人的方法時間複雜度最低:找一堆線頭來,按照有向圖的結構連成一張網,然後一手拿乙個頂點,向兩邊一抻,中間拉直了的那條路就是最短路徑呀。」

「哇噻!笨是一種多麼偉大的品格呀!」我眩暈得說不出話來。於是,我們這兩個自認為足夠笨的工程師足足花了兩周的時間,用電腦程式模擬了不同材質的細線在北半球的重力條件下相互連線並在兩個反方向作用力的影響下向兩

邊伸展的整個物理過程,然後以此為基礎實現了時間複雜度最小的最短路徑演算法。——瞧,在 google,什麼東西都可以自己動手實現,什麼東西也都可以推陳出新,我們的傑出表現就是最好的證明。

乍一看讓人覺得甚是巧妙,不過總覺得有什麼不對的地方,思索一番,是有此文

當然這個場景有個前提,邊都是無向邊,否則就不好模擬了。

我們來考慮一下這樣乙個模擬程式應該怎麼寫,就忽略什麼細線的材質和北半球的重力條件了,先試著簡化一下場景,因為我們想求源點s和目標點t之間的最短距離,只需要把源點s釘在牆上,其他點都掛在s下面,假設點不佔空間,此時受重力作用,s和t的距離就是它們的最短距離了,這個問題應該是和原問題等價的,拉伸方向不同而已。

這個東西怎麼模擬呢?一時沒有頭緒,我們就再簡化一下問題,假設每條邊的長度都為1,這些就簡單了嘛:最後掛出來的效果肯定是分層的,和s距離為1的點在第一層,和s距離為2的點在第二層:我們先把直接和s相連的點捋直了掛在下面,這些點肯定就是最短距離了,這是第一層;然後把能連到第一層並且還沒掛上去的點掛到第二層,最後掛到t的時候結果就出來了。

其實很清楚了,這不過就是簡單的寬搜(bfs)而已,忽略掉同層相連的邊之後整個圖就簡化為了乙個樹,樹的根就是s,最短距離就是t點的深度,演算法時間複雜度是o(m+n)的,m為邊的數量,n為點的數量。

目前還不錯,目前已知的最優兩點間最短路徑演算法是o(m + n log n)的,可是別忘了我們現在是在特殊條件下:每條邊的長度都是相同的

如果邊的長度各有不同,會出現什麼情況呢?別的先不管,我們照例掛上第一層的點(左1):

嗯,有問題了,這些點能拉直掛著的前提條件是此時它到源點的距離已經是最短的,所以當邊的長度不定的時候,點和點直接相連的邊不一定是最短距離!以上圖的c點為例,通過乙個中間節點掛上去的才是最短距離!(右2)

好像我們的方法用不上了,看看能不能改進解決呢?嗯,我們是要保證掛上去的就是最短距離,其實很好辦嘛,我們一次只掛乙個上去!

首先把直接連到s點且最近的那個點(假設是a)掛上去,此時肯定是最短距離;然後我們來掛第二個點,會有兩種情況:1.這個點是直接掛在s上的;2.這個點是掛在a上的。此時我們就有了這兩個候選集合,只需要選出其中到源點距離最短的點掛上即可(這個距離如果是和s連直接就是邊長度,如果是和a連的就加上a到s的距離,假設我們選到了b),然後在下一步需要從三個候選集合(和s連的,和a連的,和b連的)裡選擇……

這樣就滿足了我們要求的前提條件,偽**如下:

f = {}; 表示節點到邊的最短距離

now = s;

f[s] = 0;

while (now!=t):

now

f[now] = f[q] + e[q][now]; //假設now通過q連到源點s

嗯,搞定收工!算算複雜度,找距離最小的點可以用小根堆,老師教過,這是o(log n)的,外面還有個n層迴圈,還需要掃瞄邊…………等等,話說這個描述咋這麼眼熟啊?這不就是dijkstra演算法嗎~~ ಥ_ಥ ~~,所以不管怎麼玩,這個演算法的複雜度最好就是o(m + n log n)的。

看來這樣類似貪心的模擬沒用了啊,不得不上真傢伙了,真實模擬乙個網路來釘一次牆!

嗯,主要是靠腦暴,真要寫乙個就太麻煩了,我們回到原始的問題,也就是把網路平著抻直的場景

為了模擬拉這個動作,我們把s固定在牆1上,t固定在牆2上,然後一點一點把牆2從牆1的位置開始往右邊挪動……

1.要真模擬,那麼線和點都變成了實體,但它們都是沒有碰撞體積的。

2.所謂模擬,其實就是按時間步去模擬,根據實體的受力情況改變它的位置,畢竟計算機的世界是離散的嘛,我們需要一點點改變點的未知以保證線不被拉斷(為了防止線被拉斷,當線被拉直的時候會產生拉力,這就是力的傳遞)。

3.所以每個時間步需要去掃瞄每個節點,力的**乙個是重力,乙個是線的拉力,因此你還需要訪問到所有的線,這裡的複雜度已經到了o(m+n)。

4.最後我們要確定的其實就是整個系統穩定下來(掛穩不亂晃悠了)需要的時間步應該是多少。

因為我們的模擬是每次去掃瞄節點,但其實節點是不知道整個系統的真實情況的,比如我移動了一下牆2,所有的資訊(力)都需要從t點傳遞過來:首先是t的鄰點感知到了t位置的變化,因此產生了力的變化,這些點會受拉力移動一下,然後傳播下一層的點再感知到這個變化,再根據受力情況改變自己的狀態……and so on.

所以這個時間步其實是和樹的深度(就是我們假模擬一開始用的那個東西)有關的!力的傳遞是需要時間的!所以時間步的複雜度其實是o(n),ps:真實世界的情況~~zhihu.com 力的傳遞有速度嗎?

所以這個模擬方法的複雜度應該是o((m+n)*n)的,並不是所謂的時間複雜度最小的最短路徑演算法

嗯,以上純粹是我的推論~~~

因為也沒實際看到原作者的實現,但我認為從理論上來說所謂模擬方法的複雜度肯定不可能低於目前已知最優演算法的,所以工程獅還是自high居多,雖然看起來是很有說服力。

以上文章有問題還請指出,這個話題討論起來我覺得還是蠻有意思的

演算法時間複雜度空間複雜度

演算法 是解決某一類問題的通法,即一系列清晰無歧義的計算指令。每個演算法只能解決具有特定特徵的一類問題,但乙個問題可由多個演算法解決。乙個演算法應該有以下五個方面的特性 比較演算法的優劣我們從兩個維度去進行考量 時間 空間 時間複雜度,空間複雜度 找出基本語句 演算法中執行次數最多的那條語句就是基本...

演算法 時間複雜度 空間複雜度

1 if i 1 2 a 1 result 3 4 result n 2 result 1000 1000 3 array.push a array.pop 4 map.set 1,1 map.get 1,1 在計算複雜度的時候,o 1 一般會被忽略。1 for let i 0 i n i 2 wh...

演算法的時間複雜度 空間複雜度

時間複雜度和空間複雜度是度量演算法效率的常用指標 事後統計,不常用 事前統計影響因素 演算法策略 問題規模 程式語言 質量 機器執行指令的速度 撇開軟硬體的影響,演算法執行工作量的大小只依賴於問題的規模 通常用整數n表示 乙個演算法是由控制結構 順序,分支,迴圈三種 和原操作 指固有資料型別的操作 ...