この質問は、企業インタビュー中に私に尋ねられました-エレベーターメカニズムの実装にどのデータ構造が効率的ですか?
多くのグーグル検索を行った後でも、効率的なデータ構造を見つけることができません。
優先キューは、それを実装するために考えることができます。優先キューは、効率的なデータ構造ですか、エレベータメカニズムを実装するためのより効率的なデータ構造ですか?
ありがとう!
ソフトウェアでメカニズムを実装することはできないため(確かにmodelできますが) )、質問は Elevator algorithm に関するものだと思います。
アルゴリズムは一見シンプルに見えますが、データ構造のセットが手元にある場合でも、驚くほど実装が困難です。このアルゴリズムに使用する適切な構造は、3つの優先度キューです。
実装は最初に方向を決定し、次に要求された{from, to}
値のペアを配置するキューを選択します。
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階に達すると、リンクリストは空になるので、リンクリストは下に移動することを検討できます。
リンクリストが両方とも空の場合、エレベータは停止します。
だからリンクリストはエレベーターにとって効果的なデータ構造にもなり得ると思う
以下は、エレベーターシステムを設計する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
}
}
簡単にするために、単一エレベーターシステムを考えてみましょう。
使用されるデータ構造:シンプルリスト 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.
上記の規則を使用して、エレベーターは通常の作業を続けます。
各配列エントリがフロアを表す配列を持つ方法はどうですか。ユーザーがフロアに立ち寄りたい場合、アレイ内のそのエントリにマークを付け、エレベーターがそのフロアに到達したときにマークを付けると、エレベータはアレイを調べてエントリをクリアします。 SCAN/CSANスケジューリングアルゴリズムに似ています。あなたのコメントを楽しみにしています