diff --git a/src/App.jsx b/src/App.jsx
index 5cbaaa8..02b7f41 100644
--- a/src/App.jsx
+++ b/src/App.jsx
@@ -10,6 +10,7 @@ import Searchingpage from "./pages/searching/searchingPage";
import RecursionPage from "./pages/Recursion/RecursionPage";
import Treepage from "./pages/Tree/Treepage";
import SlidingWindowPage from "./pages/sliding-window/SlidingWindowPage";
+import GreedyPage from "./pages/greedy/GreedyPage";
function App() {
return (
@@ -24,6 +25,7 @@ function App() {
}/>
} />
}/>
+ } />
);
diff --git a/src/algorithms/greedy/fractionalKnapsack.js b/src/algorithms/greedy/fractionalKnapsack.js
new file mode 100644
index 0000000..29911f0
--- /dev/null
+++ b/src/algorithms/greedy/fractionalKnapsack.js
@@ -0,0 +1,91 @@
+export function* fractionalKnapsack(weights, values, capacity) {
+ const n = weights.length;
+ const items = [];
+ for (let i = 0; i < n; i++) {
+ items.push({index: i, weight: weights[i], value: values[i], ratio: values[i] / weights[i]});
+ }
+
+ yield {
+ type: "calculate_ratios",
+ message: "Calculating value-to-weight ratio for each item",
+ items: items.map(item => ({ ...item })),
+ capacity,
+ selectedItems: [],
+ totalValue: 0,
+ remainingCapacity: capacity
+ };
+
+ items.sort((a, b) => b.ratio - a.ratio);
+
+ yield {
+ type: "sort_items",
+ message: "Sorting items by value-to-weight ratio (descending) - Greedy approach",
+ items: items.map(item => ({ ...item })), capacity, selectedItems: [], totalValue: 0, remainingCapacity: capacity
+ };
+
+ const selectedItems = [];
+ let totalValue = 0;
+ let remainingCapacity = capacity;
+
+ for (let i = 0; i < items.length; i++) {
+ const item = items[i];
+
+ yield {
+ type: "consider_item",
+ message: `Considering item ${item.index + 1}: weight=${item.weight}, value=${item.value}, ratio=${item.ratio.toFixed(2)}`,
+ items: items.map(item => ({ ...item })),
+ currentItem: { ...item }, capacity, selectedItems: selectedItems.map(si => ({ ...si })), totalValue, remainingCapacity
+ };
+
+ if (remainingCapacity <= 0) {
+ yield {
+ type: "skip_item",
+ message: `Skipping item ${item.index + 1}: No remaining capacity`,
+ items: items.map(item => ({ ...item })),
+ currentItem: { ...item },capacity, selectedItems: selectedItems.map(si => ({ ...si })), totalValue, remainingCapacity
+ };
+ break;
+ }
+
+ if (item.weight <= remainingCapacity) {
+ const fraction = 1.0;
+ const itemValue = item.value;
+ const itemWeight = item.weight;
+
+ selectedItems.push({index: item.index, weight: itemWeight, value: itemValue, ratio: item.ratio, fraction: fraction, actualWeight: itemWeight, actualValue: itemValue});
+
+ totalValue += itemValue;
+ remainingCapacity -= itemWeight;
+
+ yield {
+ type: "take_full",
+ message: `Taking full item ${item.index + 1}: ${itemWeight}kg (value: ${itemValue})`,
+ items: items.map(item => ({ ...item })),
+ currentItem: { ...item },
+ capacity,
+ selectedItems: selectedItems.map(si => ({ ...si })),
+ totalValue,
+ remainingCapacity
+ };
+ } else {
+ const fraction = remainingCapacity / item.weight;
+ const actualWeight = remainingCapacity;
+ const actualValue = item.value * fraction;
+
+ selectedItems.push({index: item.index, weight: item.weight, value: item.value, ratio: item.ratio, fraction: fraction, actualWeight: actualWeight, actualValue: actualValue});
+
+ totalValue += actualValue;
+ remainingCapacity = 0;
+
+ yield {
+ type: "take_fraction",
+ message: `Taking ${(fraction * 100).toFixed(1)}% of item ${item.index + 1}: ${actualWeight.toFixed(2)}kg (value: ${actualValue.toFixed(2)})`,
+ items: items.map(item => ({ ...item })), currentItem: { ...item }, capacity, selectedItems: selectedItems.map(si => ({ ...si })), totalValue, remainingCapacity, fraction
+ };
+ break;
+ }
+ }
+
+ yield {type: "complete", message: `Knapsack filled! Total value: ${totalValue.toFixed(2)}`, items: items.map(item => ({ ...item })), capacity, selectedItems: selectedItems.map(si => ({ ...si })), totalValue, remainingCapacity};
+ return { selectedItems, totalValue, remainingCapacity};
+}
diff --git a/src/components/greedy/FractionalKnapsackVisualizer.jsx b/src/components/greedy/FractionalKnapsackVisualizer.jsx
new file mode 100644
index 0000000..5a09c93
--- /dev/null
+++ b/src/components/greedy/FractionalKnapsackVisualizer.jsx
@@ -0,0 +1,161 @@
+import React from "react";
+
+export default function FractionalKnapsackVisualizer({items = [], currentStep = null, capacity = 0}) {
+ if (!currentStep) {
+ return (
+
+ );
+ }
+ const step = currentStep;
+ const selectedItems = step.selectedItems || [];
+ const totalValue = step.totalValue || 0;
+ const remainingCapacity = step.remainingCapacity !== undefined ? step.remainingCapacity : capacity;
+ const usedCapacity = capacity - remainingCapacity;
+
+ const getItemStatus = (item) => {
+ const selected = selectedItems.find(si => si.index === item.index);
+
+ if (selected) {
+ return selected.fraction === 1.0 ? "full" : "partial";
+ }
+
+ if (step.currentItem && step.currentItem.index === item.index) {
+ if (step.type === "consider_item") {
+ return "considering";
+ } else if (step.type === "skip_item") {
+ return "skipped";
+ }
+ }
+
+ return "not_selected";
+ };
+
+ const getItemClass = (status) => {
+ switch (status) {
+ case "full":
+ return "bg-green-600 border-green-500";
+ case "partial":
+ return "bg-yellow-600 border-yellow-500";
+ case "considering":
+ return "bg-blue-600 border-blue-500";
+ case "skipped":
+ return "bg-gray-600 border-gray-500 opacity-50";
+ default:
+ return "bg-gray-700 border-gray-600";
+ }
+ };
+ return (
+
+
+
Items (Sorted by Value/Weight Ratio)
+
+ {step.items?.map((item, idx) => {
+ const status = getItemStatus(item);
+ const selected = selectedItems.find(si => si.index === item.index);
+ return (
+
+
+
Item {item.index + 1}
+
+ {item.ratio.toFixed(2)}
+
+
+
+
+ W:
+ {item.weight}
+
+
+ V:
+ {item.value}
+
+ {selected && (
+
+
+ {selected.fraction === 1.0
+ ? "100%"
+ : `${(selected.fraction * 100).toFixed(0)}%`}
+
+
+ {selected.actualWeight.toFixed(1)}kg
+
+
+ )}
+
+
+ );
+ })}
+
+
+
+
Knapsack
+
+
+
+ Capacity
+
+ {usedCapacity.toFixed(1)} / {capacity} kg
+
+
+
+
+ {capacity > 0 ? ((usedCapacity / capacity) * 100).toFixed(0) : 0}%
+
+
+
+ {selectedItems.length > 0 && (
+
+
Selected Items:
+
+ {selectedItems.map((item, idx) => (
+
+
+
Item {item.index + 1}
+
+ {item.fraction === 1.0 ? "Full" : `${(item.fraction * 100).toFixed(0)}%`}
+
+
+ {item.actualWeight.toFixed(1)}kg
+
+
+ V: {item.actualValue.toFixed(1)}
+
+
+
+ ))}
+
+
+ )}
+
+
+ Total Value:
+ {totalValue.toFixed(2)}
+
+ {remainingCapacity > 0 && step.type === "complete" && (
+
+ Remaining: {remainingCapacity.toFixed(1)} kg
+
+ )}
+
+
+
+
+ );
+}
+
diff --git a/src/pages/Homepage.jsx b/src/pages/Homepage.jsx
index f643a23..aaf662a 100644
--- a/src/pages/Homepage.jsx
+++ b/src/pages/Homepage.jsx
@@ -89,6 +89,15 @@ const sections = [
link: "/tree",
flag: false,
},
+ {
+ title: "Greedy Algorithms",
+ description:
+ "Watch how greedy choices lead to optimal solutions in problems like Fractional Knapsack.",
+ phase: "Phase 2",
+ img: "",
+ link: "/greedy",
+ flag: false,
+ },
];
const Homepage = () => {
diff --git a/src/pages/greedy/FractionalKnapsack.jsx b/src/pages/greedy/FractionalKnapsack.jsx
new file mode 100644
index 0000000..353fae1
--- /dev/null
+++ b/src/pages/greedy/FractionalKnapsack.jsx
@@ -0,0 +1,235 @@
+import React, { useState, useEffect, useRef } from "react";
+import { Toaster, toast } from "react-hot-toast";
+import { ArrowLeft, Play, Pause, StepForward, RotateCcw } from "lucide-react";
+import FractionalKnapsackVisualizer from "../../components/greedy/FractionalKnapsackVisualizer";
+import { fractionalKnapsack } from "../../algorithms/greedy/fractionalKnapsack";
+
+export default function FractionalKnapsackPage() {
+ const [weights, setWeights] = useState([10, 20, 30]);
+ const [values, setValues] = useState([60, 100, 120]);
+ const [capacity, setCapacity] = useState(50);
+ const [weightsInput, setWeightsInput] = useState("10,20,30");
+ const [valuesInput, setValuesInput] = useState("60,100,120");
+ const [capacityInput, setCapacityInput] = useState("50");
+ const [speed, setSpeed] = useState(1000);
+ const [isPlaying, setIsPlaying] = useState(false);
+ const [currentStep, setCurrentStep] = useState(null);
+ const [steps, setSteps] = useState([]);
+ const [stepIndex, setStepIndex] = useState(-1);
+ const timerRef = useRef(null);
+
+ useEffect(() => {
+ if (weights.length === 0 || values.length === 0 || capacity <= 0) {
+ setSteps([]);
+ setStepIndex(-1);
+ setCurrentStep(null);
+ return;
+ }
+ if (weights.length !== values.length) {
+ toast.error("Weights and Values arrays must have the same length!");
+ return;
+ }
+ setIsPlaying(false);
+ clearTimeout(timerRef.current);
+ const generator = fractionalKnapsack(weights, values, capacity);
+ setSteps(Array.from(generator));
+ setStepIndex(0);
+ setCurrentStep(steps[0] || null);
+ }, [weights, values, capacity]);
+
+ useEffect(() => {
+ if (isPlaying && stepIndex < steps.length - 1) {
+ timerRef.current = setTimeout(() => {
+ setStepIndex((prev) =>
+ prev + 1 < steps.length ? prev + 1 : steps.length - 1
+ );
+ }, speed);
+ } else if (stepIndex >= steps.length - 1) {
+ setIsPlaying(false);
+ }
+ return () => clearTimeout(timerRef.current);
+ }, [isPlaying, stepIndex, steps.length, speed]);
+
+ useEffect(() => {
+ if (stepIndex >= 0 && stepIndex < steps.length) {
+ setCurrentStep(steps[stepIndex]);
+ }
+ }, [stepIndex, steps]);
+
+ const handleWeightsChange = (e) => {
+ const value = e.target.value;
+ setWeightsInput(value);
+ try {
+ const parsed = value
+ .split(",")
+ .map((p) => p.trim())
+ .filter((p) => p !== "")
+ .map((p) => Number(p))
+ .filter((p) => !isNaN(p) && p > 0);
+ if (parsed.length > 0) {
+ setWeights(parsed);
+ }
+ } catch (err) {
+ console.error("Error parsing weights:", err);
+ }
+ };
+ const handleValuesChange = (e) => {
+ const value = e.target.value;
+ setValuesInput(value);
+ try {
+ const parsed = value
+ .split(",")
+ .map((p) => p.trim())
+ .filter((p) => p !== "")
+ .map((p) => Number(p))
+ .filter((p) => !isNaN(p) && p > 0);
+ if (parsed.length > 0) {
+ setValues(parsed);
+ }
+ } catch (err) {
+ console.error("Error parsing values:", err);
+ }
+ };
+
+ const handleCapacityChange = (e) => {
+ const value = e.target.value;
+ setCapacityInput(value);
+ const parsed = Number(value);
+ if (!isNaN(parsed) && parsed > 0) {
+ setCapacity(parsed);
+ }
+ };
+ const loadDemo = () => {
+ setWeights([10, 20, 30]);
+ setValues([60, 100, 120]);
+ setCapacity(50);
+ setWeightsInput("10,20,30");
+ setValuesInput("60,100,120");
+ setCapacityInput("50");
+ };
+ const togglePlay = () => {
+ if (steps.length === 0) return;
+ if (stepIndex >= steps.length - 1) {
+ setStepIndex(0);
+ setIsPlaying(true);
+ } else {
+ setIsPlaying(!isPlaying);
+ }
+ };
+ const reset = () => {
+ setIsPlaying(false);
+ clearTimeout(timerRef.current);
+ setSteps([]);
+ setStepIndex(-1);
+ setCurrentStep(null);
+ };
+
+ return (
+
+
+
+
+
+
+
+ setSpeed(Number(e.target.value))}
+ className="accent-blue-500 w-24"
+ />
+ {speed}ms
+
+
+
+
+
+
+
+
+
+
+
+ Fractional Knapsack Problem
+
+
+ {currentStep && (
+
+
+ Step {stepIndex + 1} / {steps.length}
+
+
+ {currentStep.type?.replace(/_/g, " ").toUpperCase()}
+
+
+ )}
+
+
+ {currentStep && currentStep.message && (
+
+
+ {currentStep.message}
+
+
+ )}
+
+
+ {currentStep ? (
+
+ ) : (
+ Enter weights, values, and capacity to start
+ )}
+
+
+ );
+}
+
diff --git a/src/pages/greedy/GreedyPage.jsx b/src/pages/greedy/GreedyPage.jsx
new file mode 100644
index 0000000..4ea17c8
--- /dev/null
+++ b/src/pages/greedy/GreedyPage.jsx
@@ -0,0 +1,64 @@
+import React, { useState } from "react";
+import { Target } from "lucide-react";
+import FractionalKnapsackPage from "./FractionalKnapsack";
+
+export default function GreedyPage() {
+ const [selectedAlgo, setSelectedAlgo] = useState("");
+ const renderAlgorithm = () => {
+ switch (selectedAlgo) {
+ case "fractional-knapsack":
+ return ;
+ default:
+ return (
+
+
+
+
+
+
+
+ Greedy Algorithms Visualizer
+
+
+ Select a greedy algorithm from the sidebar to begin
+ visualization. Watch how greedy choices lead to optimal solutions!
+
+
+
+
+ );
+ }
+ };
+ return (
+
+ {/* Sidebar */}
+
+
+ Greedy Panel
+
+
+
+
+
+ ← Back to Home
+
+
+
+
+ {renderAlgorithm()}
+
+
+
+ );
+}
+