上篇中我們介紹了計算公式引擎的計算原理,本期我們繼續帶着大家了解在Excel表格中公式引擎的實現原理。
背景
在上節中解決了基本運算的邏輯之后,在一些實際業務場景中,公式計算並不是單一公式進行的獨立運算。我們經常需要將一個很大的運算分解成前后依賴的小運算;同時這些單元格之間的計算會出現很多相互依賴,計算順序也是要考慮的一個關鍵問題,我們需要將一系列具有先后順序的同類運算管理起來依次執行。
為了實現這種計算關系之間的管理,出現了計算鏈,用以對公式之間的依賴和先后順序進行管理,處理在電子表單中錯綜復雜的依賴。涉及到圖的處理,臟值計算等內容。接下來我們將從圖計算出發,介紹不同圖的計算、按需計算和臟值處理的問題,更加深層次的了解Excel表格計算中計算鏈相關問題。
計算鏈
讓我們先從兩個表格計算問題出發。
- 第一種簡單情況:
在這一串計算公式之中,當C1賦值為1時,A1的結果為3。但此時如果修改C1的值,C1=10,此時B1的內容還沒有修改,A1依舊是3,然后B1=10+1=11,這里就會發現A1內容計算出錯。
- 第二種更加復雜一些的情況:
我們用圖的結點表示單元格的計算內容,箭頭代表依賴關系。入度為零的節點,完全不依賴其他節點內容,所以計算的順序應該是從不依賴其他節點的節點內容開始。單元格A依賴F、E,D依賴C、B,C、B又分別依賴F、E,唯二不依賴其他節點的內容是E、F。
這樣就得到了一個穩定正確的計算順序,這個順序被稱之為計算鏈。在這個例子中計算鏈是:F,C,E,A,D,B。
有向無環圖和有向有環圖的計算
在一張圖中,如果從一個節點出發,最后能回到這個節點,我們稱之為有向有環圖,反之被稱作有向無環圖。
左圖中不論從任意一個節點出發都不能再回到該節點,所以左圖是有向無環圖。
右圖中A點可以出發后回到A節點,所以右圖是有向有環圖。
我們將計算節點和計算節點之間的關系拆解成有向圖后,就可以使用計算有向圖的標准方法得到一個可靠的計算鏈。
有向無環圖的計算
對於每一個節點存在入度和初度的概念,入度:多少箭頭指向當前節點,例如對於A節點,入度為1;出度:當前箭頭有多箭頭指出,例如對於B節點,入度為2。
在對有向無環圖進行計算的時候,就利用了圖中入度作為優先級排序,每次運算入度為0的節點,然后移除。
以上圖為例,完整的計算過程如下:
1.初始化,統計入度: A:1 B:2 C:1 D:2 E:0 F:0
2. 計算 E 節點,更新入度: A:1 B:2 C:0 D:2 F:0
3. 計算 F 節點,更新入度: A:1 B:2 C:0 D:1
4. 計算 C 節點,更新入度: A:1 B:1 D:0
5. 計算 D 節點,更新入度: A:1 B:0
6. 計算 B 節點,更新入度: A:0
7. 計算 A 節點,計算結束有向有環圖的計算
有向有環圖的計算
解決了有向無環圖的計算問題,如果此時把上圖中B-C之間的箭頭反向調換,情況就會變的截然不同起來。此時B-C-D之間組成了一個環。
這時候依舊進行計算:
1. 初始化,統計入度: A:1 B:1 C:2 D:2 E:0 F:0
2. 計算 E 節點,更新入度: A:1 B:1 C:1 D:2 F:0
3. 計算 F 節點,更新入度: A:1 B:1 C:1 D:1
4. 沒有入度為0的節點,開始迭代計算
(迭代計算:把上一步的計算結果代入這一步的運算中去,經過多步這樣的計算,可以得出一個更為接近的結果。)
按需計算
解決這種互相依賴的復雜單元格的運算時,除了圖計算還可以采用按需計算的方法。在這里使用到的是calcOnDemand這個函數。這個函數的核心工作原理是:壓棧並且計算所需要的節點內容,該節點可以是任意內容。
以該圖內容來說明這一計算的過程:這里選擇A作為我們需要的節點
-
壓棧並計算A,需要計算B的結果
-
壓棧並計算B ,需要計算C的結果
-
壓棧並計算C ,需要計算E的結果
-
壓棧並計算E, E 計算完畢后出棧
-
計算C,C計算完畢后出棧
-
計算B,還需要計算D的結果
-
壓棧並計算D ,拿到C的結果,還需要F的結果
-
壓棧並計算F, F計算完畢后出棧
-
計算D,D計算完畢后出棧
-
計算B,B計算完畢后出棧
-
計算A,A計算完畢后出棧
-
棧空,計算完畢
這個例子取了最復雜的A作為需求節點,如果需要D節點,變為D節點入棧,C節點入棧,E節點入棧,計算后出棧,C節點計算后出棧,F節點入棧計算后出棧,D節點入棧,這樣就得到了正確的D的值,這種運算方式只計算需要的內容。
圖計算VS按需計算
在這里對圖計算和按需計算做一個對別,這兩種算法在不同情況下效率不同。
左圖中是一個從上到下的累加,使用按需計算計算的很順暢,從上向下按順序計算即可,堆棧中存在的待計算元素不超過兩個,按需計算比圖的計算更加快。
右圖中是是一個逆向計算,上一行單元格的內容依賴下一行單元格的內容,按需計算需要計算1000步,這時按需計算會比圖計算慢很多。
總體來說,圖計算比按需計算更加穩定,而按需計算在不同情況下會有不同的表現,實際使用中我們可以根據具體的使用場景采用不同的計算策略。
臟值計算
在這個圖中,如果此時修改了某節點的值,這個時候就需要根據傳播途徑,標記所有需要重算的節點。
舉例:修改了E的內容
-
根據傳播依次將E C B D A標記
-
刪去未標記的節點
-
開始計算剩余的節點
標記完成之后,我們就不再需要關注未標記節點,計算完成。
整個過程如下圖所示:
拓展:關於運算內容幾個問題
已經介紹完計算鏈的全部內容,為了幫助大家更好地理解,這里有幾個思考問題:
-
示例中的圖計算,可否可改用按需計算?
-
可不可以先計算E的值,再標記並重算C B D A?
-
根據傳播途徑標記需要重算節點的優勢?
解答部分:
-
該圖可以進行按需計算,因為按需計算和圖計算都可以對圖內容進行正確計算。
-
該圖中不可以先算E,然后重標CBDA,因為一旦我們一但先計算E,此時C依舊依賴E的內容,而C又不能先於E計算,這時ABCD節點數值都會變得不可靠,這個計算引用鏈就會被完全破壞。
-
臟數據的處理中只對傳播路徑上的節點進行處理,在實際應用場景下,幾百個單元格數據處理使,可以大大減少運算的內容。
總結
所有的單元格計算內容就全部為大家介紹完畢了,我們一起回顧一下本章節內容:計算鏈就是將一個個單元格計算串聯起來,分為普通計算和迭代計算。而這里我們介紹了兩種不同的計算方式——圖計算和按需計算,在面對不同情需要選擇采用不同的計算策略。計算鏈在整個計算過程中不像單元格的計算那么明顯,但是卻比單元格的計算更加復雜。
在了解了計算公式如何進行詞法、語法分析對公式進行快速運算,計算鏈是如何進行多單元格大數據量的處理,接下來將繼續為大家介紹異步函數在前后算計算中的花式用法。
看到這里了點個贊吧~后續本葡萄也會為大家帶來更多嚴肅或有趣的內容。