最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

如何用Java實現排列組合算法

 更新時間:2021年05月26日 10:54:37   作者:枕邊書  
本文主要介紹了如何用Java實現排列組合算法,對算法感興趣的同學,可以參考一下,理解其原理,并且試驗一下。

需求

我們的數據表有多個維度,任意多個維度組合后進行 group by 可能會產生一些”奇妙”的反應,由于不確定怎么組合,就需要將所有的組合都列出來進行嘗試。

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

要求如下:

  • 組合內的元素數大于 0 小于等于 數組大?。?/li>
  • 組合內不能有重復元素,如 [aab] 是不符合要求的組合;
  • 組合內元素的位置隨意,即 [ab] 和 [ba] 視為同一種組合;

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

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

從排列到組合-窮舉

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

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

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

代碼是這樣:

private static Set<Set<String>> exhaustion() {
    List<String> m = Arrays.asList("a", "b", "c", "d", "e");
    Set<Set<String>> result = new HashSet<>();
    int count = 3;
    for (int a = 1; a < m.size(); a++) {
        for (int b = 0; b < m.size(); b++) {
            for (int c = 0; c < m.size(); c++) {
                Set<String> tempCollection = new HashSet<>();
                tempCollection.add(m.get(a));
                tempCollection.add(m.get(b));
                tempCollection.add(m.get(c));
                // 如果三個元素中有重復的會被 Set 排重,導致 Set 的大小不為 3
                if (tempCollection.size() == count) {
                    result.add(tempCollection);
                }
            }
        }
    }

    return result;
}

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

  • 元素唯一性,選取三個元素放到 Set 內,重復的會被過濾掉,那么就可以通過集合的大小來判斷是否有重復元素了,
  • 元素無序性,Set[A B] 和 Set[B A] 都會被表示成 Set[A B]。
  • 另外又由于元素唯一性,被同時表示為 Set[A B] 的多個集合只會保留一個,這樣就可以幫助將全排列轉為組合。

可以注意得到,上面程序中 count 參數是寫死的,如果需要取出 4 個元素的話就需要四層循環(huán)嵌套了,如果取的元素個取是可變的話,普通的編碼方式就不適合了。

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

從排列到組合-分治

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

分治思想

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

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

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

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

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

代碼實現

用代碼實現如下:

public class Combination {

    public static void main(String[] args) {
        List<String> m = Arrays.asList("a", "b", "c", "d", "e");
        int n = 5;

        Set<Set<String>> combinationAll = new HashSet<>();
        // 先將問題分解成 五取一、五取二... 等的全排列
        for (int c = 1; c <= n; c++) {
            combinationAll.addAll(combination(m, new ArrayList<>(), c));
        }

        System.out.println(combinationAll);
    }

    private static Set<Set<String>> combination(List<String> remainEle, List<String> tempCollection, int fetchCount) {
        if (fetchCount == 1) {
            Set<Set<String>> eligibleCollections = new HashSet<>();
            // 在只差一個元素的情況下,遍歷剩余元素為每個臨時集合生成多個滿足條件的集合
            for (String ele : remainEle) {
                Set<String> collection = new HashSet<>(tempCollection);
                collection.add(ele);
                eligibleCollections.add(collection);
            }
            return eligibleCollections;
        }

        fetchCount--;
        Set<Set<String>> result = new HashSet<>();
        // 差多個元素時,從剩余元素中取出一個,產生多個臨時集合,還需要取 count-- 個元素。
        for (int i = 0; i < remainEle.size(); i++) {
            List<String> collection = new ArrayList<>(tempCollection);
            List<String> tempRemain = new ArrayList<>(remainEle);
            collection.add(tempRemain.remove(i));
            result.addAll(combination(tempRemain, collection, fetchCount));
        }
        return result;
    }
}

其實現就是遞歸,關于遞歸和分治,有興趣可以看一下隱藏篇:遞歸和分治。

直擊本質-位運算

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

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

組合算法也能通過位運算實現。

思想

再次考慮全組合的需求,從 M 個元素中取任意個元素形成組合,組合內元素不能重復、元素位置無關。

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

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

A  B  C  D  E

0  0  0  0  1   [E] = 1

A  B  C  D  E

0  0  0  1  0   [D] = 2

A  B  C  D  E

0  0  0  1  1   [DE] = 3

...

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

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

代碼實現

下面是 Java 代碼的實現:

public class Combination {

    public static void main(String[] args) {
        String[] m = {"A", "B", "C", "D", "E"};
        Set<Set<String>> combinationAll = combination(m);
        System.out.println(combinationAll);

    }

    private static Set<Set<String>> combination(String[] m) {
        Set<Set<String>> result = new HashSet<>();

        for (int i = 1; i < Math.pow(2, m.length) - 1; i++) {
            Set<String> eligibleCollections = new HashSet<>();
            // 依次將數字 i 與 2^n 按位與,判斷第 n 位是否為 1
            for (int j = 0; j < m.length; j++) {
                if ((i & (int) Math.pow(2, j)) == Math.pow(2, j)) {
                    eligibleCollections.add(m[j]);
                }
            }
            result.add(eligibleCollections);
        }
        return result;
    }
}

小結

排列和組合算法在實際應用中很常見,而且他們的實現方法也非常具有參考意義??偟膩碚f:排列用遞歸、組合用位運算。

以上就是如何用Java實現排列組合算法的詳細內容,更多關于用Java實現排列組合算法的資料請關注腳本之家其它相關文章!

相關文章

  • Java利用Phantomjs實現生成圖片的功能

    Java利用Phantomjs實現生成圖片的功能

    這篇文章主要介紹了Java利用Phantomjs實現生成圖片的功能,文中講解非常細致,代碼幫助大家更好的理解和學習,感興趣的朋友可以了解下
    2020-08-08
  • SpringFox實現自動生成RESTful?API文檔

    SpringFox實現自動生成RESTful?API文檔

    在開發(fā)?RESTful?API?時,編寫?API?文檔是一個重要的任務,這篇文章為大家介紹了如何使用?SpringFox?自動生成?RESTful?API?文檔,并提供示例代碼,需要的可以參考一下
    2023-06-06
  • SpringBoot項目修改訪問端口和訪問路徑的方法

    SpringBoot項目修改訪問端口和訪問路徑的方法

    這篇文章主要介紹了SpringBoot項目修改訪問端口和訪問路徑的方法,小編覺得挺不錯的,現在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-12-12
  • Java中字符數組、String類、StringBuffer三者之間相互轉換

    Java中字符數組、String類、StringBuffer三者之間相互轉換

    這篇文章主要介紹了Java中字符數組、String類、StringBuffer三者之間相互轉換,需要的朋友可以參考下
    2018-05-05
  • java使用websocket,并且獲取HttpSession 源碼分析(推薦)

    java使用websocket,并且獲取HttpSession 源碼分析(推薦)

    這篇文章主要介紹了java使用websocket,并且獲取HttpSession,通過使用配置源碼分析了各方面知識點,具體操作步驟大家可查看下文的詳細講解,感興趣的小伙伴們可以參考一下。
    2017-08-08
  • Java線程的基本概念

    Java線程的基本概念

    本文主要介紹了Java線程的基本概念。具有很好的參考價值,下面跟著小編一起來看下吧
    2017-02-02
  • 深入理解java中Arrays.sort()的用法

    深入理解java中Arrays.sort()的用法

    這篇文章主要介紹了深入理解java中Arrays.sort()的用法,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-05-05
  • Java Yml格式轉換為Properties問題

    Java Yml格式轉換為Properties問題

    本文介紹了作者編寫一個Java工具類來解決在線YAML到Properties轉換時屬性內容遺漏的問題,通過遍歷YAML文件的樹結構,作者成功實現了屬性的完整轉換,總結指出,該工具類適用于多種數據類型,并且代碼簡潔易懂
    2024-12-12
  • SpringBoot實現PDF轉圖片的代碼示例

    SpringBoot實現PDF轉圖片的代碼示例

    在本文中,我們使用SpringBoot演示了如何將PDF文件轉換為一張或多張圖片,這些示例演示了如何使用Java編程語言與其他開源技術集成,以實現各種文件格式之間的轉換,感興趣的小伙伴跟著小編一起來看看吧
    2024-08-08
  • IntelliJ IDEA 2020常用配置設置大全(方便干活)

    IntelliJ IDEA 2020常用配置設置大全(方便干活)

    這篇文章主要介紹了IntelliJ IDEA 2020常用配置設置大全(方便干活),本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2021-02-02

最新評論

舞钢市| 宿松县| 偏关县| 灌阳县| 石泉县| 易门县| 彰化市| 佛坪县| 剑阁县| 洪江市| 华安县| 大化| 班戈县| 湖南省| 涞源县| 封开县| 榆树市| 彭泽县| 察隅县| 吉安市| 湘潭县| 渭南市| 陈巴尔虎旗| 伊宁市| 颍上县| 山东省| 张掖市| 闵行区| 嵊州市| 崇左市| 海南省| 长乐市| 万载县| 谢通门县| 巴楚县| 久治县| 延长县| 醴陵市| 泸定县| 卫辉市| 屏东县|