顧客ベースのサービス所要時間を許可する
旅程の所要時間を最適化するために、ジョブの数ではなく担当する顧客の数に焦点を当てて旅程を最適化します。
たとえば、郵便配達員がパーセルや手紙を顧客に配達する郵便サービスのユースケースを考えてみましょう。このユースケースの中には、配送業者が特定の場所に商品 (荷物または手紙) を配達または集荷する場合があります。Tour Planningの場合、このような配達は、特定の時間枠が割り当てられた単一のジョブを表します。ただし、実際のビジネスシナリオでは、単一の顧客または世帯に複数の手紙やパーセルが配達される場合もあります。
HERE Tour Planning APIでは、特定の問題構成がなければ、郵便配達員が一度に複数の荷物を1人の顧客に配達できる場合でも各ジョブを独立して扱います。これにより、前の例の場合ではツアーの所要時間が長くなる可能性があります。ツアーの所要時間が長くなると、郵便配達員のシフト内に収まるジョブの数が減ったり、配達需要を満たすためにより多くのリソースが必要になったりするなど、効率性が低下する可能性があります。
複数の配達または集荷のシナリオを考慮してツアーの所要時間を最適化する
複数の配達または複数の集荷のシナリオにおける旅程の所要時間の長期化によって生じる潜在的な非効率を軽減するために、houseKeyIdプロパティを使用して、集荷タスクまたは配達タスクの数に関係なく、特定の顧客の停車地での特定のサービスの所要時間を適用できます。
注
これはアルファ機能 (新規またはテスト段階であり、現在開発中) です。アルファ機能は、テストおよびフィードバックの目的で提供されています。これらは大幅に変更されたり、一般に入手できなくなったりする可能性があります。
詳細については、「テスト段階の機能の詳細」を参照してください。
次のスニペットは、プロパティをplanオブジェクトの一部としてproblem仕様に組み込む方法を示したものです。
{
"plan": {
"jobs": [
{
"id": "job1",
"tasks": {
"deliveries": [
{
"places": [
{
"location": {
"lat": 52.5622847,
"lng": 13.4023099
},
"duration": 180,
"houseKeyId": "my_house_key_id_1"
}
],
"demand": [
1
]
}
]
}
},
(...) // Remaining parts of the problem omitted for brevity同じ場所でhouseKeyIdが一致するジョブの場合、その場所で費やした合計時間は、それらのジョブの中で最長の所要時間と等しくなります。たとえば、同じ場所にあり、houseKeyId値が一致する次のジョブがあるとします。
| JobId | 所要時間 (秒) | houseKeyId |
|---|---|---|
Job_1 | 300 | flat_1 |
Job_2 | 300 | flat_1 |
Job_3 | 600 | flat_1 |
これらのジョブの合計所要時間は600に等しく、これはこれらのジョブの中で最も長い所要時間値です。このアプローチを使用すると、ツアーの総所要時間を短縮し、リソースを最大限の効率で稼働させることができます。
以降のセクションでは、実際の例を通じてhouseKeyIdの使用法を紹介します。
顧客ベースのサービス所要時間がある場合とない場合の配達シナリオを比較する
次の比較は、問題仕様にhouseKeyIdを含めることで、配達時間を短縮してツアーをより効率化できる仕組みを示しています。
問題#1:houseKeyIdなし
このサンプルユースケースの出発地となる問題は、単独車両の運行管理に割り当てられた7つのジョブで構成されています。各ジョブは手紙の配達 (所要時間300秒) またはパーセルの配達 (所要時間600秒) のいずれかです。これらのジョブの処理に割り当てられた車両は、2時間という厳しい時間枠内ですべての割り当てを完了する必要があります。
次のJSONスニペットには問題の完全な仕様が含まれています。
Click to expand/collapse the sample JSON
{
"fleet": {
"types": [
{
"id": "small",
"profile": "car",
"costs": {
"fixed": 20,
"distance": 0,
"time": 0.005
},
"shifts": [
{
"start": {
"time": "2023-05-28T08:00:00Z",
"location": {
"lat": 52.50935,
"lng": 13.41997
}
},
"end": {
"time": "2023-05-28T10:00:00Z",
"location": {
"lat": 52.50935,
"lng": 13.41997
}
}
}
],
"capacity": [
100
],
"amount": 1
}
],
"profiles": [
{
"type": "car",
"name": "car"
}
]
},
"plan": {
"jobs": [
{
"id": "Job_1",
"tasks": {
"deliveries": [
{
"places": [
{
"location": {
"lat": 52.56182,
"lng": 13.497167
},
"duration": 300,
"tag": "letter_delivery"
}
],
"demand": [
1
]
}
]
}
},
{
"id": "Job_2",
"tasks": {
"deliveries": [
{
"places": [
{
"location": {
"lat": 52.534553,
"lng": 13.519429
},
"duration": 600,
"tag": "parcel_delivery"
}
],
"demand": [
1
]
}
]
}
},
{
"id": "Job_3",
"tasks": {
"deliveries": [
{
"places": [
{
"location": {
"lat": 52.463341,
"lng": 13.49061
},
"duration": 300,
"tag": "letter_delivery"
}
],
"demand": [
1
]
}
]
}
},
{
"id": "Job_4",
"tasks": {
"deliveries": [
{
"places": [
{
"location": {
"lat": 52.463341,
"lng": 13.49061
},
"duration": 600,
"tag": "parcel_delivery"
}
],
"demand": [
1
]
}
]
}
},
{
"id": "Job_5",
"tasks": {
"deliveries": [
{
"places": [
{
"location": {
"lat": 52.463341,
"lng": 13.49061
},
"duration": 300,
"tag": "letter_delivery"
}
],
"demand": [
1
]
}
]
}
},
{
"id": "Job_6",
"tasks": {
"deliveries": [
{
"places": [
{
"location": {
"lat": 52.463341,
"lng": 13.49061
},
"duration": 300,
"tag": "letter_delivery"
}
],
"demand": [
1
]
}
]
}
},
{
"id": "Job_7",
"tasks": {
"deliveries": [
{
"places": [
{
"location": {
"lat": 52.447476,
"lng": 13.433062
},
"duration": 600,
"tag": "parcel_delivery"
}
],
"demand": [
1
]
}
]
}
}
]
}
}プランによれば、3から6までのIDがあるジョブは同じ場所で処理され、配達は2つの別々の世帯 (C1とC2) に分かれています。
ソリューション
次のソリューションは、割り当てられた時間枠内にツアーを完了したものの、割り当てられないジョブが1つ発生したことを示しています。このジョブを割り当てると、シフトの時間枠の制約に違反することになります。次の図はツアーの内訳を視覚的に表したものです。
このケースでは、場所Cに2つの別々の世帯が存在しても、ツアーの所要時間には影響しません。この内訳は、最適化アルゴリズムが場所Cの各ジョブを別に処理し、各ジョブに全所要時間を割り当てていることを示しています。ただし、世帯C1のID3とID4のジョブと、世帯C2のID5とID6のジョブは同時に完了できた可能性があります。
その結果、ツアーの所要時間が長くなったことでジョブ7が割り当てられず、1つのツアー内で完了するジョブの数を減らすために追加のリソースを割り当てる必要があるなどの非効率性が生じる可能性があります。
次のスニペットは完全なソリューションJSONを示します。
Click to expand/collapse the sample JSON
{
"statistic": {
"cost": 49.54,
"distance": 32750,
"duration": 5908,
"times": {
"driving": 3508,
"serving": 2400,
"waiting": 0,
"stopping": 0,
"break": 0
}
},
"tours": [
{
"vehicleId": "small_1",
"typeId": "small",
"stops": [
{
"time": {
"arrival": "2023-05-28T08:00:00Z",
"departure": "2023-05-28T08:00:00Z"
},
"load": [
6
],
"activities": [
{
"jobId": "departure",
"type": "departure",
"location": {
"lat": 52.50935,
"lng": 13.41997
},
"time": {
"start": "2023-05-28T08:00:00Z",
"end": "2023-05-28T08:00:00Z"
}
}
],
"location": {
"lat": 52.50935,
"lng": 13.41997
},
"distance": 0
},
{
"time": {
"arrival": "2023-05-28T08:19:39Z",
"departure": "2023-05-28T08:24:39Z"
},
"load": [
5
],
"activities": [
{
"jobId": "Job_1",
"type": "delivery",
"jobTag": "letter_delivery",
"location": {
"lat": 52.56182,
"lng": 13.497167
},
"time": {
"start": "2023-05-28T08:19:39Z",
"end": "2023-05-28T08:24:39Z"
}
}
],
"location": {
"lat": 52.56182,
"lng": 13.497167
},
"distance": 9971
},
{
"time": {
"arrival": "2023-05-28T08:31:16Z",
"departure": "2023-05-28T08:41:16Z"
},
"load": [
4
],
"activities": [
{
"jobId": "Job_2",
"type": "delivery",
"jobTag": "parcel_delivery",
"location": {
"lat": 52.534553,
"lng": 13.519429
},
"time": {
"start": "2023-05-28T08:31:16Z",
"end": "2023-05-28T08:41:16Z"
}
}
],
"location": {
"lat": 52.534553,
"lng": 13.519429
},
"distance": 13720
},
{
"time": {
"arrival": "2023-05-28T08:58:44Z",
"departure": "2023-05-28T09:23:44Z"
},
"load": [
0
],
"activities": [
{
"jobId": "Job_4_C1",
"type": "delivery",
"jobTag": "parcel_delivery",
"location": {
"lat": 52.463341,
"lng": 13.49061
},
"time": {
"start": "2023-05-28T08:58:44Z",
"end": "2023-05-28T09:08:44Z"
}
},
{
"jobId": "Job_3_C1",
"type": "delivery",
"jobTag": "letter_delivery",
"location": {
"lat": 52.463341,
"lng": 13.49061
},
"time": {
"start": "2023-05-28T09:08:44Z",
"end": "2023-05-28T09:13:44Z"
}
},
{
"jobId": "Job_6_C2",
"type": "delivery",
"jobTag": "letter_delivery",
"location": {
"lat": 52.463341,
"lng": 13.49061
},
"time": {
"start": "2023-05-28T09:13:44Z",
"end": "2023-05-28T09:18:44Z"
}
},
{
"jobId": "Job_5_C2",
"type": "delivery",
"jobTag": "letter_delivery",
"location": {
"lat": 52.463341,
"lng": 13.49061
},
"time": {
"start": "2023-05-28T09:18:44Z",
"end": "2023-05-28T09:23:44Z"
}
}
],
"location": {
"lat": 52.463341,
"lng": 13.49061
},
"distance": 24124
},
{
"time": {
"arrival": "2023-05-28T09:38:28Z",
"departure": "2023-05-28T09:38:28Z"
},
"load": [
0
],
"activities": [
{
"jobId": "arrival",
"type": "arrival",
"location": {
"lat": 52.50935,
"lng": 13.41997
},
"time": {
"start": "2023-05-28T09:38:28Z",
"end": "2023-05-28T09:38:28Z"
}
}
],
"location": {
"lat": 52.50935,
"lng": 13.41997
},
"distance": 32750
}
],
"statistic": {
"cost": 49.54,
"distance": 32750,
"duration": 5908,
"times": {
"driving": 3508,
"serving": 2400,
"waiting": 0,
"stopping": 0,
"break": 0
}
},
"shiftIndex": 0
}
],
"unassigned": [
{
"jobId": "Job_7",
"reasons": [
{
"code": "TIME_WINDOW_CONSTRAINT",
"description": "cannot be assigned due to violation of time window",
"details": [
{
"vehicleId": "small_1",
"shiftIndex": 0
}
]
}
]
}
]
}次の図はツアーを地図上に視覚化したものです。
前の図では、停車地3でのarrival時間とdeparture時間により、停車地でのジョブの合計所要時間が25分であるとわかります。
問題#2:houseKeyIdを使用してツアーの所要時間を最適化する
前のツアーの所要時間を最適化するには、場所CのジョブにhouseKeyId値を割り当てます。各値はその場所の世帯に対応しています。これにより、ジョブの数に関係なく、同じhouseKeyId値を持つすべてのジョブの合計所要時間として、最も時間がかかるジョブの所要時間のみが取得されるようになります。
次のスニペットは更新された問題仕様を示したものです。
Click to expand/collapse the sample JSON
{
"fleet": {
"types": [
{
"id": "small",
"profile": "car",
"costs": {
"fixed": 20,
"distance": 0,
"time": 0.005
},
"shifts": [
{
"start": {
"time": "2023-05-28T08:00:00Z",
"location": {
"lat": 52.50935,
"lng": 13.41997
}
},
"end": {
"time": "2023-05-28T10:00:00Z",
"location": {
"lat": 52.50935,
"lng": 13.41997
}
}
}
],
"capacity": [
100
],
"amount": 1
}
],
"profiles": [
{
"type": "car",
"name": "car"
}
]
},
"plan": {
"jobs": [
{
"id": "Job_1",
"tasks": {
"deliveries": [
{
"places": [
{
"location": {
"lat": 52.56182,
"lng": 13.497167
},
"duration": 300,
"tag": "letter_delivery"
}
],
"demand": [
1
]
}
]
}
},
{
"id": "Job_2",
"tasks": {
"deliveries": [
{
"places": [
{
"location": {
"lat": 52.534553,
"lng": 13.519429
},
"duration": 600,
"tag": "parcel_delivery"
}
],
"demand": [
1
]
}
]
}
},
{
"id": "Job_3_C1",
"tasks": {
"deliveries": [
{
"places": [
{
"location": {
"lat": 52.463341,
"lng": 13.49061
},
"duration": 300,
"tag": "letter_delivery",
"houseKeyId": "1"
}
],
"demand": [
1
]
}
]
}
},
{
"id": "Job_4_C1",
"tasks": {
"deliveries": [
{
"places": [
{
"location": {
"lat": 52.463341,
"lng": 13.49061
},
"duration": 600,
"tag": "parcel_delivery",
"houseKeyId": "1"
}
],
"demand": [
1
]
}
]
}
},
{
"id": "Job_5_C2",
"tasks": {
"deliveries": [
{
"places": [
{
"location": {
"lat": 52.463341,
"lng": 13.49061
},
"duration": 300,
"tag": "letter_delivery",
"houseKeyId": "2"
}
],
"demand": [
1
]
}
]
}
},
{
"id": "Job_6_C2",
"tasks": {
"deliveries": [
{
"places": [
{
"location": {
"lat": 52.463341,
"lng": 13.49061
},
"duration": 300,
"tag": "letter_delivery",
"houseKeyId": "2"
}
],
"demand": [
1
]
}
]
}
},
{
"id": "Job_7",
"tasks": {
"deliveries": [
{
"places": [
{
"location": {
"lat": 52.447476,
"lng": 13.433062
},
"duration": 600,
"tag": "parcel_delivery"
}
],
"demand": [
1
]
}
]
}
}
]
}
}ソリューション
このケースでは、次のソリューションの内訳が示すように、最適化アルゴリズムによってツアー計画内のすべてのジョブが処理されました。
場所Cの各ジョブに対象世帯ごとにhouseKeyIdを割り当てることで、その場所におけるジョブの合計所要時間は25分から15分に短縮され、ジョブ7をツアー時間枠内に収めることができるようになりました。
その場所で合計所要時間が短縮されたのは、houseKeyIdsが一致するジョブの中から最長の所要時間のみを最適化アルゴリズムが考慮したためです。世帯C1の場合、パーセルの配達ジョブの所要時間が最も長くなります (600秒または10分)。世帯C2の場合、両方のジョブの所要時間は同じ (300秒または5分) であるため、アルゴリズムでは単一の所要時間のみが考慮されました。したがって、次の完全なソリューションJSONが示すように、世帯ごとのジョブ所要時間の合計は900秒 (15分) になります。
Click to expand/collapse the sample JSON
{
"statistic": {
"cost": 53.04,
"distance": 37180,
"duration": 6608,
"times": {
"driving": 4208,
"serving": 2400,
"waiting": 0,
"stopping": 0,
"break": 0
}
},
"tours": [
{
"vehicleId": "small_1",
"typeId": "small",
"stops": [
{
"time": {
"arrival": "2023-05-28T08:00:00Z",
"departure": "2023-05-28T08:00:00Z"
},
"load": [
7
],
"activities": [
{
"jobId": "departure",
"type": "departure",
"location": {
"lat": 52.50935,
"lng": 13.41997
},
"time": {
"start": "2023-05-28T08:00:00Z",
"end": "2023-05-28T08:00:00Z"
}
}
],
"location": {
"lat": 52.50935,
"lng": 13.41997
},
"distance": 0
},
{
"time": {
"arrival": "2023-05-28T08:19:39Z",
"departure": "2023-05-28T08:24:39Z"
},
"load": [
6
],
"activities": [
{
"jobId": "Job_1",
"type": "delivery",
"jobTag": "letter_delivery",
"location": {
"lat": 52.56182,
"lng": 13.497167
},
"time": {
"start": "2023-05-28T08:19:39Z",
"end": "2023-05-28T08:24:39Z"
}
}
],
"location": {
"lat": 52.56182,
"lng": 13.497167
},
"distance": 9971
},
{
"time": {
"arrival": "2023-05-28T08:31:16Z",
"departure": "2023-05-28T08:41:16Z"
},
"load": [
5
],
"activities": [
{
"jobId": "Job_2",
"type": "delivery",
"jobTag": "parcel_delivery",
"location": {
"lat": 52.534553,
"lng": 13.519429
},
"time": {
"start": "2023-05-28T08:31:16Z",
"end": "2023-05-28T08:41:16Z"
}
}
],
"location": {
"lat": 52.534553,
"lng": 13.519429
},
"distance": 13720
},
{
"time": {
"arrival": "2023-05-28T08:58:44Z",
"departure": "2023-05-28T09:13:44Z"
},
"load": [
1
],
"activities": [
{
"jobId": "Job_6_C2",
"type": "delivery",
"jobTag": "letter_delivery",
"location": {
"lat": 52.463341,
"lng": 13.49061
},
"time": {
"start": "2023-05-28T08:58:44Z",
"end": "2023-05-28T09:03:44Z"
}
},
{
"jobId": "Job_4_C1",
"type": "delivery",
"jobTag": "parcel_delivery",
"location": {
"lat": 52.463341,
"lng": 13.49061
},
"time": {
"start": "2023-05-28T09:03:44Z",
"end": "2023-05-28T09:13:44Z"
}
},
{
"jobId": "Job_3_C1",
"type": "delivery",
"jobTag": "letter_delivery",
"location": {
"lat": 52.463341,
"lng": 13.49061
},
"time": {
"start": "2023-05-28T09:03:44Z",
"end": "2023-05-28T09:13:44Z"
}
},
{
"jobId": "Job_5_C2",
"type": "delivery",
"jobTag": "letter_delivery",
"location": {
"lat": 52.463341,
"lng": 13.49061
},
"time": {
"start": "2023-05-28T08:58:44Z",
"end": "2023-05-28T09:03:44Z"
}
}
],
"location": {
"lat": 52.463341,
"lng": 13.49061
},
"distance": 24124
},
{
"time": {
"arrival": "2023-05-28T09:23:22Z",
"departure": "2023-05-28T09:33:22Z"
},
"load": [
0
],
"activities": [
{
"jobId": "Job_7",
"type": "delivery",
"jobTag": "parcel_delivery",
"location": {
"lat": 52.447476,
"lng": 13.433062
},
"time": {
"start": "2023-05-28T09:23:22Z",
"end": "2023-05-28T09:33:22Z"
}
}
],
"location": {
"lat": 52.447476,
"lng": 13.433062
},
"distance": 29529
},
{
"time": {
"arrival": "2023-05-28T09:50:08Z",
"departure": "2023-05-28T09:50:08Z"
},
"load": [
0
],
"activities": [
{
"jobId": "arrival",
"type": "arrival",
"location": {
"lat": 52.50935,
"lng": 13.41997
},
"time": {
"start": "2023-05-28T09:50:08Z",
"end": "2023-05-28T09:50:08Z"
}
}
],
"location": {
"lat": 52.50935,
"lng": 13.41997
},
"distance": 37180
}
],
"statistic": {
"cost": 53.04,
"distance": 37180,
"duration": 6608,
"times": {
"driving": 4208,
"serving": 2400,
"waiting": 0,
"stopping": 0,
"break": 0
}
},
"shiftIndex": 0
}
]
}次の図は更新されたツアーを地図上に視覚化したものです。
このケースでは、停車地3でのarrival時間とdeparture時間は、houseKeyId機能を適用した結果、最適化アルゴリズムが別々の世帯を認識していなかった例と比較して合計所要時間 (15分) が短縮されたことを示しています。さらに、以前は割り当てられなかったジョブが停車地4としてツアー内に収まるようになりました。
まとめ
このチュートリアルでは、ツアーの合計所要時間の計算基準として顧客 (世帯) の数に焦点を当て、houseKeyId機能を適用して同じ場所での複数の配達または集荷を伴うビジネスシナリオでツアーの所要時間を最適化する方法を説明しました。
次のステップ
26 日前の更新