亚洲中字慕日产2020,大陆极品少妇内射AAAAAA,无码av大香线蕉伊人久久,久久精品国产亚洲av麻豆网站

資訊專(zhuān)欄INFORMATION COLUMN

示例:如何多線(xiàn)程遍歷組合

iOS122 / 2565人閱讀

摘要:如何用多線(xiàn)程遍歷這棵樹(shù)呢按一級(jí)節(jié)點(diǎn)不同的值,分別放到線(xiàn)程里面遍歷即可。代碼如下多線(xiàn)程遍歷組合樹(shù)根據(jù)一級(jí)節(jié)點(diǎn)拆分解空間對(duì)拆分的解空間多線(xiàn)程遍歷第一個(gè)解和最后一個(gè)解遍歷解區(qū)間取的組合遍歷完成,共有個(gè)解。

這是一個(gè)再簡(jiǎn)單不過(guò)的組合問(wèn)題:

編號(hào) 0-9 的 10 個(gè)球里面拿取任意 5 個(gè),有多少種不同的組合?

答案是可以用公式算出來(lái)的,也就是 (10!) / ((5!) ^ 2) = 252 個(gè)。但是如果要把它們?nèi)勘闅v出來(lái)呢?

下面是一種效率比較高的遍歷方式,原理是將所有結(jié)果集看作是樹(shù)節(jié)點(diǎn)(準(zhǔn)確的說(shuō)是葉子節(jié)點(diǎn)),然后去遍歷這棵樹(shù)即可。樹(shù)的生成規(guī)則是:

一級(jí)節(jié)點(diǎn)的值是第一個(gè)球的編號(hào),二級(jí)節(jié)點(diǎn)是第二個(gè)球的編號(hào),依此類(lèi)推;

任何一級(jí)節(jié)點(diǎn)的值必須大于上級(jí)節(jié)點(diǎn)的值。

這樣能做到所有的葉子節(jié)點(diǎn)剛好覆蓋所有的解,沒(méi)有多余沒(méi)有缺失。

如何用多線(xiàn)程遍歷這棵樹(shù)呢?按一級(jí)節(jié)點(diǎn)不同的值,分別放到線(xiàn)程里面遍歷即可。每個(gè)節(jié)點(diǎn)代表一個(gè)子樹(shù),先計(jì)算該樹(shù)的起始和終止節(jié)點(diǎn),作為解空間的邊界,然后從起始節(jié)點(diǎn)開(kāi)始遍歷直到終止節(jié)點(diǎn)為止即可。

代碼如下:

import java.util.Arrays;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
import java.util.stream.IntStream;

/**
 * 多線(xiàn)程遍歷組合樹(shù)
 */
public class CombinationIterator {

    public static void main(String[] args) throws Exception {

        int itemCount = 50;
        int pickCount = 10;
        AtomicLong answerCount = new AtomicLong();

        long start = System.currentTimeMillis();

        // 根據(jù)一級(jí)節(jié)點(diǎn)拆分解空間
        int[] level1Values = IntStream.range(0, itemCount - pickCount + 1).toArray();
        ExecutorService threadPool = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());

        // 對(duì)拆分的解空間多線(xiàn)程遍歷
        for (int level1Value : level1Values) {

            // 第一個(gè)解和最后一個(gè)解
            int[] firstPicks = IntStream.range(level1Value, pickCount + level1Value).toArray();
            int[] lastPicks =
                    IntStream.concat(
                            IntStream.range(level1Value, level1Value + 1),
                            IntStream.range(itemCount - pickCount + 1, itemCount)
                    ).toArray();

            // 遍歷解區(qū)間
            threadPool.submit(() -> answerCount.addAndGet(iterateSubTree(firstPicks, lastPicks)));
        }

        threadPool.shutdown();
        threadPool.awaitTermination(1, TimeUnit.HOURS);

        System.out.printf("%d 取 %d 的組合遍歷完成,共有 %d 個(gè)解。%n", itemCount, pickCount, answerCount.get());
        System.out.printf("執(zhí)行時(shí)間 %dms", (System.currentTimeMillis() - start));
    }

    /**
     * 遍歷區(qū)間的組合
     *
     * @param firstPicks 區(qū)間的第一個(gè)解
     * @param lastPicks  區(qū)間的最后一個(gè)解
     *
     * @return 區(qū)間的解數(shù)量
     */
    private static long iterateSubTree(int[] firstPicks, int[] lastPicks) {
        long counter = 0;
        int[] picks = firstPicks;

        do {
            if (picks != null) {
                // System.out.println(Arrays.toString(picks));
                counter++;
            }

            picks = getNextPick(picks, lastPicks);
        } while (picks != null);

        System.out.println("區(qū)間 " + Arrays.toString(lastPicks) + " 遍歷完成,共 " + counter + " 個(gè)解");
        return counter;
    }

    /**
     * 根據(jù)當(dāng)前解計(jì)算下一個(gè)解,直到遇到最終解,則返回 null
     *
     * @param picks     當(dāng)前解
     * @param lastPicks 最終解
     *
     * @return 下一個(gè)解或 null
     */
    private static int[] getNextPick(int[] picks, int[] lastPicks) {

        if (Arrays.equals(picks, lastPicks)) {
            return null;
        }

        int[] nextPick = Arrays.copyOf(picks, picks.length);
        int movable = findMovable(nextPick, lastPicks);
        nextPick[movable]++;

        if (movable != nextPick.length - 1) {
            // 將 movable 后面的點(diǎn)移回到貼緊 movable 的右側(cè)
            partialReset(nextPick, movable);
        }

        return nextPick;
    }

    // 在 picks 中尋找第一個(gè)可以右移的點(diǎn)
    private static int findMovable(int[] picks, int[] lastPicks) {

        for (int i = picks.length - 1; i >= 0; i--) {
            if (picks[i] < lastPicks[i]) {
                return i;
            }
        }

        return -1; // 實(shí)際上不會(huì)返回 -1
    }

    // 指定位置后面的點(diǎn)都移回到貼緊該位置的右側(cè)
    private static void partialReset(int[] picks, int resetStart) {
        for (int i = resetStart + 1; i < picks.length; i++) {
            picks[i] = picks[i - 1] + 1;
        }
    }

}

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/68876.html

相關(guān)文章

  • [Java并發(fā)-11] 并發(fā)容器的使用

    摘要:同步容器及其注意事項(xiàng)中的容器主要可以分為四個(gè)大類(lèi),分別是和,但并不是所有的容器都是線(xiàn)程安全的。并發(fā)容器及其注意事項(xiàng)在版本之前所謂的線(xiàn)程安全的容器,主要指的就是同步容器,當(dāng)然因?yàn)樗蟹椒ǘ加脕?lái)保證互斥,串行度太高了,性能太差了。 Java 并發(fā)包有很大一部分內(nèi)容都是關(guān)于并發(fā)容器的,因此學(xué)習(xí)和搞懂這部分的內(nèi)容很有必要。 Java 1.5 之前提供的同步容器雖然也能保證線(xiàn)程安全,但是性能很差...

    legendaryedu 評(píng)論0 收藏0
  • Java知識(shí)點(diǎn)總結(jié)

    摘要:線(xiàn)程池中的和有什么不同直接提交的隊(duì)列該功能由對(duì)象提供。若大于最大線(xiàn)程數(shù),則執(zhí)行拒絕策略。因?yàn)閷?duì)于固定大小的線(xiàn)程池來(lái)說(shuō),不存在線(xiàn)程數(shù)量的動(dòng)態(tài)變化,所以最大線(xiàn)程數(shù)等于核心線(xiàn)程數(shù)。返回核心線(xiàn)程數(shù)為,最大線(xiàn)程數(shù)為無(wú)窮大的線(xiàn)程池。 索引的實(shí)現(xiàn)方式 1、B+樹(shù) 我們經(jīng)常聽(tīng)到B+樹(shù)就是這個(gè)概念,用這個(gè)樹(shù)的目的和紅黑樹(shù)差不多,也是為了盡量保持樹(shù)的平衡,當(dāng)然紅黑樹(shù)是二叉樹(shù),但B+樹(shù)就不是二叉樹(shù)了,節(jié)點(diǎn)下...

    I_Am 評(píng)論0 收藏0
  • Java知識(shí)點(diǎn)總結(jié)

    摘要:線(xiàn)程池中的和有什么不同直接提交的隊(duì)列該功能由對(duì)象提供。若大于最大線(xiàn)程數(shù),則執(zhí)行拒絕策略。因?yàn)閷?duì)于固定大小的線(xiàn)程池來(lái)說(shuō),不存在線(xiàn)程數(shù)量的動(dòng)態(tài)變化,所以最大線(xiàn)程數(shù)等于核心線(xiàn)程數(shù)。返回核心線(xiàn)程數(shù)為,最大線(xiàn)程數(shù)為無(wú)窮大的線(xiàn)程池。 索引的實(shí)現(xiàn)方式 1、B+樹(shù) 我們經(jīng)常聽(tīng)到B+樹(shù)就是這個(gè)概念,用這個(gè)樹(shù)的目的和紅黑樹(shù)差不多,也是為了盡量保持樹(shù)的平衡,當(dāng)然紅黑樹(shù)是二叉樹(shù),但B+樹(shù)就不是二叉樹(shù)了,節(jié)點(diǎn)下...

    Lorry_Lu 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<