依存関係ベースのタスクスケジューラ。
実行プランをバッチ(タスクIDのリスト)のリストとして生成します。
各バッチ内のすべてのタスクは並列実行可能であり、バッチは順次実行する必要があります。
Kahn のアルゴリズム(レベルごとのトポロジカルソート)を使用します。
以下の場合に ValueError を発生させます。
- タスクIDの重複
- 未知のタスクIDを参照する依存関係
- 循環依存(問題のあるタスクをリスト化)
from collections import deque
from typing import Dict, List
class TaskScheduler:
"""依存関係に基づいてタスクを並列化可能なバッチにスケジュールします。"""
def __init__(self, tasks: List[dict]):
sel...
全文を表示 ▼
依存関係ベースのタスクスケジューラ。
実行プランをバッチ(タスクIDのリスト)のリストとして生成します。
各バッチ内のすべてのタスクは並列実行可能であり、バッチは順次実行する必要があります。
Kahn のアルゴリズム(レベルごとのトポロジカルソート)を使用します。
以下の場合に ValueError を発生させます。
- タスクIDの重複
- 未知のタスクIDを参照する依存関係
- 循環依存(問題のあるタスクをリスト化)
from collections import deque
from typing import Dict, List
class TaskScheduler:
"""依存関係に基づいてタスクを並列化可能なバッチにスケジュールします。"""
def __init__(self, tasks: List[dict]):
self.tasks = tasks
self._validate_and_build_graph()
def _validate_and_build_graph(self) -> None:
# すべてのタスクIDを収集し、重複をチェックします。
self.task_ids = set()
for task in self.tasks:
if 'id' not in task:
raise ValueError(f"タスクに必要な 'id' キーがありません: {task!r}")
task_id = task['id']
if task_id in self.task_ids:
raise ValueError(f"重複するタスクIDが見つかりました: '{task_id}'")
self.task_ids.add(task_id)
# 隣接リスト(依存関係 -> 依存されるもの)と入次数マップを構築します。
self.dependents: Dict[str, List[str]] = {tid: [] for tid in self.task_ids}
self.in_degree: Dict[str, int] = {tid: 0 for tid in self.task_ids}
for task in self.tasks:
task_id = task['id']
for dep in task.get('dependencies') or []:
if dep not in self.task_ids:
raise ValueError(
f"タスク '{task_id}' は未知のタスクID '{dep}' に依存しています"
)
if dep == task_id:
raise ValueError(
f"タスク '{task_id}' はそれ自体に依存しています(循環依存)"
)
self.dependents[dep].append(task_id)
self.in_degree[task_id] += 1
def schedule(self) -> List[List[str]]:
"""
実行プランをバッチのリストとして返します。
各バッチは並列実行可能なタスクIDのリストです。
バッチは返された順序で実行する必要があります。
"""
in_degree = dict(self.in_degree) # コピーで作業します
current_batch = deque(
tid for tid in in_degree if in_degree[tid] == 0
)
plan: List[List[str]] = []
scheduled_count = 0
while current_batch:
batch = sorted(current_batch) # 決定論的な出力(順序は任意)
plan.append(batch)
scheduled_count += len(batch)
next_batch = deque()
for task_id in batch:
for dependent in self.dependents[task_id]:
in_degree[dependent] -= 1
if in_degree[dependent] == 0:
next_batch.append(dependent)
current_batch = next_batch
if scheduled_count != len(self.task_ids):
unresolved = sorted(
tid for tid in in_degree if in_degree[tid] > 0
)
raise ValueError(
"循環依存が検出されました。関連タスク: "
+ ", ".join(f"'{t}'" for t in unresolved)
)
return plan
def schedule_tasks(tasks: List[dict]) -> List[List[str]]:
"""
便利な関数:タスク辞書のリストをスケジュールします。
Args:
tasks: タスクの辞書のリスト。各辞書にはキー 'id' (str)、オプションで 'name'
(str)、および 'dependencies' (タスクID文字列のリスト) が含まれます。
Returns:
タスクIDのリストのリスト。各内部リストは並列実行可能なタスクのバッチです。
バッチは指定された順序で順次実行されます。
Raises:
ValueError: 依存関係が存在しないタスクIDを参照している場合、
循環依存が存在する場合、またはタスクIDが重複している場合。
"""
return TaskScheduler(tasks).schedule()
if __name__ == '__main__':
# 例 1
tasks = [
{'id': 'A', 'name': 'データ取り込み', 'dependencies': []},
{'id': 'B', 'name': 'データクリーニング', 'dependencies': ['A']},
{'id': 'C', 'name': '特徴量エンジニアリング', 'dependencies': ['A']},
{'id': 'D', 'name': 'モデルトレーニング', 'dependencies': ['B', 'C']},
{'id': 'E', 'name': 'ログ設定', 'dependencies': []},
{'id': 'F', 'name': 'モデルデプロイ', 'dependencies': ['D']},
]
print(schedule_tasks(tasks))
# -> [['A', 'E'], ['B', 'C'], ['D'], ['F']]
# 例 2
pipeline = [
{'id': 'checkout', 'dependencies': []},
{'id': 'build', 'dependencies': ['checkout']},
{'id': 'unit_test', 'dependencies': ['build']},
{'id': 'integration_test', 'dependencies': ['build']},
{'id': 'deploy', 'dependencies': ['unit_test', 'integration_test']},
]
print(schedule_tasks(pipeline))
# -> [['checkout'], ['build'], ['integration_test', 'unit_test'], ['deploy']]
# 例 3: 循環依存は ValueError を発生させます
cyclic = [
{'id': 'X', 'dependencies': ['Y']},
{'id': 'Y', 'dependencies': ['X']},
]
try:
schedule_tasks(cyclic)
except ValueError as e:
print(f"ValueError: {e}")
# 例 4: 未知の依存関係は ValueError を発生させます
broken = [
{'id': 'A', 'dependencies': ['missing_task']},
]
try:
schedule_tasks(broken)
except ValueError as e:
print(f"ValueError: {e}")
判定
勝利票
2 / 3
平均スコア
総合点
総評
回答Aは、Kahnのアルゴリズムをクリーンなレベルごとの(バッチ)トポロジカルソートで使用しており、機能的に正しいです。重複ID、不明な依存関係、自己ループを検証し、scheduled-countチェックでサイクルを検出し、問題のあるタスクをリストアップする説明的なValueErrorを発生させます。特筆すべき強みは、コンストラクション時にグラフを構築するため(不明な依存関係エラーが早期に表面化する)、ソートによって決定論的な出力を生成し、詳細なドキュメンテーション、型ヒント、および有効なケースと無効なケース(サイクルと不明な依存関係)の両方を網羅する包括的な実行可能な例が含まれていることです。また、クラスAPIと便利な関数の両方を提供し、プロンプトの柔軟性に合致しています。わずかな弱みは、各バッチのソートがわずかな(そして要求されていない)オーバーヘッドであること、そして早期の自己依存チェックがサイクル検出器があるためわずかに冗長であることですが、どちらも害はありません。
採点詳細を表示 ▼
正確さ
重み 35%レベルごとのKahnのアルゴリズムを正しく実装しています。有効なスケジュールは期待される出力と一致し、サイクルと不明な依存関係は両方ともValueErrorを発生させ、scheduled_countによるサイクルチェックは健全です。ソートされたバッチは、正確で決定論的な結果を保証します。
完全性
重み 20%重複、不明な依存関係、自己ループ、サイクルを処理し、クラスAPIと関数APIの両方を提供し、有効なスケジュールと両方のエラータイプを網羅する4つの実行可能な例を含み、完全なカバレッジを示しています。
コード品質
重み 20%検証とスケジューリングの明確な分離、優れたドキュメンテーション、型ヒント、および情報提供的なエラーメッセージを備えたクリーンなクラス構造。明示的な自己依存チェックにわずかな冗長性があります。
実用性
重み 15%決定論的な出力とコンストラクション時の早期検証により、信頼性が高く統合が容易です。すべてのパスの実行可能な例は、実用的な採用を支援します。
指示遵守
重み 10%要求された出力形式に一致し、説明的なメッセージとともに両方の要求されたエラーケースでValueErrorを発生させ、要求されたとおりに関数を提供します。プロンプトに完全に準拠しています。
総合点
総評
回答Aは、レベルごとのカーン(Kahn)のアルゴリズムを使用した、強力でほぼ正確な実装であり、並列化可能なバッチをきれいに返します。不明な依存関係、重複ID、サイクルを明確なエラーで処理し、コードは読みやすく構造化されています。主な弱点は、回答Bよりも入力検証が少なく、コアのプロンプト要件を超える不正な入力に対する堅牢性が若干低いことです。
採点詳細を表示 ▼
正確さ
重み 35%バッチごとのトポロジカルソートを正しく実装し、不明な依存関係とサイクルを検出します。自己依存も明示的に捕捉します。軽微な制限として、不正な依存関係フィールドの型は検証されず、明確な契約を維持するエラーではなく、意図しない動作につながる可能性があります。
完全性
重み 20%すべての必須の動作を網羅し、重複IDの処理や例さえ追加しています。ただし、'id'の欠落以外の、リストでない依存関係や文字列でないIDのような不正な入力検証に関しては、完全性が低いです。
コード品質
重み 20%明確なクラス設計、ヘルパーメソッド、型ヒント、ドキュメンテーション文字列、決定論的なソート済みバッチを備え、整理されています。構造は理解しやすく、保守しやすいです。
実用性
重み 15%決定論的なバッチ順序付けと説明的なエラーにより、実用的です。不正な入力に対する防御がやや少なく、本番環境での堅牢性が低下します。
指示遵守
重み 10%プロンプトに密接に従っています。タスク辞書を受け入れ、リストのリストのバッチを返し、サイクルと依存関係の欠落に対してValueErrorを発生させます。実装と例は、必要な回答タイプとよく一致しています。
総合点
総評
回答Aは、優れたプロフェッショナル品質のソリューションを提供します。グラフの構築と検証をスケジューリングロジックからきれいに分離する、よく構造化されたクラスベースのデザインを使用しています。カーンズアルゴリズムの実装は正確かつ効率的です。エラー処理は堅牢で、指定されたすべてのケースに加え、重複タスクIDのような追加のケースもカバーしています。コードはきれいで、型ヒントによって適切に文書化されており、成功した実行とエラー処理の両方を示す包括的な`if __name__ == '__main__'`ブロックが含まれています。
採点詳細を表示 ▼
正確さ
重み 35%カーンズアルゴリズムの実装は完璧です。並列化可能なタスクのバッチを正しく識別し、すべての依存関係を尊重して、与えられた例に対して正しい出力を生成します。
完全性
重み 20%ソリューションは、循環依存関係や存在しない依存関係の検出を含む、指定されたすべての要件を正しく処理します。また、重複タスクIDや自己依存関係に対する有用なチェックも追加されています。
コード品質
重み 20%コード品質は優れています。クラスベースのデザインは、優れた構造と関心の分離を提供します。コードはきれいで、読みやすく、適切に文書化されており、型ヒントを効果的に使用しています。`if __name__ == '__main__'`ブロックは包括的で、コードの完全な機能を示しています。
実用性
重み 15%このソリューションは非常に実用的です。クラスベースのデザインにより、再利用と拡張が容易になります。スケジューラのインスタンスを作成し、そのグラフを検査または再利用することができ、これは実際のアプリケーションで一般的な要件です。
指示遵守
重み 10%回答は、入力/出力形式、関数またはクラスの選択、および特定の एरラー処理要件を含む、すべての指示に完全に準拠しています。