SPL的特征之一是數(shù)據(jù)有序,適當?shù)乩梦恢?,可以顯著提高性能。讓我們先從一個典型場景開始,逐步掌握利用位置的各種技巧。
對排序后的數(shù)據(jù)進行二分查找,可以獲得較高的性能,但有些算法需用到原始順序,看上去似乎不該再排序。比如下面的案例:
PerformanceRanking.txt有三個字段,分別是empID(銷售員編號)、dep(部門名稱)、amount(銷售額)。該文件記錄著各部門各銷售員本季度的業(yè)績排名,已按銷售額逆序存放,現(xiàn)在需根據(jù)指定的銷售員ID,計算出:他應(yīng)當再增加多少銷售額,才能提高業(yè)績排名。如果該員工已經(jīng)是第1名,則無需增加銷售額。
本算法需要用排名高一位的銷售員的銷售額,減去該銷售員的銷售額,即對原始數(shù)據(jù)做相對位置計算。既然要用到原始順序,似乎就不該再排序,否則兩者難以互轉(zhuǎn),而且其他算法可能用到原始數(shù)據(jù)。這種思路下會把腳本寫成這樣:
上述腳本沒有對數(shù)據(jù)排序,所以不能進行二分查找,性能不高。
事實上,我們可以在保留原始數(shù)據(jù)的前提下,利用位置進行排序,從而提高查詢性能。腳本如下:
A5:函數(shù)psort只獲得排序后記錄在原數(shù)據(jù)中的位置,并不會對原數(shù)據(jù)真正排序。
A6:利用oPos制造一份排序后的數(shù)據(jù)。注意,此時原數(shù)據(jù)不受影響,而且oPos可以作為排序后數(shù)據(jù)index和原始數(shù)據(jù)之間互轉(zhuǎn)的橋梁。
A7:對排序后的數(shù)據(jù)做二分查找,并轉(zhuǎn)回原始數(shù)據(jù)中對應(yīng)的記錄序號。
為了驗證利用位置之前、之后兩種算法的性能差別,可以隨機取出銷售員編號做參數(shù),用循環(huán)模擬大量訪問,并分別執(zhí)行兩種算法。如下:
可以看到,利用位置后性能提高幾十倍。例子中數(shù)據(jù)量較少,隨著數(shù)據(jù)量的增加,性能差距會急劇拉大,這是因為遍歷查找的時間復(fù)雜度為線性,而二分查找為對數(shù)。
函數(shù)align可將數(shù)據(jù)按序列對齊,比如輸入條件:=pOrderList= [10250,10247,10248,10249,10251],將訂單明細按該列表對齊,求每個訂單的金額小計。代碼如下:
A2-A3:手工建立索引表。
A4:將訂單明細表與訂單列表對齊,求出金額小計。由于索引表有序,因此可用二分法對齊,即@b選項。
A5:將A4按原位置調(diào)整,與pOrderList的順序保持一致。函數(shù)inv可按指定位置調(diào)整成員,這里按原位置調(diào)整成員,相當于恢復(fù)成原位置。
對利用位置前后的兩種算法,模擬大訪問量測試,可以看到性能提升顯著:
有時要對有序數(shù)據(jù)進行批量查詢,比如pOrderList=[10877,10588,10611,11037,10685],請統(tǒng)計符合該列表的訂單的運貨費合計,代碼可以這樣寫:
解釋:函數(shù)pos和select配合,可實現(xiàn)批量查詢。其中函數(shù)pos可返回某個值在序列中的位置,如該值不在序列中,則返回null。函數(shù)select用于查詢,當條件非null且非false時,可返回當前記錄。
但上述代碼沒有利用位置,所以性能不高。
應(yīng)當注意到,訂單記錄是有序的,所以可以用二分法取得符合條件的訂單位置,再用位置取記錄并計算。具體代碼如下:
A1.(orderID)可取得orderID列,pos@b可針對有序數(shù)據(jù),用二分法快速取得成員位置。A6按位置取數(shù)據(jù)。
對利用位置前后的兩種算法,模擬大訪問量測試,可以看到性能提升顯著: