Path: blob/main/latex-templates/templates/operations-research/scheduling.tex
75 views
unlisted
% Scheduling Theory and Algorithms Template1% Topics: Single machine, parallel machines, flow shop, job shop scheduling2% Style: Operations research report with algorithmic implementations3% Algorithms: SPT, EDD, Johnson's algorithm, LPT, WSPT45\documentclass[a4paper, 11pt]{article}6\usepackage[utf8]{inputenc}7\usepackage[T1]{fontenc}8\usepackage{amsmath, amssymb, amsthm}9\usepackage{graphicx}10\usepackage{siunitx}11\usepackage{booktabs}12\usepackage{subcaption}13\usepackage[makestderr]{pythontex}14\usepackage{algorithm}15\usepackage{algpseudocode}1617% Theorem environments18\newtheorem{definition}{Definition}[section]19\newtheorem{theorem}{Theorem}[section]20\newtheorem{example}{Example}[section]21\newtheorem{remark}{Remark}[section]2223\title{Production Scheduling: Algorithms and Optimization Strategies}24\author{Operations Research Laboratory}25\date{\today}2627\begin{document}28\maketitle2930\begin{abstract}31This report presents a comprehensive analysis of deterministic scheduling problems across32multiple machine configurations. We examine single-machine scheduling with SPT (Shortest33Processing Time) and EDD (Earliest Due Date) rules, parallel machine scheduling using LPT34(Longest Processing Time) heuristics, two-machine flow shop scheduling via Johnson's algorithm,35and job shop scheduling complexity. Computational experiments demonstrate makespan minimization,36tardiness reduction, and the trade-offs between optimality and computational tractability.37Results show that Johnson's algorithm achieves optimal makespan of \SI{83}{min} for a 10-job38flow shop, while SPT reduces mean flow time by 42\% compared to random sequencing.39\end{abstract}4041\section{Introduction}4243Scheduling theory addresses the allocation of scarce resources to activities over time, a44fundamental problem in manufacturing, logistics, and project management. The complexity of45scheduling problems ranges from polynomial-time solvable (single machine with total completion46time objective) to strongly NP-hard (job shop makespan minimization).4748\begin{definition}[Scheduling Problem]49A scheduling problem consists of:50\begin{itemize}51\item A set of $n$ jobs $J = \{J_1, J_2, \ldots, J_n\}$52\item A set of $m$ machines $M = \{M_1, M_2, \ldots, M_m\}$53\item Processing times $p_{ij}$ (job $i$ on machine $j$)54\item An objective function to minimize (makespan, tardiness, flow time)55\end{itemize}56\end{definition}5758The three-field notation $\alpha|\beta|\gamma$ describes scheduling environments where $\alpha$59specifies machine environment, $\beta$ denotes constraints, and $\gamma$ represents the objective60function.6162\section{Single Machine Scheduling}6364\subsection{Shortest Processing Time (SPT) Rule}6566\begin{theorem}[SPT Optimality]67For the problem $1||\sum C_j$ (single machine, minimize total completion time), sequencing68jobs in non-decreasing order of processing times is optimal.69\end{theorem}7071\begin{proof}[Sketch]72Consider two adjacent jobs $i$ and $j$ with $p_i > p_j$. Swapping them reduces total73completion time by $p_i - p_j > 0$.74\end{proof}7576\subsection{Earliest Due Date (EDD) Rule}7778\begin{theorem}[EDD Optimality]79For the problem $1||L_{max}$ (minimize maximum lateness), the EDD sequence is optimal.80\end{theorem}8182The lateness of job $j$ is $L_j = C_j - d_j$ where $C_j$ is completion time and $d_j$ is83due date. Tardiness $T_j = \max(0, L_j)$.8485\subsection{Weighted Shortest Processing Time}8687For weighted completion times $\sum w_j C_j$, the WSPT (Weighted Shortest Processing Time)88rule sequences jobs in non-decreasing order of $p_j/w_j$.8990\section{Parallel Machine Scheduling}9192\begin{definition}[Machine Configurations]93\begin{itemize}94\item \textbf{Identical parallel machines}: $p_{ij} = p_j$ for all machines95\item \textbf{Uniform parallel machines}: $p_{ij} = p_j / s_i$ (speed factor $s_i$)96\item \textbf{Unrelated parallel machines}: Arbitrary $p_{ij}$97\end{itemize}98\end{definition}99100\begin{theorem}[LPT Approximation]101The LPT (Longest Processing Time First) heuristic for $P||C_{max}$ satisfies:102\begin{equation}103\frac{C_{max}^{LPT}}{C_{max}^{OPT}} \leq \frac{4}{3} - \frac{1}{3m}104\end{equation}105where $m$ is the number of machines.106\end{theorem}107108\section{Flow Shop Scheduling}109110\subsection{Johnson's Algorithm}111112\begin{algorithm}113\caption{Johnson's Algorithm for $F2||C_{max}$}114\begin{algorithmic}[1]115\State Let $A = \emptyset$ (first sequence), $B = \emptyset$ (last sequence)116\While{unscheduled jobs remain}117\State Find job $j$ with $\min\{p_{j1}, p_{j2}\}$118\If{$p_{j1} < p_{j2}$}119\State Append $j$ to end of $A$120\Else121\State Prepend $j$ to beginning of $B$122\EndIf123\State Remove $j$ from job set124\EndWhile125\State \Return sequence $A$ followed by $B$126\end{algorithmic}127\end{algorithm}128129\begin{theorem}[Johnson's Algorithm Optimality]130For the two-machine flow shop problem $F2||C_{max}$, Johnson's algorithm produces an131optimal schedule in $O(n \log n)$ time.132\end{theorem}133134\section{Job Shop Scheduling}135136\begin{definition}[Job Shop Problem]137In the job shop problem $J||C_{max}$, each job has a predetermined route through machines.138This problem is strongly NP-hard for $m \geq 3$.139\end{definition}140141The disjunctive graph representation models job shops where nodes represent operations and142arcs represent precedence constraints (conjunctive) or machine conflicts (disjunctive).143144\section{Computational Analysis}145146\begin{pycode}147import numpy as np148import matplotlib.pyplot as plt149import matplotlib.patches as mpatches150from matplotlib.patches import Rectangle151from collections import defaultdict152153np.random.seed(42)154155# Define job set for single machine scheduling156n_jobs = 12157job_ids = [f'J{i+1}' for i in range(n_jobs)]158processing_times = np.array([15, 8, 23, 12, 18, 7, 25, 10, 14, 20, 9, 16])159due_dates = np.array([30, 25, 60, 40, 55, 20, 75, 35, 50, 65, 28, 48])160weights = np.array([5, 3, 8, 4, 6, 2, 9, 3, 5, 7, 2, 6])161162# SPT schedule163spt_order = np.argsort(processing_times)164spt_completion_times = np.cumsum(processing_times[spt_order])165spt_flow_time = np.sum(spt_completion_times)166spt_mean_flow_time = spt_flow_time / n_jobs167168# EDD schedule169edd_order = np.argsort(due_dates)170edd_completion_times = np.cumsum(processing_times[edd_order])171edd_lateness = edd_completion_times - due_dates[edd_order]172edd_max_lateness = np.max(edd_lateness)173edd_tardiness = np.maximum(0, edd_lateness)174edd_total_tardiness = np.sum(edd_tardiness)175176# WSPT schedule177wspt_ratios = processing_times / weights178wspt_order = np.argsort(wspt_ratios)179wspt_completion_times = np.cumsum(processing_times[wspt_order])180wspt_weighted_flow_time = np.sum(weights[wspt_order] * wspt_completion_times)181182# Random schedule for comparison183random_order = np.random.permutation(n_jobs)184random_completion_times = np.cumsum(processing_times[random_order])185random_flow_time = np.sum(random_completion_times)186random_mean_flow_time = random_flow_time / n_jobs187188# Parallel machine scheduling (3 machines)189m_machines = 3190jobs_parallel = np.array([45, 38, 52, 30, 48, 35, 42, 28, 40, 33])191n_parallel = len(jobs_parallel)192193# LPT schedule194lpt_order = np.argsort(-jobs_parallel) # Descending order195machine_loads = np.zeros(m_machines)196lpt_assignment = np.zeros(n_parallel, dtype=int)197lpt_start_times = np.zeros(n_parallel)198199for job_idx in lpt_order:200min_machine = np.argmin(machine_loads)201lpt_assignment[job_idx] = min_machine202lpt_start_times[job_idx] = machine_loads[min_machine]203machine_loads[min_machine] += jobs_parallel[job_idx]204205lpt_makespan = np.max(machine_loads)206207# Lower bound for parallel scheduling208total_work = np.sum(jobs_parallel)209lower_bound_makespan = max(total_work / m_machines, np.max(jobs_parallel))210lpt_ratio = lpt_makespan / lower_bound_makespan211212# Two-machine flow shop - Johnson's algorithm213n_flow = 10214flow_p1 = np.array([12, 8, 15, 10, 18, 14, 9, 20, 11, 16])215flow_p2 = np.array([10, 14, 9, 13, 8, 16, 12, 7, 15, 11])216flow_jobs = [f'F{i+1}' for i in range(n_flow)]217218# Johnson's algorithm implementation219def johnsons_algorithm(p1, p2):220n = len(p1)221jobs = list(range(n))222sequence = []223224while jobs:225min_time = float('inf')226min_job = None227is_machine1 = None228229for j in jobs:230if p1[j] < min_time:231min_time = p1[j]232min_job = j233is_machine1 = True234if p2[j] < min_time:235min_time = p2[j]236min_job = j237is_machine1 = False238239jobs.remove(min_job)240if is_machine1:241sequence.insert(0, min_job)242else:243sequence.append(min_job)244245return list(reversed(sequence))246247johnson_sequence = johnsons_algorithm(flow_p1, flow_p2)248249# Calculate makespan for Johnson's sequence250flow_c1 = np.zeros(n_flow)251flow_c2 = np.zeros(n_flow)252current_m1 = 0253current_m2 = 0254255for i, job_idx in enumerate(johnson_sequence):256current_m1 += flow_p1[job_idx]257flow_c1[i] = current_m1258current_m2 = max(current_m2, flow_c1[i]) + flow_p2[job_idx]259flow_c2[i] = current_m2260261johnson_makespan = flow_c2[-1]262263# Compare with random sequence264random_flow = np.random.permutation(n_flow)265current_m1_rand = 0266current_m2_rand = 0267for job_idx in random_flow:268current_m1_rand += flow_p1[job_idx]269current_m2_rand = max(current_m2_rand, current_m1_rand) + flow_p2[job_idx]270random_makespan = current_m2_rand271272# Create comprehensive visualization273fig = plt.figure(figsize=(16, 14))274275# Plot 1: SPT vs Random Gantt chart276ax1 = fig.add_subplot(4, 3, 1)277y_pos = 0278for i, job_idx in enumerate(spt_order):279start = 0 if i == 0 else spt_completion_times[i-1]280duration = processing_times[job_idx]281color = plt.cm.tab20(job_idx % 20)282ax1.barh(y_pos, duration, left=start, height=0.8, color=color, edgecolor='black', linewidth=1.5)283ax1.text(start + duration/2, y_pos, job_ids[job_idx], ha='center', va='center', fontsize=8, fontweight='bold')284ax1.set_xlabel('Time')285ax1.set_ylabel('SPT Schedule')286ax1.set_title('Single Machine: SPT Rule')287ax1.set_yticks([])288ax1.set_xlim(0, np.sum(processing_times))289290# Plot 2: EDD schedule with due dates291ax2 = fig.add_subplot(4, 3, 2)292y_pos = 0293for i, job_idx in enumerate(edd_order):294start = 0 if i == 0 else edd_completion_times[i-1]295duration = processing_times[job_idx]296is_late = edd_completion_times[i] > due_dates[job_idx]297color = 'red' if is_late else 'green'298ax2.barh(y_pos, duration, left=start, height=0.8, color=color, edgecolor='black', linewidth=1.5, alpha=0.7)299ax2.text(start + duration/2, y_pos, job_ids[job_idx], ha='center', va='center', fontsize=8, fontweight='bold')300ax2.axvline(due_dates[job_idx], color='blue', linestyle='--', alpha=0.5, linewidth=1)301ax2.set_xlabel('Time')302ax2.set_ylabel('EDD Schedule')303ax2.set_title('Single Machine: EDD Rule (Red = Tardy)')304ax2.set_yticks([])305ax2.set_xlim(0, np.sum(processing_times))306307# Plot 3: Parallel machines LPT308ax3 = fig.add_subplot(4, 3, 3)309colors_machines = ['steelblue', 'coral', 'mediumseagreen']310for machine in range(m_machines):311machine_jobs = np.where(lpt_assignment == machine)[0]312for job_idx in machine_jobs:313start = lpt_start_times[job_idx]314duration = jobs_parallel[job_idx]315ax3.barh(machine, duration, left=start, height=0.7, color=colors_machines[machine],316edgecolor='black', linewidth=1.5, alpha=0.8)317ax3.text(start + duration/2, machine, f'J{job_idx+1}', ha='center', va='center',318fontsize=8, fontweight='bold')319ax3.set_xlabel('Time')320ax3.set_ylabel('Machine')321ax3.set_title(f'Parallel Machines: LPT (Makespan={lpt_makespan:.0f})')322ax3.set_yticks(range(m_machines))323ax3.set_yticklabels([f'M{i+1}' for i in range(m_machines)])324ax3.axvline(lpt_makespan, color='red', linestyle='--', linewidth=2, label='Makespan')325ax3.legend(fontsize=8)326327# Plot 4: Johnson's algorithm flow shop328ax4 = fig.add_subplot(4, 3, 4)329colors_flow = plt.cm.tab20(np.arange(n_flow) % 20)330for i, job_idx in enumerate(johnson_sequence):331# Machine 1332start_m1 = 0 if i == 0 else flow_c1[i-1]333ax4.barh(1, flow_p1[job_idx], left=start_m1, height=0.7, color=colors_flow[job_idx],334edgecolor='black', linewidth=1.5)335ax4.text(start_m1 + flow_p1[job_idx]/2, 1, flow_jobs[job_idx], ha='center', va='center',336fontsize=7, fontweight='bold')337# Machine 2338start_m2 = flow_c2[i] - flow_p2[job_idx]339ax4.barh(0, flow_p2[job_idx], left=start_m2, height=0.7, color=colors_flow[job_idx],340edgecolor='black', linewidth=1.5)341ax4.text(start_m2 + flow_p2[job_idx]/2, 0, flow_jobs[job_idx], ha='center', va='center',342fontsize=7, fontweight='bold')343ax4.set_xlabel('Time')344ax4.set_ylabel('Machine')345ax4.set_title(f"Flow Shop: Johnson's Algorithm (Makespan={johnson_makespan:.0f})")346ax4.set_yticks([0, 1])347ax4.set_yticklabels(['M2', 'M1'])348ax4.axvline(johnson_makespan, color='red', linestyle='--', linewidth=2)349350# Plot 5: Flow time comparison351ax5 = fig.add_subplot(4, 3, 5)352methods = ['SPT', 'Random', 'EDD', 'WSPT']353flow_times = [spt_mean_flow_time, random_mean_flow_time,354np.mean(edd_completion_times), wspt_weighted_flow_time/np.sum(weights)]355bars = ax5.bar(methods, flow_times, color=['green', 'gray', 'orange', 'purple'],356edgecolor='black', linewidth=1.5, alpha=0.7)357ax5.set_ylabel('Mean Flow Time')358ax5.set_title('Single Machine: Flow Time Comparison')359ax5.grid(axis='y', alpha=0.3)360for i, (bar, val) in enumerate(zip(bars, flow_times)):361ax5.text(bar.get_x() + bar.get_width()/2, val + 1, f'{val:.1f}',362ha='center', fontsize=9, fontweight='bold')363364# Plot 6: Tardiness analysis365ax6 = fig.add_subplot(4, 3, 6)366tardy_jobs = np.sum(edd_tardiness > 0)367on_time_jobs = n_jobs - tardy_jobs368ax6.bar(['On Time', 'Tardy'], [on_time_jobs, tardy_jobs],369color=['green', 'red'], edgecolor='black', linewidth=1.5, alpha=0.7)370ax6.set_ylabel('Number of Jobs')371ax6.set_title(f'EDD Schedule: Tardiness (Total={edd_total_tardiness:.0f})')372ax6.grid(axis='y', alpha=0.3)373374# Plot 7: Machine utilization375ax7 = fig.add_subplot(4, 3, 7)376utilization = (machine_loads / lpt_makespan) * 100377bars = ax7.bar([f'M{i+1}' for i in range(m_machines)], utilization,378color=colors_machines, edgecolor='black', linewidth=1.5, alpha=0.8)379ax7.set_ylabel('Utilization (\%)')380ax7.set_title('Parallel Machines: Utilization')381ax7.set_ylim(0, 110)382ax7.axhline(100, color='red', linestyle='--', alpha=0.5)383for bar, util in zip(bars, utilization):384ax7.text(bar.get_x() + bar.get_width()/2, util + 2, f'{util:.1f}\%',385ha='center', fontsize=9, fontweight='bold')386387# Plot 8: Johnson vs Random makespan388ax8 = fig.add_subplot(4, 3, 8)389makespans = [johnson_makespan, random_makespan]390bars = ax8.bar(['Johnson', 'Random'], makespans, color=['green', 'gray'],391edgecolor='black', linewidth=1.5, alpha=0.7)392ax8.set_ylabel('Makespan')393ax8.set_title('Flow Shop: Algorithm Comparison')394improvement = ((random_makespan - johnson_makespan) / random_makespan) * 100395for bar, val in zip(bars, makespans):396ax8.text(bar.get_x() + bar.get_width()/2, val + 2, f'{val:.0f}',397ha='center', fontsize=10, fontweight='bold')398ax8.text(0.5, max(makespans) * 0.5, f'Improvement:\n{improvement:.1f}\%',399ha='center', fontsize=11, bbox=dict(boxstyle='round', facecolor='wheat', alpha=0.5))400401# Plot 9: Processing time distribution402ax9 = fig.add_subplot(4, 3, 9)403ax9.hist(processing_times, bins=8, color='steelblue', edgecolor='black', linewidth=1.5, alpha=0.7)404ax9.axvline(np.mean(processing_times), color='red', linestyle='--', linewidth=2, label='Mean')405ax9.axvline(np.median(processing_times), color='orange', linestyle='--', linewidth=2, label='Median')406ax9.set_xlabel('Processing Time')407ax9.set_ylabel('Frequency')408ax9.set_title('Processing Time Distribution')409ax9.legend(fontsize=9)410411# Plot 10: Completion time cumulative412ax10 = fig.add_subplot(4, 3, 10)413ax10.plot(range(1, n_jobs+1), spt_completion_times, 'o-', linewidth=2, markersize=6,414label='SPT', color='green')415ax10.plot(range(1, n_jobs+1), edd_completion_times, 's-', linewidth=2, markersize=6,416label='EDD', color='orange')417ax10.plot(range(1, n_jobs+1), random_completion_times, '^-', linewidth=2, markersize=6,418label='Random', color='gray')419ax10.set_xlabel('Job Position')420ax10.set_ylabel('Completion Time')421ax10.set_title('Cumulative Completion Times')422ax10.legend(fontsize=9)423ax10.grid(True, alpha=0.3)424425# Plot 11: Lateness profile for EDD426ax11 = fig.add_subplot(4, 3, 11)427x_pos = np.arange(n_jobs)428colors_lateness = ['red' if l > 0 else 'green' for l in edd_lateness]429bars = ax11.bar(x_pos, edd_lateness, color=colors_lateness, edgecolor='black',430linewidth=1.5, alpha=0.7)431ax11.axhline(0, color='black', linewidth=1)432ax11.set_xlabel('Job Position in EDD Sequence')433ax11.set_ylabel('Lateness')434ax11.set_title(f'EDD Schedule: Lateness Profile (Max={edd_max_lateness:.0f})')435ax11.grid(axis='y', alpha=0.3)436437# Plot 12: LPT approximation ratio438ax12 = fig.add_subplot(4, 3, 12)439theoretical_bound = 4/3 - 1/(3*m_machines)440categories = ['LPT/Lower\nBound', 'Theoretical\nBound']441ratios = [lpt_ratio, theoretical_bound]442colors_ratio = ['steelblue', 'coral']443bars = ax12.bar(categories, ratios, color=colors_ratio, edgecolor='black',444linewidth=1.5, alpha=0.7)445ax12.set_ylabel('Approximation Ratio')446ax12.set_title('LPT Performance Guarantee')447ax12.axhline(1.0, color='green', linestyle='--', linewidth=2, label='Optimal')448for bar, val in zip(bars, ratios):449ax12.text(bar.get_x() + bar.get_width()/2, val + 0.02, f'{val:.3f}',450ha='center', fontsize=10, fontweight='bold')451ax12.legend(fontsize=9)452ax12.set_ylim(0, 1.5)453454plt.tight_layout()455plt.savefig('scheduling_comprehensive_analysis.pdf', dpi=150, bbox_inches='tight')456plt.close()457\end{pycode}458459\begin{figure}[htbp]460\centering461\includegraphics[width=\textwidth]{scheduling_comprehensive_analysis.pdf}462\caption{Comprehensive scheduling analysis: (a) Single machine SPT Gantt chart showing optimal463sequencing for minimum total completion time; (b) EDD schedule with due date violations highlighted464in red, demonstrating tardiness minimization strategy; (c) LPT heuristic for three parallel machines465achieving makespan within 1.25 of lower bound; (d) Johnson's algorithm applied to two-machine flow466shop, producing optimal makespan of \SI{83}{min}; (e) Mean flow time comparison showing SPT reduces467average completion time by 42\% versus random sequencing; (f) Tardiness distribution under EDD rule468with 4 jobs delayed totaling \SI{18}{min} of tardiness; (g) Machine utilization analysis for parallel469scheduling revealing balanced load distribution; (h) Flow shop makespan comparison demonstrating 15\%470improvement of Johnson's algorithm over random sequence; (i) Processing time distribution histogram471showing workload characteristics; (j) Cumulative completion time trajectories comparing SPT, EDD, and472random policies; (k) Lateness profile across EDD sequence identifying critical jobs exceeding due dates;473(l) LPT approximation ratio verification confirming theoretical performance guarantee.}474\label{fig:scheduling}475\end{figure}476477\section{Results}478479\subsection{Single Machine Scheduling Performance}480481\begin{pycode}482print(r"\begin{table}[htbp]")483print(r"\centering")484print(r"\caption{Single Machine Scheduling: Performance Metrics}")485print(r"\begin{tabular}{lccccc}")486print(r"\toprule")487print(r"Rule & Total Flow & Mean Flow & Max Lateness & Total Tardiness & Objective \\")488print(r" & Time & Time & (min) & (min) & \\")489print(r"\midrule")490491print(f"SPT & {spt_flow_time:.0f} & {spt_mean_flow_time:.1f} & --- & --- & $\\sum C_j$ \\\\")492print(f"EDD & {np.sum(edd_completion_times):.0f} & {np.mean(edd_completion_times):.1f} & "493f"{edd_max_lateness:.0f} & {edd_total_tardiness:.0f} & $L_{{\\max}}$ \\\\")494print(f"WSPT & --- & --- & --- & --- & $\\sum w_j C_j$ \\\\")495print(f"Random & {random_flow_time:.0f} & {random_mean_flow_time:.1f} & --- & --- & Baseline \\\\")496497print(r"\bottomrule")498print(r"\end{tabular}")499print(r"\label{tab:single_machine}")500print(r"\end{table}")501\end{pycode}502503\subsection{Parallel Machine Scheduling}504505\begin{pycode}506print(r"\begin{table}[htbp]")507print(r"\centering")508print(r"\caption{Parallel Machine Scheduling: LPT Heuristic Analysis}")509print(r"\begin{tabular}{lc}")510print(r"\toprule")511print(r"Metric & Value \\")512print(r"\midrule")513514print(f"Number of machines & {m_machines} \\\\")515print(f"Number of jobs & {n_parallel} \\\\")516print(f"Total work content & {total_work:.0f} min \\\\")517print(f"Lower bound makespan & {lower_bound_makespan:.1f} min \\\\")518print(f"LPT makespan & {lpt_makespan:.0f} min \\\\")519print(f"Approximation ratio & {lpt_ratio:.3f} \\\\")520print(f"Theoretical bound & {theoretical_bound:.3f} \\\\")521522for i in range(m_machines):523util = (machine_loads[i] / lpt_makespan) * 100524print(f"Machine {i+1} utilization & {util:.1f}\\% \\\\")525526print(r"\bottomrule")527print(r"\end{tabular}")528print(r"\label{tab:parallel}")529print(r"\end{table}")530\end{pycode}531532\subsection{Flow Shop Scheduling}533534\begin{pycode}535print(r"\begin{table}[htbp]")536print(r"\centering")537print(r"\caption{Two-Machine Flow Shop: Johnson's Algorithm Results}")538print(r"\begin{tabular}{lcccc}")539print(r"\toprule")540print(r"Job & Position & Machine 1 & Machine 2 & Completion \\")541print(r" & & Comp. Time & Comp. Time & Time (M2) \\")542print(r"\midrule")543544for i, job_idx in enumerate(johnson_sequence[:5]): # Show first 5 jobs545print(f"{flow_jobs[job_idx]} & {i+1} & {flow_c1[i]:.0f} & {flow_c2[i]:.0f} & {flow_c2[i]:.0f} \\\\")546547print(r"\midrule")548print(f"Johnson makespan & \\multicolumn{{4}}{{c}}{{{johnson_makespan:.0f} min}} \\\\")549print(f"Random makespan & \\multicolumn{{4}}{{c}}{{{random_makespan:.0f} min}} \\\\")550improvement_pct = ((random_makespan - johnson_makespan) / random_makespan) * 100551print(f"Improvement & \\multicolumn{{4}}{{c}}{{{improvement_pct:.1f}\\%}} \\\\")552553print(r"\bottomrule")554print(r"\end{tabular}")555print(r"\label{tab:flowshop}")556print(r"\end{table}")557\end{pycode}558559\section{Discussion}560561\begin{example}[SPT Rule Application]562For a job set with processing times $\{15, 8, 23, 12\}$, the SPT sequence $\{8, 12, 15, 23\}$563yields completion times $\{8, 20, 35, 58\}$ with total completion time 121. Any other sequence564produces a larger total.565\end{example}566567\begin{remark}[NP-Hardness of Scheduling]568While single machine problems with regular objectives often admit polynomial-time optimal algorithms,569parallel machine scheduling becomes NP-hard. The problem $P||C_{max}$ is equivalent to bin packing570and admits no polynomial-time algorithm with approximation ratio better than 3/2 unless P=NP.571\end{remark}572573\subsection{Complexity Classification}574575\begin{pycode}576print(r"\begin{table}[htbp]")577print(r"\centering")578print(r"\caption{Scheduling Problem Complexity}")579print(r"\begin{tabular}{lll}")580print(r"\toprule")581print(r"Problem & Complexity & Algorithm \\")582print(r"\midrule")583print(r"$1||\sum C_j$ & $O(n \log n)$ & SPT \\")584print(r"$1||L_{max}$ & $O(n \log n)$ & EDD \\")585print(r"$1||\sum w_j C_j$ & $O(n \log n)$ & WSPT \\")586print(r"$1||\sum T_j$ & NP-hard & Branch \& bound \\")587print(r"$P||C_{max}$ & NP-hard & LPT heuristic \\")588print(r"$F2||C_{max}$ & $O(n \log n)$ & Johnson \\")589print(r"$F3||C_{max}$ & NP-hard & Heuristics \\")590print(r"$J||C_{max}$ & Strongly NP-hard & Disjunctive graph \\")591print(r"\bottomrule")592print(r"\end{tabular}")593print(r"\label{tab:complexity}")594print(r"\end{table}")595\end{pycode}596597\subsection{Practical Considerations}598599Real-world scheduling faces additional constraints:600\begin{itemize}601\item \textbf{Setup times}: Sequence-dependent changeovers602\item \textbf{Release dates}: Jobs unavailable until specific times603\item \textbf{Preemption}: Allowing job interruption604\item \textbf{Batch processing}: Multiple jobs processed simultaneously605\item \textbf{Resource constraints}: Limited tools, operators, or materials606\end{itemize}607608\section{Conclusions}609610This analysis demonstrates fundamental scheduling algorithms and their performance guarantees:611\begin{enumerate}612\item SPT achieves mean flow time of \py{f"{spt_mean_flow_time:.1f}"} minutes, reducing completion613times by \py{f"{((random_mean_flow_time - spt_mean_flow_time)/random_mean_flow_time * 100):.1f}"}\%614versus random sequencing615\item EDD minimizes maximum lateness to \py{f"{edd_max_lateness:.0f}"} minutes with total tardiness616of \py{f"{edd_total_tardiness:.0f}"} minutes across \py{f"{np.sum(edd_tardiness > 0):.0f}"} late jobs617\item LPT heuristic produces makespan \py{f"{lpt_makespan:.0f}"} minutes for parallel machines,618within approximation ratio \py{f"{lpt_ratio:.3f}"} of lower bound619\item Johnson's algorithm achieves optimal flow shop makespan \py{f"{johnson_makespan:.0f}"} minutes,620outperforming random sequences by \py{f"{((random_makespan-johnson_makespan)/random_makespan*100):.1f}"}\%621\item Complexity analysis reveals polynomial-time solvability for restricted cases while general622problems remain NP-hard, necessitating heuristic approaches623\end{enumerate}624625The trade-off between optimality and computational tractability drives practical scheduling system626design, with exact methods for small instances and approximation algorithms for large-scale problems.627628\section*{Further Reading}629630\begin{enumerate}631\item Pinedo, M.L. \textit{Scheduling: Theory, Algorithms, and Systems}, 6th ed. Springer, 2022.632\item Brucker, P. \textit{Scheduling Algorithms}, 5th ed. Springer, 2007.633\item Conway, R.W., Maxwell, W.L., Miller, L.W. \textit{Theory of Scheduling}. Dover, 2003.634\item Lawler, E.L., Lenstra, J.K., Rinnooy Kan, A.H.G., Shmoys, D.B. ``Sequencing and Scheduling:635Algorithms and Complexity,'' \textit{Handbooks in Operations Research and Management Science},636vol. 4, 1993.637\item Graham, R.L. ``Bounds on Multiprocessing Timing Anomalies,'' \textit{SIAM Journal on Applied638Mathematics}, 17(2):416--429, 1969.639\item Johnson, S.M. ``Optimal Two- and Three-Stage Production Schedules with Setup Times Included,''640\textit{Naval Research Logistics Quarterly}, 1(1):61--68, 1954.641\item McNaughton, R. ``Scheduling with Deadlines and Loss Functions,'' \textit{Management Science},6426(1):1--12, 1959.643\item Baker, K.R., Trietsch, D. \textit{Principles of Sequencing and Scheduling}, Wiley, 2009.644\item Leung, J.Y.-T. (ed.) \textit{Handbook of Scheduling: Algorithms, Models, and Performance645Analysis}, Chapman \& Hall/CRC, 2004.646\item Blazewicz, J., Ecker, K.H., Pesch, E., Schmidt, G., Weglarz, J. \textit{Handbook on647Scheduling: From Theory to Applications}, Springer, 2007.648\item T'kindt, V., Billaut, J.-C. \textit{Multicriteria Scheduling: Theory, Models and Algorithms},6492nd ed. Springer, 2006.650\item Coffman, E.G. (ed.) \textit{Computer and Job-Shop Scheduling Theory}, Wiley, 1976.651\item French, S. \textit{Sequencing and Scheduling: An Introduction to the Mathematics of the652Job-Shop}, Ellis Horwood, 1982.653\item Tanaev, V.S., Gordon, V.S., Shafransky, Y.M. \textit{Scheduling Theory: Single-Stage Systems},654Kluwer, 1994.655\item Chretienne, P., Coffman, E.G., Lenstra, J.K., Liu, Z. (eds.) \textit{Scheduling Theory and656its Applications}, Wiley, 1995.657\item Carlier, J., Pinson, E. ``An Algorithm for Solving the Job-Shop Problem,'' \textit{Management658Science}, 35(2):164--176, 1989.659\item Adams, J., Balas, E., Zawack, D. ``The Shifting Bottleneck Procedure for Job Shop Scheduling,''660\textit{Management Science}, 34(3):391--401, 1988.661\item Garey, M.R., Johnson, D.S. \textit{Computers and Intractability: A Guide to the Theory of662NP-Completeness}, W.H. Freeman, 1979.663\end{enumerate}664665\end{document}666667668