窩窩電梯優化

電梯優化奇技淫巧

多線並行,多策略競爭

此處 特地感謝 lsj 霸霸提出的這一神奇思路,我只是把這個想法變爲現實。java

很慚愧,只作了一點一點微小的工做(端起茶杯)算法

1、提出背景

已知的各類算法都有各自的侷限性

  1. ALS 在處理大多數狀況的時候效果明顯差於 Look ,可是ALS,在面對安全

    1-FROM-1-TO-3性能

    2-FROM--2-TO-3優化

    3-FROM-10-TO-15ui

    這種狀況的時候,反而會由於它 先來先選 的沙雕策略比 Look 折返路程更少。this

  2. 貪心算法在面對精心構造的數據的時候可能會被來回 放風箏 這是 墜痛苦的spa

防止互測被卡TMax

指導書最開始並無禁止互測經過針對對方算法構造一個極具針對性的數據點。線程

我的見解是,針對一個多數狀況下較優的算法,經過一個很極端的例子,去卡爆對方性能,獲取hack分數code

是有點缺德的。同時這個所謂的Bug還難以修復。

開發這個多策略的 初心 是防止本身的程序出如今極端狀況下慢於純ALS的狀況。

前三次的經驗積累

第二次和第三次做業當中,個人一些優化算法,可能在某些狀況下出現負優化,因而我須要根據判斷

優化先後的字符串長度取捨本次的優化。 因而lsj julao提出能不能在此處根據時間取捨優化,並初步提出了

多電梯並行的操做。

2、實現

大致思路

多策略能夠理解爲一種在 算法選擇 層面的貪心,總體思路以下

  • 開多個電梯線程,這些電梯線程不一樣於第三次做業的合做關係,他們不只算法不一樣,且他們之間的運行是徹底獨立的。
  • 主線程獲取新的請求,把這個請求給每一個電梯線程各分配一次。
  • 各個線程把本身的輸出 重定向 到一個緩衝區中,這裏我用的是官方輸出接口自帶的一個指定PrintStream的重載,把輸出重定向到一個ByteArrayStream 當中。
  • 設置設置一個輸出類,這個輸出類有一個靜態鎖,用於保證全局輸出的線程安全。最早執行完全部請求的線程得到這個靜態鎖,把本身緩衝區內的數據所有輸出出來,並經過一些標記之類的東西阻止其餘線程也進行輸出。 以後直接exit(0)用最暴力的方式結束程序。

部分源碼

主線程

public class ElevatorSystem {
    public static void main(String[] args) {
        GlobalPermission permission = new GlobalPermission();

        Rail rail0 = new Rail(16,3);
        Rail rail1 = new Rail(16,3);
        Rail rail2 = new Rail(16,3);

        Dispatcher dispatcher0 = new Dispatcher(rail0,permission);
        Dispatcher dispatcher1 = new Dispatcher(rail1,permission);
        Dispatcher dispatcher2 = new Dispatcher(rail2,permission);

        dispatcher0.addSillyElevator();
        dispatcher1.addLookElevator();
        dispatcher2.addAlsElevator();

        ArrayList<Elevator> elevators0 = dispatcher0.getElevators();
        ArrayList<Elevator> elevators1 = dispatcher1.getElevators();
        ArrayList<Elevator> elevators2 = dispatcher2.getElevators();
        TimableOutput.initStartTimestamp();
        permission.systemStart();
        Thread elevator0 = new Thread(elevators0.get(0));
        Thread elevator1 = new Thread(elevators1.get(0));
        Thread elevator2 = new Thread(elevators2.get(0));
        elevator0.start();
        elevator1.start();
        elevator2.start();

        ElevatorInput elevatorInput = new ElevatorInput(System.in);

        while (true) {
            PersonRequest request = elevatorInput.nextPersonRequest();
            if (request == null) {
                permission.systemQuit();
                try {
                    elevatorInput.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
                break;
            }
            try {
                // 每拿到一個新的請求,把這個請求給每一個電梯各分配一次
                dispatcher0.getBuffer().put(request);
                dispatcher1.getBuffer().put(request);
                dispatcher2.getBuffer().put(request);

            } catch (Exception e) {
                e.printStackTrace();
            }
        }

    }
}

輸出輔助類

我把緩衝區以及最後的帶鎖輸出方法封裝到了一個OutputHelper類中,每一個電梯都自帶一個OutputHelper對象

public class OutputHelper {
    private static boolean hasOuput = false;
    private ByteArrayOutputStream storeStream = new ByteArrayOutputStream();
    private PrintStream stream = new PrintStream(storeStream);

    public static synchronized void output(OutputHelper helper) {
        if (!hasOuput) {
            helper.finalOutput();
            hasOuput = true;
        }
    }

    public void println(String string) {
        TimableOutput.println(string,stream);
    }

    public void finalOutput() {
        Scanner scanner = new Scanner(storeStream.toString());
        while (scanner.hasNextLine()) {
            System.out.println(scanner.nextLine());
        }
    }
}

電梯運行線程

public void run() {
        dispatcher.updateRequests();
        while (this.hasMoreWork()) {
            dispatcher.updateRequests();
            ElevatorOrder order = getOrderAndUpdateBuffer();
            dispatcher.updateRequests();
            executeOrder(order);
        }
        // 任務執行完畢,開始輸出
        OutputHelper.output(helper); // 輸出!
        System.err.println(scheduler); // 輸出「優勝者」的信息,便於統計算法性能
        System.exit(0); // 暴力關程序
    }

強測運行效果

根據本人利用err流輸出的統計信息,在強測數據中,本人的高分數據點(95+)的最優策略包含了本人使用的三種算法,可見這種多策略對性能的提高是很大的。

固然,在這裏我必須得膜那些用單策略可是性能分高於個人dalao,三個算法比不過對方一個算法,哭哭。

方法兩個優點

  1. 較爲簡單地大幅度提升性能
  2. 能夠規避掉本身某一算法中出現瘋狂懟門、電梯摸魚的Bug。 由於這種狀況,電梯必定沒法完成任務,那麼他們必定沒法輸出,那麼正確執行的電梯就會掩蓋這一錯誤。

3、更多的拓展(過後孔明)

動態地改變所採用的算法

此方法出自shh,可是他摸了,沒去實現,哭哭。

這裏大概講一下shh的思路。

咱們以前所說的多策略都是每一個線程給定一個算法,而後取最優。

若是遇到一下這種狀況,這個方法的效果可能會大打折扣

  • 請求具備明顯的階段性
  • 不一樣階段的請求的特徵明顯適合不一樣的算法

那麼這樣子看來,咱們的多策略仍是不夠img

因而,shh提出了根據每固定執行步數給各個算法打個分,而後調整算法的操做。

(反正我是不會實現的,哭哭)

加入評測機檢驗本身的輸出

前文咱們提到,多策略在其中一個線程由於bug而陷入長時間沒法運行的狀態時,通常不會輸出錯誤。

由於那個bug線程一直摸魚,沒法輸出,就會使得正確執行的線程能夠輸出。

那麼,這就帶來另外一個問題 若是一個線程由於Bug,執行錯誤地線程最早結束,那麼輸出將會是錯誤的。

這裏,咱們就能夠把本身評測機的評測模塊加進來,對咱們本身的輸出結果進行檢驗。

若是是正確的輸出,則正常輸出並結束程序,若是不是正確數據則等待下一個能夠輸出的線程,直到等到第一個正確的輸出。

此外若是擔憂本身電梯出現異常的問題,能夠try-catch一下,若是有異常就失去輸出的權力。

這樣,就能夠大幅度下降本身程序出錯的機率~

用於統計各個算法的執行效果

在第三次做業中,我須要統計爲三部電梯選擇合適的控制算法。

因而我經過根據三部電梯的任務特徵生成針對性數據,經過運行多策略電梯統計各個策略的效果。

最後根據統計結果爲三部電梯的調度策略作出調整。

更多sao東西,還未發現,哭哭

相關文章
相關標籤/搜索