diff --git a/dynamic_programming/0/0/1_knapsack_problem.r b/dynamic_programming/0/0/1_knapsack_problem.r new file mode 100644 index 00000000..4ff1cc3a --- /dev/null +++ b/dynamic_programming/0/0/1_knapsack_problem.r @@ -0,0 +1,194 @@ +# 0/1 Knapsack Problem (Dynamic Programming) +# +# The 0/1 Knapsack problem is one of the most classic problems in dynamic programming. +# Given a set of items, each with a weight and a value, determine the maximum total value +# you can obtain by putting items in a knapsack with a fixed capacity. Each item can +# either be included (1) or excluded (0) — hence the name "0/1". +# +# Time Complexity: O(n * W) where n = number of items, W = knapsack capacity +# Space Complexity: O(n * W) for full DP table, O(W) for optimized version +# +# Applications: +# - Budget allocation problems +# - Resource optimization (CPU scheduling, project selection) +# - Portfolio selection in finance +# - Cargo loading and packing +# - Subset optimization in AI planning + +# Classic DP solution for 0/1 Knapsack +knapsack_01 <- function(weights, values, capacity) { + #' Solve 0/1 Knapsack Problem using Dynamic Programming + #' @param weights: Numeric vector of item weights + #' @param values: Numeric vector of item values + #' @param capacity: Maximum weight capacity of the knapsack + #' @return: List containing max value, selected items, and DP table + + n <- length(values) + + # Handle edge case + if (n == 0 || capacity == 0) { + return(list( + max_value = 0, + selected_items = c(), + dp_table = matrix(0, nrow = n + 1, ncol = capacity + 1) + )) + } + + # Create DP table: dp[i, w] = max value using first i items with capacity w + dp <- matrix(0, nrow = n + 1, ncol = capacity + 1) + + # Fill DP table + for (i in 1:n) { + for (w in 0:capacity) { + # Don't include item i + dp[i + 1, w + 1] <- dp[i, w + 1] + + # Include item i (if it fits) + if (weights[i] <= w) { + include_value <- values[i] + dp[i, w - weights[i] + 1] + dp[i + 1, w + 1] <- max(dp[i + 1, w + 1], include_value) + } + } + } + + # Backtrack to find selected items + selected <- c() + i <- n + w <- capacity + + while (i > 0 && w > 0) { + # If value came from including item i + if (dp[i + 1, w + 1] != dp[i, w + 1]) { + selected <- c(i, selected) + w <- w - weights[i] + } + i <- i - 1 + } + + return(list( + max_value = dp[n + 1, capacity + 1], + selected_items = selected, + dp_table = dp + )) +} + +# Space-optimized version using only 1D array +knapsack_01_optimized <- function(weights, values, capacity) { + #' Space optimized 0/1 Knapsack using 1D array + #' @return: Maximum total value + + n <- length(values) + + if (n == 0 || capacity == 0) { + return(0) + } + + dp <- rep(0, capacity + 1) + + # Process each item + for (i in 1:n) { + # Traverse from right to left to avoid overwriting needed values + for (w in capacity:weights[i]) { + if (weights[i] <= w) { + dp[w + 1] <- max(dp[w + 1], values[i] + dp[w - weights[i] + 1]) + } + } + } + + return(dp[capacity + 1]) +} + +# Helper function to print DP table +print_knapsack_dp <- function(dp_table, weights, values, capacity) { + cat("DP Table for 0/1 Knapsack:\n") + cat("Weights:", paste(weights, collapse = ", "), "\n") + cat("Values :", paste(values, collapse = ", "), "\n") + cat("Capacity:", capacity, "\n\n") + + # Print capacity headers + cat(" ") + cat(paste(sprintf("%3d", 0:capacity), collapse = " ")) + cat("\n") + cat(paste(rep("-", 8 + 4 * (capacity + 1)), collapse = ""), "\n") + + for (i in 1:nrow(dp_table)) { + cat(sprintf("Item %2d | ", i - 1)) + cat(paste(sprintf("%3d", dp_table[i, ]), collapse = " ")) + cat("\n") + } + cat("\n") +} + +# =========================== +# Example Usage & Testing +# =========================== +cat("=== 0/1 Knapsack Problem (Dynamic Programming) ===\n\n") + +# Test 1: Basic Example +weights <- c(1, 3, 4, 5) +values <- c(1, 4, 5, 7) +capacity <- 7 + +cat("Test 1: Basic Example\n") +cat("Weights:", paste(weights, collapse = ", "), "\n") +cat("Values :", paste(values, collapse = ", "), "\n") +cat("Capacity:", capacity, "\n\n") + +result <- knapsack_01(weights, values, capacity) +print_knapsack_dp(result$dp_table, weights, values, capacity) +cat("Maximum Value:", result$max_value, "\n") +cat("Selected Item Indices:", paste(result$selected_items, collapse = ", "), "\n") +cat("Total Weight:", sum(weights[result$selected_items]), "\n") +cat("Total Value:", sum(values[result$selected_items]), "\n\n") + +# Test 2: Space Optimized Example +cat("Test 2: Space Optimized Version\n") +max_val_opt <- knapsack_01_optimized(weights, values, capacity) +cat("Maximum Value (Optimized):", max_val_opt, "\n") +cat("Verification: Both methods match:", result$max_value == max_val_opt, "\n\n") + +# Test 3: Larger Dataset +cat("Test 3: Larger Dataset\n") +set.seed(42) +weights <- sample(1:15, 10) +values <- sample(10:100, 10) +capacity <- 35 + +cat("Weights:", paste(weights, collapse = ", "), "\n") +cat("Values :", paste(values, collapse = ", "), "\n") +cat("Capacity:", capacity, "\n\n") + +large_result <- knapsack_01(weights, values, capacity) +cat("Maximum Value:", large_result$max_value, "\n") +cat("Selected Items:", paste(large_result$selected_items, collapse = ", "), "\n") +cat("Total Weight:", sum(weights[large_result$selected_items]), "\n\n") + +# Test 4: Edge Cases +cat("Test 4: Edge Cases\n") +cat("Empty items:", knapsack_01(c(), c(), 10)$max_value, "\n") +cat("Zero capacity:", knapsack_01(weights, values, 0)$max_value, "\n") +cat("Single item fits:", knapsack_01(c(5), c(10), 10)$max_value, "\n") +cat("Single item doesn't fit:", knapsack_01(c(10), c(10), 5)$max_value, "\n\n") + +# Test 5: Performance Check +cat("Test 5: Performance Comparison (n=100)\n") +n <- 100 +weights <- sample(1:15, n, replace = TRUE) +values <- sample(10:100, n, replace = TRUE) +capacity <- 200 + +start_time <- Sys.time() +res_std <- knapsack_01_optimized(weights, values, capacity) +std_time <- as.numeric(Sys.time() - start_time, units = "secs") + +cat("Optimized DP result:", res_std, "\n") +cat("Time taken:", sprintf("%.4f sec", std_time), "\n") + +# Verify correctness +cat("\nVerifying correctness with full DP:\n") +start_time <- Sys.time() +res_full <- knapsack_01(weights, values, capacity) +full_time <- as.numeric(Sys.time() - start_time, units = "secs") +cat("Full DP result:", res_full$max_value, "\n") +cat("Time taken:", sprintf("%.4f sec", full_time), "\n") +cat("Results match:", res_std == res_full$max_value, "\n") \ No newline at end of file