日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

二进制全排列 java_排列组合算法真厉害,傻瓜都能学会

發(fā)布時間:2023/12/4 编程问答 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 二进制全排列 java_排列组合算法真厉害,傻瓜都能学会 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
作者:枕邊書來源:https://zhenbianshu.github.io/2019/01/charming_alg_permutation_and_combination.html

需求

最近工作中碰到一個需求:我們的數(shù)據(jù)表有多個維度,任意多個維度組合后進行 group by 可能會產(chǎn)生一些”奇妙”的反應(yīng),由于不確定怎么組合,就需要將所有的組合都列出來進行嘗試。

抽象一下就是從一個集合中取出任意元素,形成唯一的組合。如 [a,b,c] 可組合為 [a]、[b]、[c]、[ab]、[bc]、[ac]、[abc]。

要求如下:

  • 組合內(nèi)的元素數(shù)大于 0 小于等于 數(shù)組大小;
  • 組合內(nèi)不能有重復(fù)元素,如 [aab] 是不符合要求的組合;
  • 組合內(nèi)元素的位置隨意,即 [ab] 和 [ba] 視為同一種組合;

看到這里,就應(yīng)該想到高中所學(xué)習(xí)的排列組合了,同樣是從集合中取出元素形成一個另一個集合,如果集合內(nèi)元素位置隨意,就是組合,從 b 個元素中取 a 個元素的組合有 種。而如果要求元素順序不同也視為不同集合的話,就是排列,從 m 個元素取 n 個元素的排列有 種。

我遇到的這個需求就是典型的組合,用公式來表示就是從元素個數(shù)為 n 的集合中列出 種組合。

文中算法用Java實現(xiàn)。

從排列到組合-窮舉

對于這種需求,首先想到的當(dāng)然是窮舉。由于排列的要求較少,實現(xiàn)更簡單一些,如果我先找出所有排列,再剔除由于位置不同而重復(fù)的元素,即可實現(xiàn)需求。假設(shè)需要從 [A B C D E] 五個元素中取出所有組合,那么我們先找出所有元素的全排列,然后再將類似 [A B] 和 [B A] 兩種集合去重即可。

我們又知道 ,那么我們先考慮一種情況 ,假設(shè)是 ,從 5 個元素中選出三個進行全排列。

被選取的三個元素,每一個都可以是 ABCDE 之一,然后再排除掉形成的集合中有重復(fù)元素的,就是 5 選 3 的全排列了。

代碼是這樣:

對于結(jié)果組合的排重,我借用了 Java 中 HashSet 的兩個特性:

元素唯一性,選取三個元素放到 Set 內(nèi),重復(fù)的會被過濾掉,那么就可以通過集合的大小來判斷是否有重復(fù)元素了,

元素?zé)o序性,Set[A B] 和 Set[B A] 都會被表示成 Set[A B]。另外又由于元素唯一性,被同時表示為 Set[A B] 的多個集合只會保留一個,這樣就可以幫助將全排列轉(zhuǎn)為組合。可以注意得到,上面程序中 count 參數(shù)是寫死的,如果需要取出 4 個元素的話就需要四層循環(huán)嵌套了,如果取的元素個取是可變的話,普通的編碼方式就不適合了。

: 可變層數(shù)的循環(huán)可以用 遞歸 來實現(xiàn)。

從排列到組合-分治

窮舉畢竟太過暴力,我們來通過分治思想來重新考慮一下這個問題:

分治思想

分治的思想總的來說就是”大事化小,小事化了”,它將復(fù)雜的問題往簡單劃分,直到劃分為可直接解決的問題,再從這個直接可以解決的問題向上聚合,最后解決問題。

從 M 個元素中取出 N 個元素整個問題很復(fù)雜,用分治思想就可以理解為

首先,如果我們已經(jīng)從 M 中元素取出了一個元素,那么集合中還剩下 M-1 個,需要取的元素就剩下 N-1 個。還不好解決的話,我們假設(shè)又從 M-1 中取出了一個元素,集合中還剩下 M-2 個,需要取的元素只剩下 N-2 個。直到我們可能取了有 M-N+1 次,需要取的元素只剩下一個了,再從剩余集合中取,就是一個簡單問題了,很簡單,取法有 M-N+1 種。如果我們解決了這個問題,已經(jīng)取完最后一次了產(chǎn)生了 M-N+1 種臨時集合,再考慮從 M-N+2 個元素中取一個元素呢,又有 M-N+2 種可能。將這些可能聚合到一塊,直到取到了 N 個元素,這個問題也就解決了。

還是從 5 個元素中取 3 個元素的示例

從 5 個元素中取 3 個元素是一個復(fù)雜問題,為了簡化它,我們認為已經(jīng)取出了一個元素,還要再從剩余的 4 個元素中取出 2 個,求解公式為:。從 4 個元素中取出 2 個依舊不易解決,那我們再假設(shè)又取出了一個元素,接下來的問題是如何從 3 個元素中取一個,公式為 。從 3 個元素中取 1 個已經(jīng)是個簡單問題了,有三種可能,再向上追溯,與四取一、五取一的可能性做乘,從而解決這個問題。代碼實現(xiàn)

用代碼實現(xiàn)如下:

其實就是 遞歸。

直擊本質(zhì)-位運算

從元素的全排列找全組合,比窮舉略好,但還不是最好的方法,畢竟它”繞了一次道”。

很多算法都能通過位運算巧秒地解決,其優(yōu)勢主要有兩點:一者位運算在計算機中執(zhí)行效率超高,再者由于位運算語義簡單,算法大多直指本質(zhì)。

組合算法也能通過位運算實現(xiàn)。

思想

再次考慮全組合的需求,從 M 個元素中取任意個元素形成組合,組合內(nèi)元素不能重復(fù)、元素位置無關(guān)。

之前的方法都是從結(jié)果組合是否滿足要求來考慮問題,考慮組合是否有重復(fù)元素、是否已有同樣的組合等條件。如果換種思路,從待選元素上來考慮呢?

對于每個元素來說,它的狀態(tài)就簡單得多了,要么被放進組合,要么不放進組合。每個元素都有這么兩種狀態(tài)。如果從 5 個元素中任意取 N 個元素形成組合的話,用二進制位來表示每個元素是否被放到組合里,就是:

看到這里,應(yīng)該就非常清楚了吧,每種組合都可以拆解為 N 個二進制位的表達形式,而每個二進制組合同時代表著一個十進制數(shù)字,所以每個十進制數(shù)字都就能代表著一種組合。

十進制數(shù)字的數(shù)目我們很簡單就能算出來,從00000... 到 11111... 一共有 種,排除掉全都不被放進組合這種可能,結(jié)果有種。

代碼實現(xiàn)

下面是 Java 代碼的實現(xiàn):

小結(jié)

排列和組合算法在實際應(yīng)用中很常見,而且他們的實現(xiàn)方法也非常具有參考意義。總的來說:排列用遞歸、組合用位運算。

總結(jié)

以上是生活随笔為你收集整理的二进制全排列 java_排列组合算法真厉害,傻瓜都能学会的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。