Spaces:
Running
Running
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<title>Linear Programming Study Guide</title> | |
<script src="https://cdn.tailwindcss.com"></script> | |
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> | |
<style> | |
.tab-content { | |
display: none; | |
} | |
.tab-content.active { | |
display: block; | |
animation: fadeIn 0.5s ease-in-out; | |
} | |
@keyframes fadeIn { | |
from { opacity: 0; transform: translateY(10px); } | |
to { opacity: 1; transform: translateY(0); } | |
} | |
.modal { | |
display: none; | |
position: fixed; | |
z-index: 100; | |
left: 0; | |
top: 0; | |
width: 100%; | |
height: 100%; | |
background-color: rgba(0,0,0,0.5); | |
} | |
.modal-content { | |
background-color: #f8fafc; | |
margin: 10% auto; | |
padding: 20px; | |
border-radius: 0.5rem; | |
max-width: 600px; | |
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06); | |
} | |
.flashcard { | |
perspective: 1000px; | |
cursor: pointer; | |
} | |
.flashcard-inner { | |
position: relative; | |
width: 100%; | |
height: 100%; | |
transition: transform 0.6s; | |
transform-style: preserve-3d; | |
} | |
.flashcard.flipped .flashcard-inner { | |
transform: rotateY(180deg); | |
} | |
.flashcard-front, .flashcard-back { | |
position: absolute; | |
width: 100%; | |
height: 100%; | |
backface-visibility: hidden; | |
padding: 1.5rem; | |
border-radius: 0.5rem; | |
display: flex; | |
align-items: center; | |
justify-content: center; | |
text-align: center; | |
} | |
.flashcard-back { | |
transform: rotateY(180deg); | |
background-color: #e2e8f0; | |
} | |
.constraint-line { | |
stroke-width: 2; | |
stroke-dasharray: 5,5; | |
} | |
.feasible-region { | |
fill: rgba(56, 178, 172, 0.2); | |
stroke: rgba(56, 178, 172, 0.8); | |
stroke-width: 2; | |
} | |
.objective-line { | |
stroke-width: 2; | |
stroke: #6b46c1; | |
} | |
.optimal-point { | |
fill: #e53e3e; | |
stroke: #63171b; | |
stroke-width: 2; | |
} | |
</style> | |
</head> | |
<body class="bg-gray-50 font-sans"> | |
<div class="container mx-auto px-4 py-8 max-w-6xl"> | |
<!-- Header --> | |
<header class="mb-8 text-center"> | |
<h1 class="text-4xl font-bold text-indigo-800 mb-2">Linear Programming Study Guide</h1> | |
<p class="text-lg text-gray-600">Mastering ECE 580 Exam 2 Concepts (Chapters 15-17)</p> | |
<!-- Progress Bar --> | |
<div class="mt-6 mb-4"> | |
<div class="flex justify-between mb-1"> | |
<span class="text-sm font-medium text-gray-700" id="progress-text">0% Complete</span> | |
<span class="text-sm font-medium text-gray-700" id="points">0 Points</span> | |
</div> | |
<div class="w-full bg-gray-200 rounded-full h-4"> | |
<div id="progress-bar" class="bg-teal-500 h-4 rounded-full" style="width: 0%"></div> | |
</div> | |
</div> | |
</header> | |
<!-- Navigation Tabs --> | |
<div class="flex flex-wrap border-b border-gray-200 mb-6"> | |
<button class="tab-btn px-4 py-2 font-medium text-gray-600 hover:text-indigo-600 transition-colors border-b-2 border-transparent hover:border-indigo-300 active" data-tab="formulating">Formulating Problems</button> | |
<button class="tab-btn px-4 py-2 font-medium text-gray-600 hover:text-indigo-600 transition-colors border-b-2 border-transparent hover:border-indigo-300" data-tab="graphical">Graphical Solutions</button> | |
<button class="tab-btn px-4 py-2 font-medium text-gray-600 hover:text-indigo-600 transition-colors border-b-2 border-transparent hover:border-indigo-300" data-tab="standard">Standard Form</button> | |
<button class="tab-btn px-4 py-2 font-medium text-gray-600 hover:text-indigo-600 transition-colors border-b-2 border-transparent hover:border-indigo-300" data-tab="simplex">Simplex Method</button> | |
<button class="tab-btn px-4 py-2 font-medium text-gray-600 hover:text-indigo-600 transition-colors border-b-2 border-transparent hover:border-indigo-300" data-tab="duality">Duality</button> | |
</div> | |
<!-- Toolbar --> | |
<div class="flex justify-between mb-6"> | |
<div class="flex space-x-2"> | |
<button id="flashcards-btn" class="bg-indigo-100 hover:bg-indigo-200 text-indigo-800 px-4 py-2 rounded-lg flex items-center transition-colors"> | |
<i class="fas fa-layer-group mr-2"></i> Flashcards | |
</button> | |
<button id="quiz-btn" class="bg-purple-100 hover:bg-purple-200 text-purple-800 px-4 py-2 rounded-lg flex items-center transition-colors"> | |
<i class="fas fa-question-circle mr-2"></i> Pop Quiz | |
</button> | |
</div> | |
<div class="flex items-center"> | |
<span class="bg-yellow-100 text-yellow-800 px-3 py-1 rounded-full text-sm font-medium flex items-center"> | |
<i class="fas fa-star mr-1"></i> <span id="badge-count">0</span> Badges | |
</span> | |
</div> | |
</div> | |
<!-- Tab Contents --> | |
<div class="bg-white rounded-xl shadow-md p-6 mb-8"> | |
<!-- Formulating Problems Tab --> | |
<div id="formulating" class="tab-content active"> | |
<h2 class="text-2xl font-bold text-gray-800 mb-4">Formulating Linear Programming Problems</h2> | |
<div class="grid md:grid-cols-2 gap-6 mb-6"> | |
<!-- Content Block 1 --> | |
<div class="bg-blue-50 p-4 rounded-lg"> | |
<h3 class="text-lg font-semibold text-blue-800 mb-2">What is Linear Programming?</h3> | |
<p class="text-gray-700 mb-3">Linear programming (LP) is a mathematical method for determining the best outcome in a mathematical model whose requirements are represented by linear relationships.</p> | |
<p class="text-gray-700">Key components:</p> | |
<ul class="list-disc pl-5 text-gray-700 space-y-1 mt-2"> | |
<li><strong>Objective Function:</strong> What we want to maximize or minimize</li> | |
<li><strong>Decision Variables:</strong> Variables that represent choices</li> | |
<li><strong>Constraints:</strong> Limitations on the decision variables</li> | |
</ul> | |
</div> | |
<!-- Content Block 2 --> | |
<div class="bg-green-50 p-4 rounded-lg"> | |
<h3 class="text-lg font-semibold text-green-800 mb-2">Example: Taco & Burger Stand</h3> | |
<p class="text-gray-700 mb-3">A food stand sells tacos ($2 profit each) and burgers ($3 profit each). They have limited ingredients:</p> | |
<ul class="list-disc pl-5 text-gray-700 space-y-1"> | |
<li>Meat: 20 lbs (taco: 0.25 lb, burger: 0.5 lb)</li> | |
<li>Cheese: 15 lbs (taco: 0.1 lb, burger: 0.3 lb)</li> | |
<li>Time: 8 hours (taco: 2 min, burger: 5 min)</li> | |
</ul> | |
<button id="taco-animation-btn" class="mt-3 bg-blue-500 hover:bg-blue-600 text-white px-3 py-1 rounded text-sm transition-colors"> | |
<i class="fas fa-play mr-1"></i> Show Animation | |
</button> | |
</div> | |
</div> | |
<!-- Animation Placeholder --> | |
<div id="taco-animation" class="hidden bg-gray-100 p-4 rounded-lg mb-6"> | |
<div class="flex justify-between items-center mb-2"> | |
<h4 class="font-medium text-gray-700">Formulating the LP Problem</h4> | |
<button id="close-animation" class="text-gray-500 hover:text-gray-700"> | |
<i class="fas fa-times"></i> | |
</button> | |
</div> | |
<div class="bg-white p-3 rounded border border-gray-200"> | |
<div class="flex items-center mb-2"> | |
<div class="w-8 h-8 bg-blue-100 rounded-full flex items-center justify-center mr-2"> | |
<span class="text-blue-600 font-bold">1</span> | |
</div> | |
<p class="text-gray-700">Define decision variables: Let x = number of tacos, y = number of burgers</p> | |
</div> | |
<div class="flex items-center mb-2"> | |
<div class="w-8 h-8 bg-blue-100 rounded-full flex items-center justify-center mr-2"> | |
<span class="text-blue-600 font-bold">2</span> | |
</div> | |
<p class="text-gray-700">Objective function: Maximize profit = 2x + 3y</p> | |
</div> | |
<div class="flex items-center mb-2"> | |
<div class="w-8 h-8 bg-blue-100 rounded-full flex items-center justify-center mr-2"> | |
<span class="text-blue-600 font-bold">3</span> | |
</div> | |
<p class="text-gray-700">Constraints:</p> | |
</div> | |
<div class="ml-10"> | |
<p class="text-gray-700">0.25x + 0.5y ≤ 20 (Meat constraint)</p> | |
<p class="text-gray-700">0.1x + 0.3y ≤ 15 (Cheese constraint)</p> | |
<p class="text-gray-700">2x + 5y ≤ 480 (Time constraint in minutes)</p> | |
<p class="text-gray-700">x ≥ 0, y ≥ 0 (Non-negativity)</p> | |
</div> | |
</div> | |
</div> | |
<!-- Interactive Practice --> | |
<div class="bg-yellow-50 p-4 rounded-lg mb-6"> | |
<h3 class="text-lg font-semibold text-yellow-800 mb-3">Practice: Pizza & Wings</h3> | |
<p class="text-gray-700 mb-3">A restaurant sells pizza ($8 profit) and wings ($5 profit). They have:</p> | |
<ul class="list-disc pl-5 text-gray-700 mb-4"> | |
<li>Dough: 50 lbs (pizza: 1 lb, wings: 0.2 lb)</li> | |
<li>Sauce: 20 lbs (pizza: 0.5 lb, wings: 0.1 lb)</li> | |
<li>Oven time: 12 hours (pizza: 15 min, wings: 10 min)</li> | |
</ul> | |
<div class="grid md:grid-cols-2 gap-4"> | |
<div> | |
<label class="block text-gray-700 mb-1">Objective Function:</label> | |
<input type="text" id="pizza-objective" class="w-full px-3 py-2 border border-gray-300 rounded" placeholder="e.g., Maximize 8x + 5y"> | |
</div> | |
<div> | |
<label class="block text-gray-700 mb-1">Constraints:</label> | |
<textarea id="pizza-constraints" class="w-full px-3 py-2 border border-gray-300 rounded" rows="4" placeholder="Enter each constraint on a new line"></textarea> | |
</div> | |
</div> | |
<div class="flex justify-between mt-4"> | |
<button id="check-pizza" class="bg-green-500 hover:bg-green-600 text-white px-4 py-2 rounded flex items-center"> | |
<i class="fas fa-check mr-2"></i> Check Answer | |
</button> | |
<button id="show-pizza-solution" class="text-blue-600 hover:text-blue-800 flex items-center"> | |
<i class="fas fa-lightbulb mr-2"></i> Show Solution | |
</button> | |
</div> | |
<div id="pizza-feedback" class="hidden mt-4 p-3 rounded"></div> | |
</div> | |
<!-- Quick Review --> | |
<div class="bg-purple-50 p-4 rounded-lg"> | |
<h3 class="text-lg font-semibold text-purple-800 mb-2">Quick Review</h3> | |
<ul class="list-disc pl-5 text-gray-700 space-y-1"> | |
<li>LP problems have an objective to maximize or minimize</li> | |
<li>Decision variables represent quantities to be determined</li> | |
<li>Constraints limit the possible values of variables</li> | |
<li>All relationships must be linear (no exponents, no products of variables)</li> | |
<li>Non-negativity constraints are usually required</li> | |
</ul> | |
<div class="mt-4"> | |
<button class="mark-complete bg-indigo-500 hover:bg-indigo-600 text-white px-4 py-2 rounded flex items-center"> | |
<i class="fas fa-check-circle mr-2"></i> Mark Complete | |
</button> | |
</div> | |
</div> | |
</div> | |
<!-- Graphical Solutions Tab --> | |
<div id="graphical" class="tab-content"> | |
<h2 class="text-2xl font-bold text-gray-800 mb-4">Graphical Solutions</h2> | |
<div class="grid md:grid-cols-2 gap-6 mb-6"> | |
<!-- Content Block 1 --> | |
<div class="bg-blue-50 p-4 rounded-lg"> | |
<h3 class="text-lg font-semibold text-blue-800 mb-2">Graphical Method Basics</h3> | |
<p class="text-gray-700 mb-3">For problems with two variables, we can solve LP problems graphically:</p> | |
<ol class="list-decimal pl-5 text-gray-700 space-y-1"> | |
<li>Plot each constraint as an equation</li> | |
<li>Identify the feasible region (area satisfying all constraints)</li> | |
<li>Plot the objective function for different values</li> | |
<li>Find the optimal solution at a corner point of the feasible region</li> | |
</ol> | |
</div> | |
<!-- Content Block 2 --> | |
<div class="bg-green-50 p-4 rounded-lg"> | |
<h3 class="text-lg font-semibold text-green-800 mb-2">Special Cases</h3> | |
<ul class="list-disc pl-5 text-gray-700 space-y-1"> | |
<li><strong>Multiple Solutions:</strong> Objective function parallel to a constraint</li> | |
<li><strong>Unbounded:</strong> Feasible region extends infinitely</li> | |
<li><strong>Infeasible:</strong> No points satisfy all constraints</li> | |
</ul> | |
</div> | |
</div> | |
<!-- Interactive Graph --> | |
<div class="bg-gray-100 p-4 rounded-lg mb-6"> | |
<h3 class="text-lg font-semibold text-gray-800 mb-3">Interactive Graph</h3> | |
<div class="grid md:grid-cols-3 gap-4 mb-4"> | |
<div> | |
<label class="block text-gray-700 mb-1">Objective:</label> | |
<select id="graph-objective" class="w-full px-3 py-2 border border-gray-300 rounded"> | |
<option value="max">Maximize</option> | |
<option value="min">Minimize</option> | |
</select> | |
</div> | |
<div> | |
<label class="block text-gray-700 mb-1">Function:</label> | |
<input type="text" id="graph-function" class="w-full px-3 py-2 border border-gray-300 rounded" placeholder="e.g., 3x + 2y"> | |
</div> | |
<div> | |
<label class="block text-gray-700 mb-1">Value:</label> | |
<input type="number" id="graph-value" class="w-full px-3 py-2 border border-gray-300 rounded" placeholder="e.g., 30" value="30"> | |
</div> | |
</div> | |
<div class="grid md:grid-cols-2 gap-4 mb-4"> | |
<div> | |
<label class="block text-gray-700 mb-1">Add Constraint:</label> | |
<div class="flex"> | |
<input type="text" id="new-constraint" class="flex-1 px-3 py-2 border border-gray-300 rounded-l" placeholder="e.g., x + y ≤ 10"> | |
<button id="add-constraint" class="bg-blue-500 hover:bg-blue-600 text-white px-3 py-2 rounded-r"> | |
<i class="fas fa-plus"></i> | |
</button> | |
</div> | |
</div> | |
<div> | |
<label class="block text-gray-700 mb-1">Constraints:</label> | |
<select id="constraint-list" class="w-full px-3 py-2 border border-gray-300 rounded" size="3"> | |
<!-- Constraints will be added here --> | |
</select> | |
</div> | |
</div> | |
<div class="flex space-x-2 mb-4"> | |
<button id="find-optimal" class="bg-green-500 hover:bg-green-600 text-white px-4 py-2 rounded flex items-center"> | |
<i class="fas fa-bullseye mr-2"></i> Find Optimal | |
</button> | |
<button id="reset-graph" class="bg-gray-500 hover:bg-gray-600 text-white px-4 py-2 rounded flex items-center"> | |
<i class="fas fa-redo mr-2"></i> Reset | |
</button> | |
</div> | |
<div class="bg-white p-2 rounded border border-gray-300"> | |
<svg id="graph" width="100%" height="400" viewBox="0 0 500 400" class="border border-gray-200"> | |
<!-- Graph elements will be added here --> | |
</svg> | |
</div> | |
</div> | |
<!-- Quick Review --> | |
<div class="bg-purple-50 p-4 rounded-lg"> | |
<h3 class="text-lg font-semibold text-purple-800 mb-2">Quick Review</h3> | |
<ul class="list-disc pl-5 text-gray-700 space-y-1"> | |
<li>Graphical method works for 2-variable problems</li> | |
<li>Feasible region is the intersection of all constraints</li> | |
<li>Optimal solution is at a corner point (vertex)</li> | |
<li>Objective function is moved parallel until it touches the feasible region</li> | |
</ul> | |
<div class="mt-4"> | |
<button class="mark-complete bg-indigo-500 hover:bg-indigo-600 text-white px-4 py-2 rounded flex items-center"> | |
<i class="fas fa-check-circle mr-2"></i> Mark Complete | |
</button> | |
</div> | |
</div> | |
</div> | |
<!-- Standard Form Tab --> | |
<div id="standard" class="tab-content"> | |
<h2 class="text-2xl font-bold text-gray-800 mb-4">Standard Form</h2> | |
<div class="grid md:grid-cols-2 gap-6 mb-6"> | |
<!-- Content Block 1 --> | |
<div class="bg-blue-50 p-4 rounded-lg"> | |
<h3 class="text-lg font-semibold text-blue-800 mb-2">Standard Form Requirements</h3> | |
<p class="text-gray-700 mb-3">All LP problems must be converted to standard form for the simplex method:</p> | |
<ul class="list-disc pl-5 text-gray-700 space-y-1"> | |
<li>Maximization problem (convert minimization by multiplying objective by -1)</li> | |
<li>All constraints are equations (not inequalities)</li> | |
<li>All variables are non-negative</li> | |
<li>Right-hand side constants are non-negative</li> | |
</ul> | |
</div> | |
<!-- Content Block 2 --> | |
<div class="bg-green-50 p-4 rounded-lg"> | |
<h3 class="text-lg font-semibold text-green-800 mb-2">Conversion Rules</h3> | |
<ul class="list-disc pl-5 text-gray-700 space-y-1"> | |
<li><strong>≤ constraints:</strong> Add slack variable (s ≥ 0)</li> | |
<li><strong>≥ constraints:</strong> Subtract surplus variable (s ≥ 0) and add artificial variable (a ≥ 0)</li> | |
<li><strong>= constraints:</strong> Add artificial variable (a ≥ 0)</li> | |
<li><strong>Unrestricted variables:</strong> Replace x with x⁺ - x⁻ where x⁺, x⁻ ≥ 0</li> | |
</ul> | |
</div> | |
</div> | |
<!-- Example Conversion --> | |
<div class="bg-yellow-50 p-4 rounded-lg mb-6"> | |
<h3 class="text-lg font-semibold text-yellow-800 mb-3">Example Conversion</h3> | |
<div class="grid md:grid-cols-2 gap-4 mb-4"> | |
<div> | |
<h4 class="font-medium text-gray-700 mb-2">Original Problem:</h4> | |
<div class="bg-white p-3 rounded border border-gray-200"> | |
<p>Minimize: -x₁ + 2x₂</p> | |
<p>Subject to:</p> | |
<p>x₁ + x₂ ≤ 6</p> | |
<p>x₁ - x₂ ≥ 2</p> | |
<p>x₁ + 2x₂ = 10</p> | |
<p>x₁ ≥ 0, x₂ unrestricted</p> | |
</div> | |
</div> | |
<div> | |
<h4 class="font-medium text-gray-700 mb-2">Standard Form:</h4> | |
<div class="bg-white p-3 rounded border border-gray-200"> | |
<p>Maximize: x₁ - 2x₂⁺ + 2x₂⁻</p> | |
<p>Subject to:</p> | |
<p>x₁ + x₂⁺ - x₂⁻ + s₁ = 6</p> | |
<p>x₁ - x₂⁺ + x₂⁻ - s₂ + a₁ = 2</p> | |
<p>x₁ + 2x₂⁺ - 2x₂⁻ + a₂ = 10</p> | |
<p>All variables ≥ 0</p> | |
</div> | |
</div> | |
</div> | |
<button id="show-conversion-animation" class="bg-blue-500 hover:bg-blue-600 text-white px-4 py-2 rounded flex items-center"> | |
<i class="fas fa-play mr-2"></i> Show Conversion Steps | |
</button> | |
</div> | |
<!-- Practice Section --> | |
<div class="bg-orange-50 p-4 rounded-lg mb-6"> | |
<h3 class="text-lg font-semibold text-orange-800 mb-3">Practice Conversion</h3> | |
<div class="mb-4"> | |
<label class="block text-gray-700 mb-1">Original Problem:</label> | |
<textarea id="original-problem" class="w-full px-3 py-2 border border-gray-300 rounded" rows="4" placeholder="Enter the original LP problem"></textarea> | |
</div> | |
<div class="mb-4"> | |
<label class="block text-gray-700 mb-1">Converted Standard Form:</label> | |
<textarea id="standard-form" class="w-full px-3 py-2 border border-gray-300 rounded" rows="4" placeholder="Enter your converted standard form"></textarea> | |
</div> | |
<div class="flex justify-between"> | |
<button id="check-standard" class="bg-green-500 hover:bg-green-600 text-white px-4 py-2 rounded flex items-center"> | |
<i class="fas fa-check mr-2"></i> Check Answer | |
</button> | |
<button id="show-standard-solution" class="text-blue-600 hover:text-blue-800 flex items-center"> | |
<i class="fas fa-lightbulb mr-2"></i> Show Solution | |
</button> | |
</div> | |
<div id="standard-feedback" class="hidden mt-4 p-3 rounded"></div> | |
</div> | |
<!-- Quick Review --> | |
<div class="bg-purple-50 p-4 rounded-lg"> | |
<h3 class="text-lg font-semibold text-purple-800 mb-2">Quick Review</h3> | |
<ul class="list-disc pl-5 text-gray-700 space-y-1"> | |
<li>Standard form requires maximization, equality constraints, and non-negative variables</li> | |
<li>Slack variables are added for ≤ constraints</li> | |
<li>Surplus and artificial variables are added for ≥ constraints</li> | |
<li>Artificial variables are needed for = constraints</li> | |
<li>Unrestricted variables are split into positive and negative parts</li> | |
</ul> | |
<div class="mt-4"> | |
<button class="mark-complete bg-indigo-500 hover:bg-indigo-600 text-white px-4 py-2 rounded flex items-center"> | |
<i class="fas fa-check-circle mr-2"></i> Mark Complete | |
</button> | |
</div> | |
</div> | |
</div> | |
<!-- Simplex Method Tab --> | |
<div id="simplex" class="tab-content"> | |
<h2 class="text-2xl font-bold text-gray-800 mb-4">Simplex Method</h2> | |
<div class="grid md:grid-cols-2 gap-6 mb-6"> | |
<!-- Content Block 1 --> | |
<div class="bg-blue-50 p-4 rounded-lg"> | |
<h3 class="text-lg font-semibold text-blue-800 mb-2">Simplex Algorithm Steps</h3> | |
<ol class="list-decimal pl-5 text-gray-700 space-y-1"> | |
<li>Convert problem to standard form</li> | |
<li>Set up initial simplex tableau</li> | |
<li>Identify entering variable (most negative coefficient in objective row)</li> | |
<li>Identify departing variable (minimum ratio test)</li> | |
<li>Pivot to create new tableau</li> | |
<li>Repeat until all coefficients in objective row are non-negative</li> | |
</ol> | |
</div> | |
<!-- Content Block 2 --> | |
<div class="bg-green-50 p-4 rounded-lg"> | |
<h3 class="text-lg font-semibold text-green-800 mb-2">Special Cases</h3> | |
<ul class="list-disc pl-5 text-gray-700 space-y-1"> | |
<li><strong>Degeneracy:</strong> Minimum ratio test has a tie</li> | |
<li><strong>Unbounded:</strong> No positive denominators in ratio test</li> | |
<li><strong>Multiple Solutions:</strong> Zero coefficient in objective row for non-basic variable</li> | |
<li><strong>Infeasible:</strong> Artificial variable remains in basis</li> | |
</ul> | |
</div> | |
</div> | |
<!-- Simplex Practice --> | |
<div class="bg-gray-100 p-4 rounded-lg mb-6"> | |
<h3 class="text-lg font-semibold text-gray-800 mb-3">Simplex Practice</h3> | |
<div class="mb-4"> | |
<label class="block text-gray-700 mb-1">Initial Tableau:</label> | |
<div class="overflow-x-auto"> | |
<table id="simplex-tableau" class="w-full border-collapse"> | |
<thead> | |
<tr> | |
<th class="border px-4 py-2">Basic</th> | |
<th class="border px-4 py-2">x₁</th> | |
<th class="border px-4 py-2">x₂</th> | |
<th class="border px-4 py-2">s₁</th> | |
<th class="border px-4 py-2">s₂</th> | |
<th class="border px-4 py-2">RHS</th> | |
</tr> | |
</thead> | |
<tbody> | |
<tr> | |
<td class="border px-4 py-2">s₁</td> | |
<td class="border px-4 py-2"><input type="number" class="w-16 px-2 py-1 border" value="1"></td> | |
<td class="border px-4 py-2"><input type="number" class="w-16 px-2 py-1 border" value="1"></td> | |
<td class="border px-4 py-2"><input type="number" class="w-16 px-2 py-1 border" value="1"></td> | |
<td class="border px-4 py-2"><input type="number" class="w-16 px-2 py-1 border" value="0"></td> | |
<td class="border px-4 py-2"><input type="number" class="w-16 px-2 py-1 border" value="6"></td> | |
</tr> | |
<tr> | |
<td class="border px-4 py-2">s₂</td> | |
<td class="border px-4 py-2"><input type="number" class="w-16 px-2 py-1 border" value="2"></td> | |
<td class="border px-4 py-2"><input type="number" class="w-16 px-2 py-1 border" value="1"></td> | |
<td class="border px-4 py-2"><input type="number" class="w-16 px-2 py-1 border" value="0"></td> | |
<td class="border px-4 py-2"><input type="number" class="w-16 px-2 py-1 border" value="1"></td> | |
<td class="border px-4 py-2"><input type="number" class="w-16 px-2 py-1 border" value="8"></td> | |
</tr> | |
<tr class="bg-gray-100"> | |
<td class="border px-4 py-2">z</td> | |
<td class="border px-4 py-2"><input type="number" class="w-16 px-2 py-1 border" value="-3"></td> | |
<td class="border px-4 py-2"><input type="number" class="w-16 px-2 py-1 border" value="-2"></td> | |
<td class="border px-4 py-2"><input type="number" class="w-16 px-2 py-1 border" value="0"></td> | |
<td class="border px-4 py-2"><input type="number" class="w-16 px-2 py-1 border" value="0"></td> | |
<td class="border px-4 py-2"><input type="number" class="w-16 px-2 py-1 border" value="0"></td> | |
</tr> | |
</tbody> | |
</table> | |
</div> | |
</div> | |
<div class="flex flex-wrap gap-2 mb-4"> | |
<button id="next-step" class="bg-blue-500 hover:bg-blue-600 text-white px-4 py-2 rounded flex items-center"> | |
<i class="fas fa-forward mr-2"></i> Next Step | |
</button> | |
<button id="reset-simplex" class="bg-gray-500 hover:bg-gray-600 text-white px-4 py-2 rounded flex items-center"> | |
<i class="fas fa-redo mr-2"></i> Reset | |
</button> | |
<button id="hint-simplex" class="bg-yellow-500 hover:bg-yellow-600 text-white px-4 py-2 rounded flex items-center"> | |
<i class="fas fa-question mr-2"></i> Hint | |
</button> | |
<button id="auto-solve" class="bg-green-500 hover:bg-green-600 text-white px-4 py-2 rounded flex items-center"> | |
<i class="fas fa-robot mr-2"></i> Auto Solve | |
</button> | |
</div> | |
<div id="simplex-feedback" class="hidden p-3 bg-white rounded border border-gray-300 mb-4"></div> | |
<div id="simplex-steps" class="hidden"> | |
<h4 class="font-medium text-gray-700 mb-2">Solution Steps:</h4> | |
<div class="space-y-2" id="steps-container"></div> | |
</div> | |
</div> | |
<!-- Quick Review --> | |
<div class="bg-purple-50 p-4 rounded-lg"> | |
<h3 class="text-lg font-semibold text-purple-800 mb-2">Quick Review</h3> | |
<ul class="list-disc pl-5 text-gray-700 space-y-1"> | |
<li>Simplex method moves from one basic feasible solution to another</li> | |
<li>Entering variable has most negative coefficient in objective row</li> | |
<li>Departing variable is chosen by minimum ratio test</li> | |
<li>Pivot operation updates the tableau</li> | |
<li>Process continues until optimality is reached</li> | |
</ul> | |
<div class="mt-4"> | |
<button class="mark-complete bg-indigo-500 hover:bg-indigo-600 text-white px-4 py-2 rounded flex items-center"> | |
<i class="fas fa-check-circle mr-2"></i> Mark Complete | |
</button> | |
</div> | |
</div> | |
</div> | |
<!-- Duality Tab --> | |
<div id="duality" class="tab-content"> | |
<h2 class="text-2xl font-bold text-gray-800 mb-4">Duality</h2> | |
<div class="grid md:grid-cols-2 gap-6 mb-6"> | |
<!-- Content Block 1 --> | |
<div class="bg-blue-50 p-4 rounded-lg"> | |
<h3 class="text-lg font-semibold text-blue-800 mb-2">Duality Concepts</h3> | |
<p class="text-gray-700 mb-3">Every LP problem (primal) has a corresponding dual problem:</p> | |
<ul class="list-disc pl-5 text-gray-700 space-y-1"> | |
<li>Primal maximization becomes dual minimization (and vice versa)</li> | |
<li>Primal constraints become dual variables</li> | |
<li>Primal variables become dual constraints</li> | |
<li>Constraint coefficients become constraint coefficients in transposed form</li> | |
</ul> | |
</div> | |
<!-- Content Block 2 --> | |
<div class="bg-green-50 p-4 rounded-lg"> | |
<h3 class="text-lg font-semibold text-green-800 mb-2">Duality Theorems</h3> | |
<ul class="list-disc pl-5 text-gray-700 space-y-1"> | |
<li><strong>Weak Duality:</strong> Value of any feasible dual solution ≥ value of any feasible primal solution</li> | |
<li><strong>Strong Duality:</strong> If one problem has optimal solution, so does the other, with equal objective values</li> | |
<li><strong>Complementary Slackness:</strong> At optimality, either primal slack or dual variable is zero for each constraint</li> | |
</ul> | |
</div> | |
</div> | |
<!-- Example Conversion --> | |
<div class="bg-yellow-50 p-4 rounded-lg mb-6"> | |
<h3 class="text-lg font-semibold text-yellow-800 mb-3">Example: Primal to Dual</h3> | |
<div class="grid md:grid-cols-2 gap-4 mb-4"> | |
<div> | |
<h4 class="font-medium text-gray-700 mb-2">Primal Problem:</h4> | |
<div class="bg-white p-3 rounded border border-gray-200"> | |
<p>Maximize: 3x₁ + 2x₂</p> | |
<p>Subject to:</p> | |
<p>x₁ + x₂ ≤ 4</p> | |
<p>2x₁ + x₂ ≤ 6</p> | |
<p>x₁ ≥ 0, x₂ ≥ 0</p> | |
</div> | |
</div> | |
<div> | |
<h4 class="font-medium text-gray-700 mb-2">Dual Problem:</h4> | |
<div class="bg-white p-3 rounded border border-gray-200"> | |
<p>Minimize: 4y₁ + 6y₂</p> | |
<p>Subject to:</p> | |
<p>y₁ + 2y₂ ≥ 3</p> | |
<p>y₁ + y₂ ≥ 2</p> | |
<p>y₁ ≥ 0, y₂ ≥ 0</p> | |
</div> | |
</div> | |
</div> | |
<button id="show-duality-animation" class="bg-blue-500 hover:bg-blue-600 text-white px-4 py-2 rounded flex items-center"> | |
<i class="fas fa-play mr-2"></i> Show Conversion Steps | |
</button> | |
</div> | |
<!-- Practice Section --> | |
<div class="bg-orange-50 p-4 rounded-lg mb-6"> | |
<h3 class="text-lg font-semibold text-orange-800 mb-3">Practice Duality</h3> | |
<div class="mb-4"> | |
<label class="block text-gray-700 mb-1">Primal Problem:</label> | |
<textarea id="primal-problem" class="w-full px-3 py-2 border border-gray-300 rounded" rows="4" placeholder="Enter the primal LP problem"></textarea> | |
</div> | |
<div class="mb-4"> | |
<label class="block text-gray-700 mb-1">Dual Problem:</label> | |
<textarea id="dual-problem" class="w-full px-3 py-2 border border-gray-300 rounded" rows="4" placeholder="Enter your dual formulation"></textarea> | |
</div> | |
<div class="flex justify-between"> | |
<button id="check-dual" class="bg-green-500 hover:bg-green-600 text-white px-4 py-2 rounded flex items-center"> | |
<i class="fas fa-check mr-2"></i> Check Answer | |
</button> | |
<button id="show-dual-solution" class="text-blue-600 hover:text-blue-800 flex items-center"> | |
<i class="fas fa-lightbulb mr-2"></i> Show Solution | |
</button> | |
</div> | |
<div id="dual-feedback" class="hidden mt-4 p-3 rounded"></div> | |
</div> | |
<!-- Quick Review --> | |
<div class="bg-purple-50 p-4 rounded-lg"> | |
<h3 class="text-lg font-semibold text-purple-800 mb-2">Quick Review</h3> | |
<ul class="list-disc pl-5 text-gray-700 space-y-1"> | |
<li>Dual of a maximization problem is a minimization problem</li> | |
<li>Number of dual variables = number of primal constraints</li> | |
<li>Number of dual constraints = number of primal variables</li> | |
<li>Constraint coefficients are transposed</li> | |
<li>Weak duality provides bounds, strong duality provides exact equality at optimality</li> | |
</ul> | |
<div class="mt-4"> | |
<button class="mark-complete bg-indigo-500 hover:bg-indigo-600 text-white px-4 py-2 rounded flex items-center"> | |
<i class="fas fa-check-circle mr-2"></i> Mark Complete | |
</button> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
<!-- Flashcards Modal --> | |
<div id="flashcards-modal" class="modal"> | |
<div class="modal-content"> | |
<div class="flex justify-between items-center mb-4"> | |
<h3 class="text-xl font-bold text-gray-800">Linear Programming Flashcards</h3> | |
<button id="close-flashcards" class="text-gray-500 hover:text-gray-700"> | |
<i class="fas fa-times"></i> | |
</button> | |
</div> | |
<div class="bg-white p-4 rounded-lg border border-gray-200 mb-4"> | |
<div id="flashcard-container" class="flex flex-col items-center"> | |
<div class="flashcard w-full h-64 mb-4" id="current-flashcard"> | |
<div class="flashcard-inner"> | |
<div class="flashcard-front bg-indigo-100 flex items-center justify-center"> | |
<p class="text-xl font-medium text-indigo-800" id="flashcard-question">Click to flip</p> | |
</div> | |
<div class="flashcard-back"> | |
<p class="text-lg text-gray-700" id="flashcard-answer">Answer will appear here</p> | |
</div> | |
</div> | |
</div> | |
<div class="flex justify-between w-full"> | |
<button id="prev-flashcard" class="bg-gray-500 hover:bg-gray-600 text-white px-4 py-2 rounded"> | |
<i class="fas fa-arrow-left"></i> | |
</button> | |
<span id="flashcard-count" class="text-gray-700">1/10</span> | |
<button id="next-flashcard" class="bg-gray-500 hover:bg-gray-600 text-white px-4 py-2 rounded"> | |
<i class="fas fa-arrow-right"></i> | |
</button> | |
</div> | |
</div> | |
</div> | |
<div class="flex justify-center"> | |
<button id="shuffle-flashcards" class="bg-blue-500 hover:bg-blue-600 text-white px-4 py-2 rounded flex items-center"> | |
<i class="fas fa-random mr-2"></i> Shuffle | |
</button> | |
</div> | |
</div> | |
</div> | |
<!-- Quiz Modal --> | |
<div id="quiz-modal" class="modal"> | |
<div class="modal-content"> | |
<div class="flex justify-between items-center mb-4"> | |
<h3 class="text-xl font-bold text-gray-800">Linear Programming Pop Quiz</h3> | |
<button id="close-quiz" class="text-gray-500 hover:text-gray-700"> | |
<i class="fas fa-times"></i> | |
</button> | |
</div> | |
<div class="bg-white p-4 rounded-lg border border-gray-200 mb-4"> | |
<div id="quiz-container"> | |
<div class="mb-4"> | |
<h4 class="text-lg font-medium text-gray-700 mb-2" id="quiz-question">Question will appear here</h4> | |
<div class="space-y-2" id="quiz-options"> | |
<!-- Options will be added here --> | |
</div> | |
</div> | |
<div id="quiz-feedback" class="hidden p-3 rounded mb-4"></div> | |
<div class="flex justify-between"> | |
<button id="prev-question" class="bg-gray-500 hover:bg-gray-600 text-white px-4 py-2 rounded"> | |
<i class="fas fa-arrow-left"></i> Previous | |
</button> | |
<span id="quiz-count" class="text-gray-700">1/5</span> | |
<button id="next-question" class="bg-blue-500 hover:bg-blue-600 text-white px-4 py-2 rounded"> | |
Next <i class="fas fa-arrow-right"></i> | |
</button> | |
</div> | |
</div> | |
</div> | |
<div class="flex justify-center"> | |
<button id="submit-quiz" class="bg-green-500 hover:bg-green-600 text-white px-4 py-2 rounded flex items-center"> | |
<i class="fas fa-check-circle mr-2"></i> Submit Quiz | |
</button> | |
</div> | |
</div> | |
</div> | |
<script> | |
// Tab switching functionality | |
document.querySelectorAll('.tab-btn').forEach(button => { | |
button.addEventListener('click', () => { | |
// Remove active class from all buttons and content | |
document.querySelectorAll('.tab-btn').forEach(btn => btn.classList.remove('active')); | |
document.querySelectorAll('.tab-content').forEach(content => content.classList.remove('active')); | |
// Add active class to clicked button | |
button.classList.add('active'); | |
// Show corresponding content | |
const tabId = button.getAttribute('data-tab'); | |
document.getElementById(tabId).classList.add('active'); | |
}); | |
}); | |
// Formulating Problems Animation | |
document.getElementById('taco-animation-btn').addEventListener('click', () => { | |
document.getElementById('taco-animation').classList.remove('hidden'); | |
}); | |
document.getElementById('close-animation').addEventListener('click', () => { | |
document.getElementById('taco-animation').classList.add('hidden'); | |
}); | |
// Pizza Practice | |
document.getElementById('check-pizza').addEventListener('click', () => { | |
const objective = document.getElementById('pizza-objective').value.trim(); | |
const constraints = document.getElementById('pizza-constraints').value.trim(); | |
const feedback = document.getElementById('pizza-feedback'); | |
if (!objective || !constraints) { | |
feedback.innerHTML = '<div class="bg-red-100 text-red-800 p-3 rounded">Please enter both the objective function and constraints.</div>'; | |
feedback.classList.remove('hidden'); | |
return; | |
} | |
// Simple validation (would be more robust in a real implementation) | |
const expectedObjective = "Maximize 8x + 5y"; | |
const expectedConstraints = [ | |
"1x + 0.2y ≤ 50", | |
"0.5x + 0.1y ≤ 20", | |
"15x + 10y ≤ 720", | |
"x ≥ 0", | |
"y ≥ 0" | |
]; | |
let correct = true; | |
let message = ''; | |
if (!objective.toLowerCase().includes(expectedObjective.toLowerCase())) { | |
correct = false; | |
message += '<p>Objective function should be similar to: ' + expectedObjective + '</p>'; | |
} | |
const constraintLines = constraints.split('\n').map(c => c.trim()); | |
if (constraintLines.length < expectedConstraints.length) { | |
correct = false; | |
message += '<p>You seem to be missing some constraints. Expected ' + expectedConstraints.length + ' constraints.</p>'; | |
} | |
if (correct) { | |
feedback.innerHTML = '<div class="bg-green-100 text-green-800 p-3 rounded">Correct! Well done.</div>'; | |
addPoints(10); | |
} else { | |
feedback.innerHTML = '<div class="bg-yellow-100 text-yellow-800 p-3 rounded">' + | |
'<p>Almost there! Here are some suggestions:</p>' + message + '</div>'; | |
} | |
feedback.classList.remove('hidden'); | |
}); | |
document.getElementById('show-pizza-solution').addEventListener('click', () => { | |
document.getElementById('pizza-objective').value = "Maximize 8x + 5y"; | |
document.getElementById('pizza-constraints').value = | |
"1x + 0.2y ≤ 50\n" + | |
"0.5x + 0.1y ≤ 20\n" + | |
"15x + 10y ≤ 720\n" + | |
"x ≥ 0\n" + | |
"y ≥ 0"; | |
}); | |
// Graphical Solutions | |
// Initialize SVG graph | |
const svg = document.getElementById('graph'); | |
const svgNS = "http://www.w3.org/2000/svg"; | |
// Draw axes | |
const xAxis = document.createElementNS(svgNS, 'line'); | |
xAxis.setAttribute('x1', '50'); | |
xAxis.setAttribute('y1', '350'); | |
xAxis.setAttribute('x2', '450'); | |
xAxis.setAttribute('y2', '350'); | |
xAxis.setAttribute('stroke', 'black'); | |
xAxis.setAttribute('stroke-width', '2'); | |
svg.appendChild(xAxis); | |
const yAxis = document.createElementNS(svgNS, 'line'); | |
yAxis.setAttribute('x1', '50'); | |
yAxis.setAttribute('y1', '350'); | |
yAxis.setAttribute('x2', '50'); | |
yAxis.setAttribute('y2', '50'); | |
yAxis.setAttribute('stroke', 'black'); | |
yAxis.setAttribute('stroke-width', '2'); | |
svg.appendChild(yAxis); | |
// Add axis labels | |
const xLabel = document.createElementNS(svgNS, 'text'); | |
xLabel.setAttribute('x', '440'); | |
xLabel.setAttribute('y', '340'); | |
xLabel.setAttribute('font-size', '14'); | |
xLabel.setAttribute('text-anchor', 'end'); | |
xLabel.textContent = 'x'; | |
svg.appendChild(xLabel); | |
const yLabel = document.createElementNS(svgNS, 'text'); | |
yLabel.setAttribute('x', '60'); | |
yLabel.setAttribute('y', '60'); | |
yLabel.setAttribute('font-size', '14'); | |
yLabel.textContent = 'y'; | |
svg.appendChild(yLabel); | |
// Add tick marks | |
for (let i = 1; i <= 8; i++) { | |
const xTick = document.createElementNS(svgNS, 'line'); | |
xTick.setAttribute('x1', 50 + i * 50); | |
xTick.setAttribute('y1', '345'); | |
xTick.setAttribute('x2', 50 + i * 50); | |
xTick.setAttribute('y2', '355'); | |
xTick.setAttribute('stroke', 'black'); | |
xTick.setAttribute('stroke-width', '1'); | |
svg.appendChild(xTick); | |
const xTickLabel = document.createElementNS(svgNS, 'text'); | |
xTickLabel.setAttribute('x', 50 + i * 50); | |
xTickLabel.setAttribute('y', '370'); | |
xTickLabel.setAttribute('font-size', '12'); | |
xTickLabel.setAttribute('text-anchor', 'middle'); | |
xTickLabel.textContent = i; | |
svg.appendChild(xTickLabel); | |
const yTick = document.createElementNS(svgNS, 'line'); | |
yTick.setAttribute('x1', '45'); | |
yTick.setAttribute('y1', 350 - i * 50); | |
yTick.setAttribute('x2', '55'); | |
yTick.setAttribute('y2', 350 - i * 50); | |
yTick.setAttribute('stroke', 'black'); | |
yTick.setAttribute('stroke-width', '1'); | |
svg.appendChild(yTick); | |
const yTickLabel = document.createElementNS(svgNS, 'text'); | |
yTickLabel.setAttribute('x', '35'); | |
yTickLabel.setAttribute('y', 355 - i * 50); | |
yTickLabel.setAttribute('font-size', '12'); | |
yTickLabel.setAttribute('text-anchor', 'end'); | |
yTickLabel.textContent = i; | |
svg.appendChild(yTickLabel); | |
} | |
// Store graph elements for later manipulation | |
const graphElements = { | |
constraints: [], | |
feasibleRegion: null, | |
objectiveLine: null, | |
optimalPoint: null | |
}; | |
// Add constraint functionality | |
document.getElementById('add-constraint').addEventListener('click', () => { | |
const constraintText = document.getElementById('new-constraint').value.trim(); | |
if (!constraintText) return; | |
// Add to constraint list | |
const option = document.createElement('option'); | |
option.textContent = constraintText; | |
document.getElementById('constraint-list').appendChild(option); | |
// Clear input | |
document.getElementById('new-constraint').value = ''; | |
// Plot constraint (simplified for demo) | |
const parts = constraintText.split(/(<=|>=|=)/); | |
if (parts.length !== 3) return; | |
const left = parts[0].trim(); | |
const op = parts[1].trim(); | |
const right = parts[2].trim(); | |
// Parse constraint (very simplified parsing for demo) | |
let a, b, c; | |
if (left.includes('x') && left.includes('y')) { | |
const xyParts = left.split(/x|y/); | |
a = parseFloat(xyParts[0]) || 1; | |
b = parseFloat(xyParts[1]) || 1; | |
} else if (left.includes('x')) { | |
a = parseFloat(left.replace('x', '')) || 1; | |
b = 0; | |
} else if (left.includes('y')) { | |
a = 0; | |
b = parseFloat(left.replace('y', '')) || 1; | |
} | |
c = parseFloat(right); | |
// Plot the line ax + by = c | |
if (a !== 0 && b !== 0) { | |
// Find two points to draw the line | |
const x1 = 0, y1 = c / b; | |
const x2 = c / a, y2 = 0; | |
const line = document.createElementNS(svgNS, 'line'); | |
line.setAttribute('x1', 50 + x1 * 50); | |
line.setAttribute('y1', 350 - y1 * 50); | |
line.setAttribute('x2', 50 + x2 * 50); | |
line.setAttribute('y2', 350 - y2 * 50); | |
line.setAttribute('stroke', 'blue'); | |
line.setAttribute('stroke-width', '2'); | |
line.setAttribute('stroke-dasharray', '5,5'); | |
line.classList.add('constraint-line'); | |
svg.appendChild(line); | |
// Store for later reference | |
graphElements.constraints.push(line); | |
} | |
}); | |
// Find optimal solution (simplified for demo) | |
document.getElementById('find-optimal').addEventListener('click', () => { | |
// In a real implementation, this would solve the LP problem | |
// For demo purposes, we'll just show a point at (4,2) | |
// Remove previous optimal point if exists | |
if (graphElements.optimalPoint) { | |
svg.removeChild(graphElements.optimalPoint); | |
} | |
// Add optimal point | |
const point = document.createElementNS(svgNS, 'circle'); | |
point.setAttribute('cx', '250'); | |
point.setAttribute('cy', '250'); | |
point.setAttribute('r', '6'); | |
point.setAttribute('fill', 'red'); | |
point.setAttribute('stroke', 'darkred'); | |
point.setAttribute('stroke-width', '2'); | |
point.classList.add('optimal-point'); | |
svg.appendChild(point); | |
graphElements.optimalPoint = point; | |
// Add label | |
const label = document.createElementNS(svgNS, 'text'); | |
label.setAttribute('x', '265'); | |
label.setAttribute('y', '255'); | |
label.setAttribute('font-size', '14'); | |
label.textContent = 'Optimal (4,2)'; | |
svg.appendChild(label); | |
// Store for removal | |
graphElements.optimalLabel = label; | |
// Add points for finding the solution | |
addPoints(15); | |
}); | |
// Reset graph | |
document.getElementById('reset-graph').addEventListener('click', () => { | |
// Remove all constraints | |
graphElements.constraints.forEach(line => { | |
svg.removeChild(line); | |
}); | |
graphElements.constraints = []; | |
// Remove feasible region if exists | |
if (graphElements.feasibleRegion) { | |
svg.removeChild(graphElements.feasibleRegion); | |
graphElements.feasibleRegion = null; | |
} | |
// Remove objective line if exists | |
if (graphElements.objectiveLine) { | |
svg.removeChild(graphElements.objectiveLine); | |
graphElements.objectiveLine = null; | |
} | |
// Remove optimal point if exists | |
if (graphElements.optimalPoint) { | |
svg.removeChild(graphElements.optimalPoint); | |
graphElements.optimalPoint = null; | |
} | |
if (graphElements.optimalLabel) { | |
svg.removeChild(graphElements.optimalLabel); | |
graphElements.optimalLabel = null; | |
} | |
// Clear constraint list | |
document.getElementById('constraint-list').innerHTML = ''; | |
}); | |
// Standard Form Practice | |
document.getElementById('check-standard').addEventListener('click', () => { | |
const original = document.getElementById('original-problem').value.trim(); | |
const standard = document.getElementById('standard-form').value.trim(); | |
const feedback = document.getElementById('standard-feedback'); | |
if (!original || !standard) { | |
feedback.innerHTML = '<div class="bg-red-100 text-red-800 p-3 rounded">Please enter both the original problem and your standard form conversion.</div>'; | |
feedback.classList.remove('hidden'); | |
return; | |
} | |
// Simple validation (would be more robust in a real implementation) | |
if (standard.toLowerCase().includes('slack') || standard.toLowerCase().includes('surplus') || | |
standard.toLowerCase().includes('artificial') || standard.toLowerCase().includes('maximize')) { | |
feedback.innerHTML = '<div class="bg-green-100 text-green-800 p-3 rounded">Good job! Your conversion looks correct.</div>'; | |
addPoints(10); | |
} else { | |
feedback.innerHTML = '<div class="bg-yellow-100 text-yellow-800 p-3 rounded">' + | |
'<p>Check your conversion. Standard form should:</p>' + | |
'<ul class="list-disc pl-5"><li>Be a maximization problem</li>' + | |
'<li>Have equality constraints</li>' + | |
'<li>Include slack/surplus/artificial variables as needed</li></ul></div>'; | |
} | |
feedback.classList.remove('hidden'); | |
}); | |
document.getElementById('show-standard-solution').addEventListener('click', () => { | |
document.getElementById('original-problem').value = | |
"Minimize: -x₁ + 2x₂\n" + | |
"Subject to:\n" + | |
"x₁ + x₂ ≤ 6\n" + | |
"x₁ - x₂ ≥ 2\n" + | |
"x₁ + 2x₂ = 10\n" + | |
"x₁ ≥ 0, x₂ unrestricted"; | |
document.getElementById('standard-form').value = | |
"Maximize: x₁ - 2x₂⁺ + 2x₂⁻\n" + | |
"Subject to:\n" + | |
"x₁ + x₂⁺ - x₂⁻ + s₁ = 6\n" + | |
"x₁ - x₂⁺ + x₂⁻ - s₂ + a₁ = 2\n" + | |
"x₁ + 2x₂⁺ - 2x₂⁻ + a₂ = 10\n" + | |
"All variables ≥ 0"; | |
}); | |
// Simplex Method Practice | |
document.getElementById('next-step').addEventListener('click', () => { | |
const feedback = document.getElementById('simplex-feedback'); | |
feedback.innerHTML = '<div class="bg-blue-100 text-blue-800 p-3 rounded">Next step: Pivot on x₁ column (most negative in objective row) and s₂ row (minimum ratio test).</div>'; | |
feedback.classList.remove('hidden'); | |
// In a real implementation, this would perform the actual pivot operation | |
}); | |
document.getElementById('hint-simplex').addEventListener('click', () => { | |
const feedback = document.getElementById('simplex-feedback'); | |
feedback.innerHTML = '<div class="bg-yellow-100 text-yellow-800 p-3 rounded">Hint: Look for the most negative number in the objective row to choose the entering variable.</div>'; | |
feedback.classList.remove('hidden'); | |
}); | |
document.getElementById('auto-solve').addEventListener('click', () => { | |
const stepsContainer = document.getElementById('steps-container'); | |
stepsContainer.innerHTML = ` | |
<div class="bg-white p-3 rounded border border-gray-200"> | |
<p class="font-medium">Step 1: Initial Tableau</p> | |
<p>Entering variable: x₁ (most negative in objective row)</p> | |
<p>Departing variable: s₂ (minimum ratio test: 6/1=6, 8/2=4)</p> | |
</div> | |
<div class="bg-white p-3 rounded border border-gray-200"> | |
<p class="font-medium">Step 2: Pivot Operation</p> | |
<p>Divide pivot row by 2 to make pivot element 1</p> | |
<p>Update other rows to zero out x₁ column</p> | |
</div> | |
<div class="bg-white p-3 rounded border border-gray-200"> | |
<p class="font-medium">Step 3: Second Tableau</p> | |
<p>Entering variable: x₂ (most negative in objective row)</p> | |
<p>Departing variable: s₁ (minimum ratio test)</p> | |
</div> | |
<div class="bg-white p-3 rounded border border-gray-200"> | |
<p class="font-medium">Step 4: Final Tableau</p> | |
<p>All coefficients in objective row are non-negative</p> | |
<p>Optimal solution: x₁=2, x₂=4, z=14</p> | |
</div> | |
`; | |
document.getElementById('simplex-steps').classList.remove('hidden'); | |
addPoints(20); | |
}); | |
// Duality Practice | |
document.getElementById('check-dual').addEventListener('click', () => { | |
const primal = document.getElementById('primal-problem').value.trim(); | |
const dual = document.getElementById('dual-problem').value.trim(); | |
const feedback = document.getElementById('dual-feedback'); | |
if (!primal || !dual) { | |
feedback.innerHTML = '<div class="bg-red-100 text-red-800 p-3 rounded">Please enter both the primal problem and your dual formulation.</div>'; | |
feedback.classList.remove('hidden'); | |
return; | |
} | |
// Simple validation (would be more robust in a real implementation) | |
if (dual.toLowerCase().includes('min') && dual.toLowerCase().includes('y') && | |
(dual.toLowerCase().includes('≥') || dual.toLowerCase().includes('>='))) { | |
feedback.innerHTML = '<div class="bg-green-100 text-green-800 p-3 rounded">Correct! Your dual formulation looks good.</div>'; | |
addPoints(10); | |
} else { | |
feedback.innerHTML = '<div class="bg-yellow-100 text-yellow-800 p-3 rounded">' + | |
'<p>Check your dual formulation. Remember:</p>' + | |
'<ul class="list-disc pl-5"><li>Primal max becomes dual min</li>' + | |
'<li>Primal constraints become dual variables</li>' + | |
'<li>Constraint coefficients are transposed</li></ul></div>'; | |
} | |
feedback.classList.remove('hidden'); | |
}); | |
document.getElementById('show-dual-solution').addEventListener('click', () => { | |
document.getElementById('primal-problem').value = | |
"Maximize: 4x₁ + 6x₂\n" + | |
"Subject to:\n" + | |
"x₁ + 2x₂ ≤ 3\n" + | |
"x₁ + x₂ ≤ 2\n" + | |
"x₁ ≥ 0, x₂ ≥ 0"; | |
document.getElementById('dual-problem').value = | |
"Minimize: 3y₁ + 2y₂\n" + | |
"Subject to:\n" + | |
"y₁ + y₂ ≥ 4\n" + | |
"2y₁ + y₂ ≥ 6\n" + | |
"y₁ ≥ 0, y₂ ≥ 0"; | |
}); | |
// Mark Complete buttons | |
document.querySelectorAll('.mark-complete').forEach(button => { | |
button.addEventListener('click', function() { | |
// Update progress | |
const progressBar = document.getElementById('progress-bar'); | |
const progressText = document.getElementById('progress-text'); | |
let currentWidth = parseFloat(progressBar.style.width) || 0; | |
currentWidth += 20; // Each topic is 20% | |
if (currentWidth > 100) currentWidth = 100; | |
progressBar.style.width = currentWidth + '%'; | |
progressText.textContent = Math.round(currentWidth) + '% Complete'; | |
// Add points | |
addPoints(5); | |
// Show completion message | |
const parentDiv = this.parentNode.parentNode; | |
const completeMsg = document.createElement('div'); | |
completeMsg.className = 'bg-green-100 text-green-800 p-2 rounded text-sm mt-2'; | |
completeMsg.innerHTML = '<i class="fas fa-check-circle mr-1"></i> Topic marked as complete!'; | |
parentDiv.appendChild(completeMsg); | |
// Disable button | |
this.disabled = true; | |
this.classList.remove('bg-indigo-500', 'hover:bg-indigo-600'); | |
this.classList.add('bg-gray-400', 'cursor-not-allowed'); | |
}); | |
}); | |
// Flashcards | |
const flashcards = [ | |
{ question: "What is linear programming?", answer: "A mathematical method for determining the best outcome in a model with linear relationships." }, | |
{ question: "What are the three main components of an LP problem?", answer: "Objective function, decision variables, and constraints." }, | |
{ question: "What is the feasible region?", answer: "The set of all points that satisfy all constraints simultaneously." }, | |
{ question: "What is standard form in LP?", answer: "Maximization problem with equality constraints and non-negative variables." }, | |
{ question: "What is a slack variable?", answer: "A variable added to a ≤ constraint to convert it to an equation." }, | |
{ question: "What is the simplex method?", answer: "An algorithm that moves from one basic feasible solution to another to find the optimal solution." }, | |
{ question: "How do you choose the entering variable in simplex?", answer: "The non-basic variable with the most negative coefficient in the objective row." }, | |
{ question: "What is the minimum ratio test?", answer: "Used to determine the departing variable by finding the smallest non-negative ratio of RHS to pivot column coefficient." }, | |
{ question: "What is duality in LP?", answer: "Every LP problem has a corresponding dual problem with important theoretical relationships." }, | |
{ question: "What does the weak duality theorem state?", answer: "The value of any feasible dual solution is ≥ the value of any feasible primal solution." } | |
]; | |
let currentFlashcardIndex = 0; | |
// Open flashcards modal | |
document.getElementById('flashcards-btn').addEventListener('click', () => { | |
document.getElementById('flashcards-modal').style.display = 'block'; | |
showFlashcard(currentFlashcardIndex); | |
}); | |
// Close flashcards modal | |
document.getElementById('close-flashcards').addEventListener('click', () => { | |
document.getElementById('flashcards-modal').style.display = 'none'; | |
}); | |
// Show flashcard | |
function showFlashcard(index) { | |
const flashcard = document.getElementById('current-flashcard'); | |
flashcard.classList.remove('flipped'); | |
document.getElementById('flashcard-question').textContent = flashcards[index].question; | |
document.getElementById('flashcard-answer').textContent = flashcards[index].answer; | |
document.getElementById('flashcard-count').textContent = (index + 1) + '/' + flashcards.length; | |
} | |
// Flip flashcard | |
document.getElementById('current-flashcard').addEventListener('click', function() { | |
this.classList.toggle('flipped'); | |
}); | |
// Previous flashcard | |
document.getElementById('prev-flashcard').addEventListener('click', () => { | |
currentFlashcardIndex = (currentFlashcardIndex - 1 + flashcards.length) % flashcards.length; | |
showFlashcard(currentFlashcardIndex); | |
}); | |
// Next flashcard | |
document.getElementById('next-flashcard').addEventListener('click', () => { | |
currentFlashcardIndex = (currentFlashcardIndex + 1) % flashcards.length; | |
showFlashcard(currentFlashcardIndex); | |
}); | |
// Shuffle flashcards | |
document.getElementById('shuffle-flashcards').addEventListener('click', () => { | |
for (let i = flashcards.length - 1; i > 0; i--) { | |
const j = Math.floor(Math.random() * (i + 1)); | |
[flashcards[i], flashcards[j]] = [flashcards[j], flashcards[i]]; | |
} | |
currentFlashcardIndex = 0; | |
showFlashcard(currentFlashcardIndex); | |
addPoints(5); | |
}); | |
// Quiz | |
const quizQuestions = [ | |
{ | |
question: "Which of the following is NOT a requirement for standard form in LP?", | |
options: [ | |
"Maximization problem", | |
"Equality constraints", | |
"Non-negative variables", | |
"At least three variables" | |
], | |
answer: 3 | |
}, | |
{ | |
question: "In the simplex method, how is the entering variable chosen?", | |
options: [ | |
"Largest coefficient in objective row", | |
"Most negative coefficient in objective row", | |
"Random selection", | |
"Variable with smallest index" | |
], | |
answer: 1 | |
}, | |
{ | |
question: "What does the strong duality theorem state?", | |
options: [ | |
"Primal and dual problems always have the same number of variables", | |
"If one problem has an optimal solution, so does the other with equal objective values", | |
"Dual problems are always easier to solve than primal problems", | |
"All LP problems have multiple optimal solutions" | |
], | |
answer: 1 | |
}, | |
{ | |
question: "What is added to convert a ≥ constraint to standard form?", | |
options: [ | |
"Only a slack variable", | |
"Only a surplus variable", | |
"A surplus variable and an artificial variable", | |
"Only an artificial variable" | |
], | |
answer: 2 | |
}, | |
{ | |
question: "Where is the optimal solution found in graphical LP?", | |
options: [ | |
"At the center of the feasible region", | |
"At a corner point of the feasible region", | |
"At the point closest to the origin", | |
"At the intersection of the first two constraints" | |
], | |
answer: 1 | |
} | |
]; | |
let currentQuizIndex | |
</html> |