web-dev-qa-db-ja.com

エレベーターメカニズムのデータ構造

この質問は、企業インタビュー中に私に尋ねられました-エレベーターメカニズムの実装にどのデータ構造が効率的ですか?

多くのグーグル検索を行った後でも、効率的なデータ構造を見つけることができません。

優先キューは、それを実装するために考えることができます。優先キューは、効率的なデータ構造ですか、エレベータメカニズムを実装するためのより効率的なデータ構造ですか?

ありがとう!

21
ritesh_NITW

ソフトウェアでメカニズムを実装することはできないため(確かにmodelできますが) )、質問は Elevator algorithm に関するものだと思います。

アルゴリズムは一見シンプルに見えますが、データ構造のセットが手元にある場合でも、驚くほど実装が困難です。このアルゴリズムに使用する適切な構造は、3つの優先度キューです。

  1. 現在のポイントを過ぎたエントリがある現在の方向の場合、
  2. 逆方向
  3. 現在のポイントの前の現在の方向。

実装は最初に方向を決定し、次に要求された{from, to}値のペアを配置するキューを選択します。

29
dasblinkenlight

2つのリンクリストを使用して、1つは上方向の移動要求に使用し、もう1つは下方向の移動要求に使用するとどうなりますか。

最初のリクエスト: a)。エレベーターは0階にあり、リクエストは3階にあります。

上方に移動するためのリンクリスト。

3-> null。

下向きの移動のためのリンクリスト。

ヌル。

2番目の要求: b)。エレベーターが1階に移動し、2階から上方への移動要求があります。

上方に移動するためのリンクリスト。

2-> 3-> null

下向きの移動のためのリンクリスト。

ヌル。

番目のリクエスト: c)2人が2階に入り、1人が5階のボタンを押し、もう1人が1階のボタンを押したとします。

上方に移動するためのリンクリスト。

3-> 5-> null

注:ここで、2は上にリンクされたリストに到達したため削除されました。

下向きの移動のためのリンクリスト。

1-> null。

d)人が3階に入り、0階のボタンを押したとします

上方に移動するためのリンクリスト。

5-> null

下向きの移動のためのリンクリスト。

1-> 0-> null。

5階に達すると、リンクリストは空になるので、リンクリストは下に移動することを検討できます。

リンクリストが両方とも空の場合、エレベータは停止します。

だからリンクリストはエレベーターにとって効果的なデータ構造にもなり得ると思う

14
sacrishi

以下は、エレベーターシステムを設計する1つの方法です。各エレベータは、キュー(ブロッキングキューの場合もあります)を使用してフロアリクエストを格納します。また、すべてのエレベーターキューを監視する中央のElevatorManagerがあり、ビジネスルールに応じて特定のエレベーターにリクエストを委任できます。 ElevatorManagerの仕事は、要求を関連するエレベーターに効率的に委任することです。以下の擬似コードは委任アルゴリズムを最適化しませんが、エレベータのリストに対して実際の委任を行う方法を示しています。

エレベーターシステムに必要なクラス:

ElevatorManager [シングルトン-これは、建物内のn台のエレベーターを管理するメインエレベータープログラムです]
メンバー:
エレベーターのリスト
Floor.Requestのキュー//これは両方向のリクエストを維持します。 1つの改善は、各方向に1つずつ、2つのキューを保持することですが、複雑さが増します
MIN_FLOOR
MAX_FLOOR
操作:
delgate()
halt()//エレベータシステム全体をメンテナンスモードに設定するか、動作を停止します


エレベーター [個々のエレベーターを表します。建物にエレベーターがn台ある可能性があります]
メンバー:
Floorのキュー//これはソートする必要があるため、PriorityQueueを使用できます
Direction:Enum [方向の列挙-アップ、ダウン、待機、アイドル、メンテナンス]
CurrentFloor:フロア
操作:
operate()
moveUp()
下に移動()
openDoor()
closeDoor()
callEmergencyLine()
getDirection()
getCurrentFloor()
setInMaintenanceMode()


 [個々のフロアを表します]
メンバー:
eNum of Floors
クラスリクエスト{
currentFloor
destinationFloor
方向[上、下]
}
操作:
上がる()
降りる()

上記のコンポーネントの主要な擬似コードの一部:

class Floor {
    goUp() {
        ElevatorManager.queue.offer(new Request(currentFloor, destinationFloor, up));
    }   

    goDown() {
        ElevatorManager.queue.offer(new Request(currentFloor, destinationFloor, down));
    }
}

ElevatorManager {
    delegate() {

        // Instead of using one object, we could use a list to track idle and elevators moving in same direction so that these list could be used for next requests in queue
        // but again to simplify pseudocode, I am using single objects instead of lists
        Elevator idleElevator; // track idle elevator
        Elevator elevatorMovingInSameDirection; // elevator moving in same direction as next request in main elevator manager queue 

        while(!halt()) { //keep delegating until powered down or whole system is halted through main controls

            if(queue.peek() != null) {

                Request req = queue.peek();
                boolean startAgain = false; // flag to start from beginning if the request is already pushed to one of the elevators queue during iterating elevators

                for(Elevator elevator : elevators) {

                    // first find if there is an elevator at current floor going in same direction as current request in queue
                    if(req.currentFloor == elevator.currentFloor && req.direction == elevator.direction) {
                        elevator.queue.offer(req.destinationFloor);
                        queue.poll(); // remove this request from Elevator Manager queue

                        startAgain = true;
                        break;
                    }

                    // check if this elevator is idle
                    if(elevator.direction == "idle")) {
                        idleElevator = elevator; // For this simple design, I am ok to overwrite idle elevator value and instead get the latest idle elevatior
                    }

                    // check if this elevator is moving in desired direction and elevator's current floor is behind desired floor in queue
                    if(elevator.direction == req.direction) {

                        // Make sure elevators moving in same direction should also be behind the floor where request is made
                        if(req.direction == "Up" && req.currentFloor - elevator.currentFloor > 0) {

                            elevatorMovingInSameDirection = elevator; // Same as above, it's ok to get this overwritten and instead get the latest elevator moving in same      direction
                        }

                        // Make sure elevators moving in same direction should also be behind the floor where request is made
                        if(req.direction == "Down" && req.currentFloor - elevator.currentFloor < 0) {
                            elevatorMovingInSameDirection = elevator;
                        }
                    }

                }

                // Only delegate to other floors if you could not find elevator going in same direction at same floor from where the request was made
                if(!startAgain && idleElevator != null) {
                    idleElevator.queue.offer(req.destinationFloor);
                    queue.poll();
                }

                // if we could neither find elevator at current floor nor idle elevator then send this request to elevator behind current Floor and moving in same direction as the request
                if(!startAgain && elevatorMovingInSameDirection != null) {
                    elevatorMovingInSameDirection.queue.offer(req.destinationFloor);
                    queue.poll();
                }


            }
        }
    }
}


Elevator {

    moveUp() {
        this.currentFloor += 1;
    }

    moveDown() {
        this.currentFloor -= 1;
    }

    operate() {

        while(queue.peek() != null) {

            Floor nextFloorInQueue = queue.peek();

            while(this.currentFloor != nextFloorInQueue.request.destinationFloor) {
                if(this.direction == "Up") {
                    moveUp();
                } else if(this.direction == "down") {
                    moveDown();
                }
            }

            queue.poll(); // remove the request from queue
            open(); //open door

            Direction backUpDirection = this.direction; //back up elevators direction to retrieve it later once dooor closes
            this.direction = "idle"; // set state to idle to let elevatorManager know that requests at current floor could be offered to this elevator queue

            Thread.sleep(10000); // sleep for 10 seconds so that people can leave elevator

            close(); // once people are out close door to move to next floor in queue
            this.direction = backUpDirection;
        }

        this.direction = "idle"; // once queue is empty set the direction to idle
    }
}

Githubでも利用可能です: https://github.com/prabhash1785/Java/blob/master/ObjectOrientedDesign/src/com/prabhash/Java/design/objectoriented/elevator/ElevatorDesignWithPseudocode .md

3

簡単にするために、単一エレベーターシステムを考えてみましょう。

使用されるデータ構造:シンプルリスト floor#を保存し、enumsイベントと状態を保存します。

システムはEvent-Stateで駆動できます。ユーザーの行動や環境のあらゆる側面を考慮して、エレベーターで投げられるすべてのシナリオを決定する必要があります。

Events of the elevator : GOINGUP, GOINGDOWN, STOP 
States of the elevator : ONMOVE, WAITING (between door open and close), IDLE (serving no one), UNDERMAINTENANCE

Elevator movement is usually driven by two activites:
1. Press Up or Down key (before the entry gate of elevator) and wait for the elevator to come and serve you. (Press-And-Wait, say Paw) 
2. Enter inside the elevator and make request by pressing keys (Enter-And-Request, say EAR)

So it can said that Paw from outside and EAR from inside can decide the Hops of the elevator. But what about direction?

Two possible types of Paw: PAWup (press Up button) and PAWdown (press Down button)

Now, EAR can be any of the three types depending upon the users behavior. These are the critical challenges in deciding the algorithm: 
1.) Normal - same direction as Paw (wanted to go down and enter lower floor#) 
2.) Opposite - wanted to go down BUT enter higher floor#
3.) Indecisive - Do Nothing, no key press

Now comes all the important rules:
RULE 1: If at IDLE, use FCFS to decide between the DownList front and UpList front - whoever is oldest, serve it first to ensure less waiting time. 
RULE 2: When both lists (DownList and UpList) are empty, move elevator to IDLE state. 
RULE 3: Elevator state change GOINGUP->STOP clears that floor# from UpList. Similarly, GOINGDOWN->STOP clears that floor from DownList.
RULE 4: Absolute Zero Skipping: GOINGxxx serves as many floors in xxxList as possible. 
RULE 5: Elevator doesn't favour Opposite-EAR, and obviously can't serve Indecisive-EAR.
RULE 6: Elevator in UNDERMAINTENANCE state is shunned from all external signals.
RULE 7: In the event of Power-cuts or Fire, the elevator goes and stops at Lobby. Flooding??

To achieve RULE#5, GOINGDOWN clears all the lower floor# in DownList in ONE GO. Similarly, GOINGUP clears all the higher floor# in UpList.    

Lets discuss one scenario to clear the above concepts:
Say, an elevator is resting at floor 7 is at IDLE state, 
    DownList : 
    UpList : 

IDLE@7 - PAWdown@12 then PAWup@9 then PAWdown@13
    DownList : 12, 13 (older requests at lower index.Push new requests at front.)
    UpList : 9 
    Using RULE#2, in the above case, 
    Event: GOINGUP to Pick@12.  

WAITING@12  - 12 cleared following RULE#3

MeanWhile, PAWup@8 then PAWup@10 then PAWdown@10, so updated lists are:
    DownList : 13, 10 
    UpList : 9, 8, 10

So here, in the current situation, if the EAR is
1.) Normal, GOINGDOWN(towards new EAR) is triggered.
2.) Opposite/Indecisive, GOINGDOWN(towards 9) is triggered and add the new EAR in UpList. 

上記の規則を使用して、エレベーターは通常の作業を続けます。

0
Saurav Sahu

各配列エントリがフロアを表す配列を持つ方法はどうですか。ユーザーがフロアに立ち寄りたい場合、アレイ内のそのエントリにマークを付け、エレベーターがそのフロアに到達したときにマークを付けると、エレベータはアレイを調べてエントリをクリアします。 SCAN/CSANスケジューリングアルゴリズムに似ています。あなたのコメントを楽しみにしています

0
rgaut