如何優雅的構建排序公式

最近的一個項目中的需求要對一堆元素進行排序,排序的依據是元素在頁面上面的座標位置,而後按照順序給全部元素一個編號。以下圖所示:
排序並編號前端

作這個需求的是一個新入職的小夥,思考摸索了好久,他也沒有找到合適的方法。不得不說,部分新入職的小夥的思惟能力仍是有待提升啊。其實這個問題很簡單,就是對元素按照座標進行排序。從圖上能夠看出規則是x座標優先於y座標,具體來講,兩個元素a和b:
若是a.x > b.x 則 a > b,
若是a.x < b.x 則 a < b,
若是a.x = b.x ,則當a.y > b.y時 a > b,a.y < b.y時候,a < bjava

把上面的規則翻譯成JavaScript,並結合數組排序函數,很輕鬆就得出瞭解決方案:node

array.sort(function(a,b){
   if(a.x > b.x ){
        return 1;
    }else if (a.x < b.x ){
        return - 1;
    }
    return a.y - b.y
})

以上規則 還能夠整理成這樣一句話,就是: 當x座標相同時,用y座標做爲排序依據,單x座標不一樣時,用x座標做爲排序依據,翻譯成代碼以下程序員

array.sort(function(a,b){
   if(a.x  !=  b.x ){
        return a.x - b.x
    }else {
        return a.y - b.y
    }
})

改爲三元運算符就是:數據庫

array.sort(function(a,b){
        return (a.x  !=  b.x) ? (a.x - b.x) : (a.y - b.y)
})

排序公式

上面已經解決了問題中的需求,可是有沒有一個數學公式就能夠解決這個問題呢? 爲何要想數學公式,由於數學公式是對於世間事物最好的、最優雅的提煉。
通過思考,能夠考慮把x座標的差值的單位值和y座標的差值的單位值,經過必定的加權比例相加,因爲x要佔用的比例更高,因此考慮x的加權值更大,公式以下:數組

Math.sign(a.x - b.x) * 2 + Math.sign(a.y - b.y)
當a.x == b.x的時候,Math.sign(a.x - b.x) == 0,應此判斷的依據天然是y座標。
當a.x != b.x的時候,Math.sign(a.x - b.x) 2的值爲 2 或者 -2 , Math.sign(a.y - b.y) 的值 爲1或者0,或者-1,因此相加的結果的正負是由Math.sign(a.x - b.x) 2決定,也就是x座標決定。
最終經過這個數學,改進代碼以下:
array.sort(function(a,b){
        return Math.sign(a.x - b.x) * 2  + Math.sign(a.y - b.y) 
})

三維座標排序和N維座標排序

若是是三維座標(x,y,z) 排序,x優先,y次之,z最末。 那麼若是是用if判斷,代碼應該以下:架構

array.sort(function(a,b){
        return (a.x  !=  b.x) ? (a.x - b.x) :( (a.y != b.y) ?   (a.y - b.y) : (a.z - b.z)
})

x若是不相等,以x差值爲判斷依據,x若是相等,若是y不相等,以y差值做爲判斷依據,不然 以z值差值做爲判斷依據。
若是一樣要構建一個數學工具呢?思路和前面同樣,把x座標的差值的單位值和y座標的差值的單位值以及z座標的差值的單位值,經過必定的加權比例相加,因爲x要佔用的比例更高,因此考慮x的加權值更大,y要次之。如何來分配權值呢? 由於不能只是x的權值比y的大,其實應該是x的權值比y和z的權值之和都要打,我最開始想的是這樣的:併發

Math.sign(a.x - b.x) 100 + Math.sign(a.y - b.y) 10 + Math.sign(a.z - b.z)

不過很快我否決了,用100和10能夠知足要求,可是感受這個差值太多,沒有必要,
忽然想到2的冪有一個公式,就是:分佈式

1 + 2 2 +... + 2 n-1 = 2 n - 1

能夠看出 2n大於1 + 22 +... + 2n-1之和,應此可使用以下公式:函數

Math.sign(a.x - b.x) 4 + Math.sign(a.y - b.y) 2 + Math.sign(a.z - b.z)

根據這個公式,若是是n維向量的排序,大概以下:

Math.sign(a.x1 - b.x1) Math.pow(2,n) + Math.sign(a.x2 - b.x2) Math.pow(2,n-1) + ... + Math.sign(a.xn - b.xn) * 1

後記

可能有人會說,我直接用條件判斷也能夠作出來,你這個公式有什麼用? 其實我前面說了,由於數學公式是對於世間事物最好的、最優雅的提煉。
同時這也是一個有意思的思考練習,相信能夠培養你的思惟能力。 不少時候,多想一想並無錯,雖然暫時看起來沒有太多做用。

歡迎關注公衆號「ITman彪叔」。彪叔,擁有10多年開發經驗,現任公司系統架構師、技術總監、技術培訓師、職業規劃師。熟悉Java、JavaScript、Python語言,熟悉數據庫。熟悉java、nodejs應用系統架構,大數據高併發、高可用、分佈式架構。在計算機圖形學、WebGL、前端可視化方面有深刻研究。對程序員思惟能力訓練和培訓、程序員職業規劃有濃厚興趣。
ITman彪叔公衆號

相關文章
相關標籤/搜索