エレベータの制御(基本編)
長いからとサンプルコードはしょってしまったのでいろいろと不都合がでてしまいました。 last_round は僕のデバックの都合上のものなので出力に関しては気にしないでください。 ごめんなさい。 お題を間違えてCのまま投稿してしまったので 言語設定せずに投稿します。 gcc -Wall -std=c99 doukaku225.c # ところで、これ投稿したの一昨日くらいなのですが、 公開のカウントって下書きした段階ではじまってる?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 | #include <stdio.h>
#include <stdbool.h>
#include <assert.h>
#define CARRYING_LIMIT (3)
#define CARRYING_MOVE_COST (5)
#define ELEVATOR_MOVE_COST (2)
typedef struct tagFloor
{
bool called; /* */
int floor_Nth; /* */
int carry_remain; /* */
long last_round; /* */
struct tagFloor *down; /* */
struct tagFloor *up; /* */
} FLOOR;
typedef struct tagElevatorBox
{
int carrying; /* */
int cool_time; /* */
FLOOR * now_floor; /* */
FLOOR * target_floor; /* */
} ELEVATOR;
static long round = 0;
static long round_wait_max = 0;
static long move_total = 0;
/* エレベータ内が搭載可能人数を返す */
int elevator_getCarryAvailed( const ELEVATOR *elevator )
{
return (CARRYING_LIMIT - elevator->carrying);
}
/* 目的の階に到着していれば真 */
bool elevator_isTargetFloor( const ELEVATOR *elevator)
{
if( elevator->now_floor == elevator->target_floor )
{
return true;
}
return false;
}
/* エレベータに乗車させる */
int elevator_carrying_in( ELEVATOR *elevator )
{
int avail = 0;
const int oldremain = elevator->now_floor->carry_remain;
avail = elevator_getCarryAvailed(elevator);
if( avail > 0 )
{
if( elevator->now_floor->carry_remain > avail )
{
elevator->now_floor->carry_remain -= avail;
elevator->carrying += avail;
}
else
{
elevator->carrying += elevator->now_floor->carry_remain;
elevator->now_floor->called = false;
elevator->now_floor->carry_remain = 0;
}
elevator->cool_time = CARRYING_MOVE_COST;
printf("IN(%d) [%d] → [%d] "
,elevator->now_floor->floor_Nth,oldremain, elevator->now_floor->carry_remain);
}
return 0;
}
/* エレベータから降車させる */
int elevator_carrying_out( ELEVATOR *elevator )
{
if( elevator->carrying > 0 )
{
elevator->carrying = 0;
elevator->cool_time = CARRYING_MOVE_COST;
printf("OUT ");
}
return 0;
}
/* エレベータを移動する */
int elevator_update(ELEVATOR *elevator)
{
if( elevator->cool_time > 0 )
{
elevator->cool_time --;
return 1;
}
if( elevator_isTargetFloor(elevator) == true )
{
return 0;
}
else
{
/* エレベータの移動 */
move_total ++;
assert( elevator->now_floor->floor_Nth != elevator->target_floor->floor_Nth );
if( elevator->now_floor->floor_Nth < elevator->target_floor->floor_Nth )
{
elevator->now_floor = elevator->now_floor->up;
}
else
{
elevator->now_floor = elevator->now_floor->down;
}
elevator->cool_time = ELEVATOR_MOVE_COST;
}
return 0;
}
/* 次に停止するべき階を選択する */
int floor_select( FLOOR **next, const FLOOR *floor, const long round )
{
long wait_max;
FLOOR *cursor;
*next = NULL; /* 最上階 */
wait_max = -1;
cursor = (FLOOR *)floor;
while( true )
{
if( cursor->called == true && (round - cursor->last_round) > wait_max )
{
*next = cursor;
wait_max = (round - cursor->last_round);
if( (round - cursor->last_round) > round_wait_max )
{
round_wait_max = (round - cursor->last_round);
}
}
if( cursor == cursor->down )
{
break;
}
cursor = cursor->down;
}
if( *next == NULL )
{
/* 1階ならばすべて運び終わっている */
*next = cursor;
return 1;
}
(*next)->last_round = round;
return 0;
}
/* */
int floor_print(const ELEVATOR* elevator, const FLOOR *floor)
{
printf("--------------------\n");
while( true )
{
printf(" [%d]th Floor: [%2d] / last_round:[%4ld] %c %c\n"
, floor->floor_Nth, floor->carry_remain,floor->last_round
, (floor==elevator->target_floor)?'=':' '
, (floor==elevator->now_floor)?'*':' ');
if( floor == floor->down )
{
break;
}
floor = floor->down;
}
return 0;
}
/* */
int main(int argc, char *argv[])
{
static FLOOR floor[] =
{
{false, 1, 0, 0, &floor[0], &floor[1]}
,{true, 2, 7, 0, &floor[0], &floor[2]}
,{true, 3, 3, 0, &floor[1], &floor[3]}
,{true, 4, 11, 0, &floor[2], &floor[4]}
,{true, 5, 7, 0, &floor[3], &floor[4]}
};
static FLOOR *top_floor = &floor[4];
static ELEVATOR schindler = { 0, 0, &floor[0], &floor[0] };
FLOOR *next = NULL;
/* 初期状態の出力*/
printf("INIT ");
floor_print(&schindler, top_floor);
/* 運搬開始 */
round = 0;
round_wait_max = 0;
move_total = 0;
while( true )
{
round ++;
elevator_update(&schindler);
if( schindler.cool_time > 0 )
{
continue;
}
if( elevator_isTargetFloor(&schindler) == true )
{
/* 目的の階に到着 */
if( schindler.now_floor->floor_Nth == 1 )
{
elevator_carrying_out(&schindler);
/* 次の停止回を最上階から検索する */
floor_select( &next, top_floor, round );
if( next->floor_Nth == 1 )
{
/* もうない */
break;
}
//floor_print(&schindler, top_floor);
printf("\n");
}
else
{
elevator_carrying_in(&schindler);
if( elevator_getCarryAvailed( &schindler ) > 0 )
{
/* まだ搭載できるので移動のついでに追加搭載する */
floor_select( &next, schindler.now_floor, round );
}
else
{
/* もう限界なので1階を目的地にする */
next = &floor[0];
}
//floor_print(&schindler, top_floor);
} /* end of if(1階か) */
/* 次の停止階を設定する */
schindler.target_floor = next;
} /* end of if(到着しているか) */
} /* end of while( 残っているか ) */
printf("\n\nEND ");
floor_print(&schindler, top_floor);
printf("%15s:[%ld]\n", "経過時間", round);
printf("%15s:[%ld]\n", "最大待ち時間", round_wait_max);
printf("%15s:[%ld]\n", "移動距離合計数", move_total);
return 0;
}
/* EOF */
|
Posted feedbacks - Nested
Flatten Hidden今回、エレベータの移動距離を出力するので 1Fスタートにしていただければと思います。
どっかで似た問題を見たなぁと思ってちょっと記憶をたどって探してみたら、一応見つかった。 東大の創造情報学の2008年2月の入試問題がよく似ている。 http://www.i.u-tokyo.ac.jp/edu/course/ci/admission.shtml
まさか東大の入試問題になっているとは思いませんでしたが、 プログラムの勉強をする上でわりとポピュラーなものです。 ほかにポピュラーなものとして 自動販売機の釣り銭勘定(基本ロジックの勉強によい)、 電卓(スタック操作の勉強にいい)等があるのですが、 エレベータの制御が抜けているのに気がついたので投稿しました。 ちなみに、これは本来並列処理や計算量の見積もり訓練の勉強にしようします。 GUIで作ると結構楽しいんですけどね^^;;
質問です。 END時のlast_roundの数値は何を表示していますか? 最大待ち時間 = 100 ということなので、各階の待ち時間ではないですよね。
ごめんなさい。これは僕のデバッグ用出力です。 最後に更新(乗車)したときのroundです。 あまり気にしないでください。
[4]th Floor: [ 0] / last_round:[ 229]
を「4Fに最後に訪れたラウンドが229」と解釈すると、そのあとは、乗せる(5)、3F下降(6)、下ろす(5)だけのはずなのに総経過時間(257)まであと28もかかっています。
1Fからスタートして、4Fにいる3人を1Fに下ろすラウンド数は
2*(4-1) + 5 + 2*(4-1) + 5 = 22
であっていますか?
はい、バグってました。 # しかもそのまま投稿しちゃってる^^; 予定では、目的地についたとき、時間を更新するつもりだったのですが ロジック的に、降車した段階で次の目的となる階を決定した段階でlast_roundを設定していました。 つまり、1Fから4Fまでの移動時間(6ラウンド)が空白になります。 論理的なもの、僕が想定しているものはsawat氏の考えているとおりです。 失礼しました。
Squeak Smalltalk で。
あまりよい方法を思いつかなかったので、よくあるエレベータのオーソドックスな動きのシミュレートだけしてみました。
待っている人がいる階では常に「↓」ボタンが押されていると仮定します。つまり、さらに上の階に呼ばれてそちらに向かっているエレベータを止めることはできません。
したがって、1階で人を降ろして空になった(あるいは初期状態で1階待機の)ボックスは、呼んでいる階のうち最上階に直行します。
停止階で人を乗せたボックスは1階に向かう途中、余裕があれば次の呼ばれている階に停まります。最大搭載人数に達した場合は途中で停止せず、1階に直行します。
老婆心ながら、このオーソドックスな動きだけをまずは基本編として出題されたほうが良かったのではないかな⋯と思いました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 | | 各階待ち人数 階数 最大搭乗人数 ボックス内人数 ボックス停止階
ラウンド数 移動数 完了ラウンド数 放置ラウンド数 ボックス移動 状態出力 |
各階待ち人数 := #(0 7 3 11 7).
階数 := 各階待ち人数 size.
最大搭乗人数 := 3.
ボックス内人数 := 0.
ボックス停止階 := 1.
ラウンド数 := 0.
移動数 := 0.
完了ラウンド数 := Array new: 階数 withAll: 0.
放置ラウンド数 := Array new: 階数 withAll: 0.
放置ラウンド数 at: 1 put: -1.
World findATranscript: nil.
ボックス移動 := [:行き先階 |
ラウンド数 := ラウンド数 + ((行き先階 - ボックス停止階) abs * 2) + 5.
移動数 := 移動数 + (行き先階 - ボックス停止階) abs.
(放置ラウンド数 at: 行き先階) = 0
ifTrue: [放置ラウンド数 at: 行き先階 put: ラウンド数].
行き先階 = 1 ifTrue: [ボックス停止階 := 1. ボックス内人数 := 0] ifFalse: [
| 待ち人数 搭乗者数 残り人数 |
待ち人数 := 各階待ち人数 at: 行き先階.
搭乗者数 := 待ち人数 min: 最大搭乗人数 - ボックス内人数.
残り人数 := 待ち人数 - 搭乗者数.
ボックス停止階 = 1 ifTrue: [Transcript cr].
ボックス停止階 := 行き先階.
Transcript
show: ボックス停止階; show: '階: '; show: 待ち人数; show: ' -> ';
show: (各階待ち人数 at: ボックス停止階 put: 残り人数); show: '. '.
"Transcript
show: ' (ラウンド: ', ラウンド数 printString, ', 移動数: ', 移動数 printString, ')'."
ボックス内人数 := ボックス内人数 + 搭乗者数.
残り人数 = 0 ifTrue: [完了ラウンド数 at: ボックス停止階 put: ラウンド数]]].
状態出力 := [
Transcript cr.
5 to: 1 by: -1 do: [:n |
Transcript cr; show: n; show: '階; ';
show: (各階待ち人数 at: n);
show: ' 人 / 完了ラウンド: ';
show: (完了ラウンド数 at: n)].
Transcript cr].
状態出力 value.
[ ボックス内人数 = 最大搭乗人数 ifTrue: [ボックス移動 value: 1] ifFalse: [
| 呼び出し階 |
呼び出し階 := 各階待ち人数 findLast: [:各階 | 各階 > 0].
呼び出し階 = 0
ifTrue: [ボックス移動 value: 1]
ifFalse: [ボックス移動 value: 呼び出し階]].
ボックス停止階 > 1 or: [各階待ち人数 anySatisfy: [:各階 | 各階 > 0]]
] whileTrue.
状態出力 value.
Transcript cr; show: '経過時間: '; show: ラウンド数.
"Transcript cr; show: '階別の放置時間一覧: '; show: 放置ラウンド数."
Transcript cr; show: '階別の最大放置時間: '; show: 放置ラウンド数 max.
Transcript cr; show: 'のべ移動階数: '; show: 移動数
"出力 =>
5階; 7 人 / 完了ラウンド: 0
4階; 11 人 / 完了ラウンド: 0
3階; 3 人 / 完了ラウンド: 0
2階; 7 人 / 完了ラウンド: 0
1階; 0 人 / 完了ラウンド: 0
5階: 7 -> 4.
5階: 4 -> 1.
5階: 1 -> 0. 4階: 11 -> 9.
4階: 9 -> 6.
4階: 6 -> 3.
4階: 3 -> 0.
3階: 3 -> 0.
2階: 7 -> 4.
2階: 4 -> 1.
2階: 1 -> 0.
5階; 0 人 / 完了ラウンド: 65
4階; 0 人 / 完了ラウンド: 138
3階; 0 人 / 完了ラウンド: 158
2階; 0 人 / 完了ラウンド: 202
1階; 0 人 / 完了ラウンド: 0
経過時間: 209
階別の最大放置時間: 174
のべ移動階数: 52 "
|
やっぱ、上から下へ運ぶだけのお題にしてしまえばよかったんですかねぇ。 移動距離だけ計算して。。。 このお題を投稿する時点でもなやんだのですよ。 実はこの基本すら飛ばして応用だけにしてしまうかとか。 今にして思えば、このお題の前後をお題にすればちょうどよかった気がしてきましたorz
Groovyで。
待ってる人の待ち時間を少なくなるよう勤めました。
(経過時間と移動距離合計数は無駄に増えます)
乗降を開始した時点で各階の待ち時間をリセットしています。
動きとしては以下のような感じです。
(1) 待っている人がいる最上階に移動
(2) 1人乗せる
(3) 1階降りて止まる
(4) 1人降ろす
(5) 1階にたどり着くまで、(2)~(4)を繰り返す
(6) 1階に到着したら、1人降ろす
出力結果
INIT --------------------
[5]th Floor: [ 7] / last_round:[0]
[4]th Floor: [11] / last_round:[0]
[3]th Floor: [ 3] / last_round:[0]
[2]th Floor: [ 7] / last_round:[0]
[1]th Floor: [ 0] / last_round:[0]
-------------------------
Elevator move from [1 → 5]
Person get on at [5]th Floor / Floor's member [7 → 6]
Elevator move from [5 → 4]
Person get off at [4]th Floor / Floor's member [11 → 12]
Person get on at [4]th Floor / Floor's member [12 → 11]
Elevator move from [4 → 3]
・
・(途中省略)
・
Elevator move from [2 → 1]
Person get off at [1]th Floor / Floor's member [26 → 27]
Elevator move from [1 → 2]
Person get on at [2]th Floor / Floor's member [1 → 0]
Elevator move from [2 → 1]
Person get off at [1]th Floor / Floor's member [27 → 28]
END --------------------
[5]th Floor: [ 0] / last_round:[51]
[4]th Floor: [ 0] / last_round:[46]
[3]th Floor: [ 0] / last_round:[46]
[2]th Floor: [ 0] / last_round:[46]
[1]th Floor: [28] / last_round:[0]
経過時間:[1036]
最大待ち時間:[51]
移動距離合計数:[148]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 | /** エレベータ */
class Elevator {
static final int MOVE_COST = 2 // 移動コスト
static final int GETTING_ON_COST = 5 // 乗降コスト
static final int MAX_PERSONS = 3 // 最大人数
def floors // フロア
int currentFloor // 現在のフロア
int persons // 乗っている人数
int elapsedRound = 0 // 経過ラウンド
int moved = 0 // 総移動距離
/** 階を移動する */
def moveTo(toFloor) {
int movin = Math.abs(toFloor - currentFloor)
println("Elevator move from [${currentFloor + 1} → ${toFloor + 1}]")
moved += movin
currentFloor = toFloor
elapseByMove(movin * MOVE_COST)
}
/** フロアからエレベータへ一人乗せる */
def getOn() {
if (persons < MAX_PERSONS && floors[currentFloor].persons) {
persons++
int before = floors[currentFloor].persons--
elapseByGettingOn(GETTING_ON_COST)
println("Person get on at [${currentFloor + 1}]th Floor / "
+ "Floor's member [${before} → ${floors[currentFloor].persons}]")
}
}
/** エレベータからフロアへ一人降ろす */
def getOff() {
if (persons) {
persons--
int before = floors[currentFloor].persons++
elapseByGettingOn(GETTING_ON_COST)
println("Person get off at [${currentFloor + 1}]th Floor / "
+ "Floor's member [${before} → ${floors[currentFloor].persons}]")
}
}
/** 移動によるラウンドの経過 */
def elapseByMove(round) {
floors.each{ it.elapse(round) }
elapsedRound += round
}
/** 乗降によるラウンドの経過 */
def elapseByGettingOn(round) {
floors.eachWithIndex{ f, idx ->
if (currentFloor == idx) {
f.resetRound()
} else {
f.elapse(round)
}
}
elapsedRound += round
}
/** フロア情報を出力 */
def dispFloorsInfo(){
(1..floors.size()).reverseEach{
println("[${it}]th Floor: "
+ "[${(floors[it-1].persons as String).padLeft(2)}] /"
+ " last_round:[${floors[it-1].maxWaitRound}]")
}
}
/** ラウンド情報を出力 */
def dispRoundInfo() {
println """\
経過時間:[${elapsedRound}]
最大待ち時間:[${floors*.maxWaitRound.max()}]
移動距離合計数:[${moved}]"""
}
}
/** フロア */
class Floor {
int persons // 待っている人数
int waitRound = 0 // 待っているラウンド
int maxWaitRound = 0 // 最大待ちラウンド
boolean noRound // ラウンド経過なし
/** ラウンド経過 */
def elapse(round) {
if (persons && !noRound) {
waitRound += round
maxWaitRound = [maxWaitRound, waitRound].max()
}
}
/** ラウンドリセット */
def resetRound() {
waitRound = 0
}
}
def elev = new Elevator(
floors:[
new Floor(persons: 0, noRound:true), // 1階 (ラウンド経過なし)
new Floor(persons: 7 ), // 2階
new Floor(persons: 3 ), // 3階
new Floor(persons:11 ), // 4階
new Floor(persons: 7 ) // 5階
],
currentFloor: 0,
persons : 0
)
// メイン処理
println 'INIT --------------------'
elev.dispFloorsInfo()
println '-------------------------'
// 1階以外で待っている人がいる間繰り返す
while(elev.floors.tail()*.persons.sum()) {
// 待っている人がいる最上階を探す
def nextList = []
elev.floors.tail()*.persons.eachWithIndex{ item, idx ->
nextList << [idx+1, item]
}
def topFloor = nextList.findAll{ it[1] != 0 }.last()[0]
// 上から順に一人ずつスライドさせてくる
for (i in topFloor..0) {
elev.moveTo(i)
switch (i) {
case topFloor: elev.getOn(); break
case 0: elev.getOff(); break
default : elev.getOff(); elev.getOn(); break
}
}
}
println 'END --------------------'
elev.dispFloorsInfo()
elev.dispRoundInfo()
|
主にプログラム作成の容易さについて効率化しました(笑)
総移動距離は最短になります。
階数表記は英国式(0Fが地階)です。
----実行結果----
4F[7->4] -> 0F. (Round: 26)
4F[4->1] -> 0F. (Round: 52)
4F[1->0] -> 3F[11->9] -> 0F. (Round: 83)
3F[9->6] -> 0F. (Round: 105)
3F[6->3] -> 0F. (Round: 127)
3F[3->0] -> 0F. (Round: 149)
2F[3->0] -> 0F. (Round: 167)
1F[7->4] -> 0F. (Round: 181)
1F[4->1] -> 0F. (Round: 195)
1F[1->0] -> 0F. (Round: 209)
Finish.
Total time: 209
Total move: 52
Max wait: 174
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 | function log(str, lineContinue) {
// for Rhino
java.lang.System.out.print(str);
if (!lineContinue) java.lang.System.out.println();
}
function Elevator(capacity, t1, t2) {
this.capacity = capacity;
this.t1 = t1;
this.t2 = t2;
this.init([]);
}
Elevator.prototype = {
simulate: function (data) {
this.init(data);
var c;
while (c = this.callingFrom()) {
this.upTo(c);
while (this.floor != 0) this.down();
}
this.printResult();
},
printResult: function () {
log("Finish.");
log("\tTotal time: " + this.round);
log("\tTotal move: " + this.move);
log("\tMax wait: " + Math.max.apply(null, this.waitTime));
},
init: function (data) {
this.floor = 0; // 英国式
this.count = 0;
this.round = 0;
this.move = 0;
this.waitTime = [];
for(var i=0,n=data.length+1;i<n;i++) this.waitTime[i]=0;
this.data = [0].concat(data);
},
upTo: function (f) {
this.round += this.t1 * (f - this.floor);
this.move += (f - this.floor);
this.aliveAt(f);
},
down: function () {
this.round += this.t1;
this.move++;
this.aliveAt(this.floor-1);
},
aliveAt: function (f) {
this.floor = f;
if (this.floor == 0) {
this.round += this.t2;
this.count = 0;
log("0F. (Round: " + this.round + ")");
} else {
var n = Math.min(this.capacity - this.count, this.data[f]);
if (n != 0) {
this.round += this.t2;
this.data[f] -= n;
this.count += n;
if(this.waitTime[f]==0) this.waitTime[f] = this.round;
log(f + "F[" + (this.data[f] + n) + "->" + this.data[f] + "] -> ", true);
}
}
},
callingFrom: function () {
for (var f = this.data.length - 1; f > 0; f--)
if (this.data[f] > 0) return f;
return 0;
}
}
if (arguments.length > 0) {
new Elevator(arguments.shift()*1, arguments.shift()*1, arguments.shift()*1).simulate(arguments);
} else {
new Elevator(3, 2, 5).simulate([7, 3, 11, 7]);
}
|
ども、raynstardです。 コメントにもありますが、ロジックを単純にするとエレベータの移動距離が最小限になります。
いってみれば、消費エネルギーの効率をよくしたということです。 つまり、エコですよ!(笑
そして、また、テストデータがあまかったorz 予定では、単純なロジックにすると移動距離がへるはずだったんだけどなぁ^^;;
とりあえず、サンプル通りのやつと、自分なりに最適化したのを1本づつ。 方針としては、トータルの待ち時間の最小化です。 各人が1階まで降りる時間*人数の合計が最小になるように、という方針です。
実際のところは、下の階層から優先的に運び出してしまって、上の階層は後回し、ということです。定員まで余裕があれば、更に上の階層へ寄り道、という手順になります。
サンプルプログラム(FindNext2)の手順だと、 Score =3464 Time =209 Run =52 ですが、この手順(FindNext1)ですと、 Score =2931 Time =222 Run =56 となり、若干エレベータの移動量(Run)及び所要時間(Time)が増えるものの、大幅に合計待ち時間(Score)が短縮されます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 | %!PS
% [[待ち人] 現在位置 乗員 定員 時刻 評価] Check1 [...]
/Check1 {
dup 0 get true exch { 0 eq and } forall % 待ち無し
1 index 1 get 0 eq and % 地上階
} bind def
% [[待ち人] 現在位置....] FindNext1 num
/FindNext1 {
0 2 getinterval aload
0 get length 1 sub 1 exch {
2 copy get 0 gt {
exch pop true exit
} if
pop
} for
true ne {
0
} if
} bind def
/FindNext2 {
(==) = ppstack (==)=
0 get
dup length 1 sub -1 1
{
2 copy get 0 gt {
exch pop true exit
} if
pop
} for
true ne {
0
} if
} bind def
/AddVal { % [] pos tt AddVal []
2 index 2 index get add 2 index 3 1 roll put
} bind def
% [[待ち人] 現在位置 乗員 定員 時刻 評価] /Next Move1 [...]
/Move1 {
exch
(Move: ) print dup 1 get 1 add 3 string cvs print ( ==> ) print
(==) = ppstack
dup 2 2 getinterval aload pop lt {
dup 3 -1 roll cvx exec
1 index 1 get
2 index 2 index 1 exch put
sub abs
2 copy 6 exch AddVal pop
MoveTime mul 4 exch AddVal
} {
exch pop
dup 1 get
2 copy 6 exch AddVal pop
MoveTime mul 4 exch AddVal
dup 1 0 put
} ifelse
dup 1 get 1 add 3 string cvs =
dup ===
} bind def
/RideOn {
% [[] ... ] n RideOn [[] ...]
1 index 0 get 2 index 1 get 2 index neg
AddVal pop
2 exch AddVal
} bind def
/IO1 {
dup 1 get 0 eq {
(Floor 1 =[Out]= ) print dup 2 get =
dup 2 get 0 gt {
4 IOTime AddVal
dup 2 get exch dup 4 get 3 -1 roll mul
5 exch AddVal
dup 2 0 put
}if
dup ===
} {
(Floor ) print dup 1 get 1 add 3 string cvs print ( =[In]= ) print
dup 1 get exch dup 0 get 3 -1 roll get
exch dup 2 get exch dup 3 get 3 -1 roll sub
3 copy 3 -1 roll lt {
pop 3 -1 roll pop
} {
pop pop exch
} ifelse
dup 0 gt {
dup =
RideOn
4 IOTime AddVal
} {
(noop) =
} ifelse
} ifelse
} bind def
/Run {
exch
{
Check1 { exit } if
1 index Move1
IO1
} loop
(Score =) print dup 5 get =
(Time =) print dup 4 get =
(Run =) print dup 6 get =
pop pop
} bind def
% ===== Test Code =====
/MoveTime 2 def
/IOTime 5 def
[[0 7 3 11 7] 0 0 3 0 0 0] /FindNext2 Run
[[0 7 3 11 7] 0 0 3 0 0 0] /FindNext1 Run
|
Haskellでやってみました。
全ての可能性を試して最小稼働時間で全員を1階に運ぶようにしています。一番単純なので…あと、一番長く放置されていた階の待ち時間まで手が回ってません。
出力はかなりいい加減です:
(209," +1 *3 -1 / +1 *3 -1 / +2 *3 -2 / +3 *3 -3 / +3 *3 -3 / +3 *3 -3 / +3 *2 +
1 *1 -4 / +4 *3 -4 / +4 *3 -4 / +1 *1 -1 /")
最初の数字はラウンド数、次の文字列はエレベーターのシークエンスで、+、-はそれぞれ相対移動階数、*はその階で乗った人の人数、/は1階で全員降りたしるしです…つまり、"+1 *3 -1 /"は(1階から)一つ上に上がって、3人乗って、ひとつ下に降りて、(一階で)全員おろしたということになります…
モナドコンビネータはパーサーだけじゃなくてこうゆう問題をやるのにも使えますね…
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 | module Main where
import Data.Array.Unboxed (UArray, listArray, assocs, (//), (!))
import Control.Monad.State
import Data.Function (on)
import Data.List (minimumBy)
import Data.Ord (compare)
-- Data Types --
data Elevator = E {
cPassenger :: Int,
iFloor :: Int
} deriving Show
data Wait = W {
unW :: (UArray Int Int)
} deriving Show
type Cost = (Int, String)
type ElevState = (Cost, (Elevator, Wait))
type ElevM = State (Elevator, Wait) Cost
-- Data --
initialWait = W $ listArray (1, 5) [0, 7, 3, 11, 7]
initialElev = E 0 1
initial = ((0, ""), (initialElev, initialWait))
-- Primitives --
isFull :: Elevator -> Bool
isFull (E p fl) = p == 3
comb :: Cost -> Cost -> Cost
comb (c1, st1) (c2, st2) = (c1 + c2, st1 ++ " " ++ st2)
waitingFloor :: Wait -> [Int]
waitingFloor = map (fst) . filter ((/=0).snd) . assocs . unW
-- (Quasi) Monadic Combinators --
combM :: Cost -> ElevM -> ElevM
combM c m = m >>= \c' -> return $ c `comb` c'
(<*>) :: ElevM -> ElevM -> ElevM
a1 <*> a2 = a1 >>= \c1 -> combM c1 a2
ifFull :: ElevM -> Cost -> ElevM
ifFull ifM c = State $ \i@(e, w) -> case (isFull e) of
False -> (c, i)
otherwise -> runState (combM c ifM) i
ifFullM :: ElevM -> ElevM -> ElevM
ifFullM pM tM = pM >>= \c -> ifFull tM c
-- Monadic Primitive Elevator Operations --
pickupM :: ElevM
pickupM = State $ _pickup
_pickup (E p fl, W ar) = ((5, str), (E p' fl, W ar'))
where
ar' = ar//[(fl, cWaiting - cPickup)]
p' = p + cPickup
cWaiting = ar!fl
cPickup = min cWaiting (3 - p)
str = '*' : (show cPickup)
dropM :: ElevM
dropM = State $ \(E p fl, w) -> case (p) of
0 -> ((0, ""), (E 0 fl, w))
otherwise -> ((5, "/"), (E 0 fl, w))
moveByM :: Int -> ElevM
moveByM d = State $ \((E p fl), w) -> (c, (E p $ fl + d, w))
where
c = (abs $ d * 2, cat d)
cat d
| d == 0 = ""
| d < 0 = show d
| otherwise = '+' : (show d)
-- Composit Monadic Operations --
takeHomeM :: ElevM
takeHomeM = State $ \i@((E _ fl), _) -> (runState $ (moveByM $ 1 - fl) <*> dropM) i
goM :: Int -> ElevM
goM flTo = State $ \i@((E _ fl), _) -> (runState $ moveByM $ flTo - fl) i
turnM :: Int -> ElevM
turnM flTo = ((goM flTo) <*> pickupM) `ifFullM` takeHomeM
-- Permutation Traversers --
tryAll :: ElevState -> [ElevState]
tryAll (c, i@(_, w))
| fls == [] = [runState (combM c takeHomeM) i]
| otherwise = map (\fl -> runState (combM c $ turnM fl) i) fls >>= tryAll
where
fls = waitingFloor w
-- Main --
main = (print.fst . minimumBy (byCost) . tryAll) initial
where
byCost :: ElevState -> ElevState -> Ordering
byCost = compare `on` (fst . fst)
|
Haskellでやったんですよー言語タグをつけるの忘れました…
1 2 3 | module Main where
main = putStrLn "see my previous post"
|
#8319と同様の方針で。 経過時間が最小になっています(たぶん)。 [INIT] 1階: 7人 2階: 11人 3階: 3人 4階: 7人 5階: 0人 -------------------- 5階 [7 => 4] => 1階 5階 [4 => 1] => 1階 5階 [1 => 0] => 4階 [11 => 9] => 1階 4階 [9 => 6] => 1階 4階 [6 => 3] => 1階 4階 [3 => 0] => 1階 3階 [3 => 0] => 1階 2階 [7 => 4] => 1階 2階 [4 => 1] => 1階 2階 [1 => 0] => 1階 -------------------- [END] 1階: 0人 2階: 0人 3階: 0人 4階: 0人 5階: 28人 -------------------- [RESULT] 経過時間: 209 最大待ち時間: 174 移動距離: 52
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 | class Elevator
CAPACITY = 3
MOVE_COST = 2
GETTING_ON_COST = 5
attr_reader :current_floor, :number_of_people, :total_round, :travel_distance
def initialize
@current_floor = 0 # 現在の階
@number_of_people = 0 # 乗っている人数
@total_round = 0 # 現在の合計ラウンド
@travel_distance = 0 # 現在の総移動距離
end
def move_to(floor)
distance = (@current_floor - floor).abs
@total_round += MOVE_COST * distance
@travel_distance += distance
@current_floor = floor
end
def get_on(n)
raise if (@number_of_people + n) > CAPACITY
@total_round += GETTING_ON_COST
@number_of_people += n
end
def get_off(n)
raise if @number_of_people < n
@total_round += GETTING_ON_COST
@number_of_people -= n
end
def room_for_getting_on
CAPACITY - @number_of_people
end
end
class Building
def initialize(floors)
@floors = floors # 各階の人数(階数は0から)
@elevator = Elevator.new
@first_visited_round = [] # 各階に初めて止まったときのラウンド
@floors.size.times { @first_visited_round << nil }
@first_visited_round[0] = 0
end
def get_on_to_evevator
n = (@floors[@elevator.current_floor] <= @elevator.room_for_getting_on) ? @floors[@elevator.current_floor] : @elevator.room_for_getting_on
@elevator.get_on(n)
@floors[@elevator.current_floor] -= n
end
def get_off_from_elevator
n = @elevator.number_of_people
@elevator.get_off(n)
@floors[@elevator.current_floor] += n
end
def finished?
@floors[1..@floors.size].inject(0) { |s, a| s + a } == 0
end
def where_to_go
floor = nil
(@floors.size - 1).downto(0) { |i|
if @floors[i] > 0
floor = i
break
end
}
raise if floor.nil?
floor
end
def simulate
until finished?
floor = where_to_go
prev = @floors[floor]
@elevator.move_to(floor)
get_on_to_evevator
print "#{floor + 1}階 [#{prev} => #{@floors[floor]}] => "
@first_visited_round[floor] = @elevator.total_round if @first_visited_round[floor].nil?
if (@elevator.number_of_people == Elevator::CAPACITY) || (@elevator.current_floor == 1)
@elevator.move_to(0)
get_off_from_elevator
puts "1階"
end
end
end
def get_total_round
@elevator.total_round
end
def get_travel_distance
@elevator.travel_distance
end
def get_max_waiting_round
@first_visited_round.max
end
def get_floors_status
status = ''
@floors.reverse.each_with_index { |n, i| status << "#{i + 1}階:\t#{n}人\n" }
status
end
end
b = Building.new([0, 7, 3, 11, 7]) # 1階 0人, 2階 7人, 3階 3人, 4階 11人, 5階 7人
puts '[INIT]'
puts b.get_floors_status
puts '-' * 20
b.simulate
puts '-' * 20
puts '[END]'
puts b.get_floors_status
puts '-' * 20
puts '[RESULT]'
puts "経過時間:\t#{b.get_total_round}"
puts "最大待ち時間:\t#{b.get_max_waiting_round}"
puts "移動距離:\t#{b.get_travel_distance}"
|





raynstard
#8289()
Rating-3/3=-1.00
エレベータを制御して、5階建てのビルの各階にいる人たちを、 「効率よく」1階のエントランスまで運んでください。 作成時の条件は次の通りです。 1. 各階において、人が残っていることはわかるが、 あと何人残っているかまではわからないものとする。 # 人が残っているところは常に呼び出しのボタンが押された状態を # 仮定して問題ありません。 2. 稼働しているエレベータは1機のみとする 3. 降車するたびに、変化がわかるような出力をしてください。 4. すべての人を運搬終わったら、最後に次の情報を出力してください。 - 運搬を始めてからの経過時間(ラウンド数) - 一番長く放置されていた階の待ち時間(ラウンド数) - エレベータの移動距離合計数 エレベータの機能は次の通りです。 - 搭載人数は最大3人までとする。 - 移動には、1つの階につき「2」ラウンドかかる。 - 人の乗降には、1回につき「5」ラウンドかかる。 ※ 「ラウンド」は包括された時間だと思ってください。 単純に「秒」と読み替えてもよいです 各階の人数は次の通りです。 5階 7人 4階 11人 3階 3人 2階 7人 1階 0人 冒頭では、「効率よく」なんて曖昧な表現をしようしましたが、 何について効率よくしたのか、設計についての見解を コメントしていただけるとうれしいです。 たとえば、 下記のサンプル出力では、各階の待ち時間を最小とすることで 効率よいとしました。(利用者の視点) ぱっと思いつくところでは、ほかにも2-3種類あるとおもいます。 さて、あなたならどう書く?(笑 // 自分で作ったやつは250L位になってしまいましたorz INIT -------------------- [5]th Floor: [ 7] / last_round:[ 0] [4]th Floor: [11] / last_round:[ 0] [3]th Floor: [ 3] / last_round:[ 0] [2]th Floor: [ 7] / last_round:[ 0] [1]th Floor: [ 0] / last_round:[ 0] IN(5) [7] → [4] OUT IN(4) [11] → [8] OUT IN(3) [3] → [0] OUT IN(2) [7] → [4] OUT IN(5) [4] → [1] OUT IN(4) [8] → [5] OUT IN(2) [4] → [1] OUT IN(5) [1] → [0] IN(4) [5] → [3] OUT IN(2) [1] → [0] OUT IN(4) [3] → [0] OUT END -------------------- [5]th Floor: [ 0] / last_round:[ 174] [4]th Floor: [ 0] / last_round:[ 229] [3]th Floor: [ 0] / last_round:[ 58] [2]th Floor: [ 0] / last_round:[ 213] [1]th Floor: [ 0] / last_round:[ 0] 経過時間:[257] 最大待ち時間:[100] 移動距離合計数:[52]1 reply [ reply ]