From 8b30acf58c4636541e76801c4f9f22dfcd53d85b Mon Sep 17 00:00:00 2001 From: Prathamesh Kalshetti Date: Sat, 18 Oct 2025 11:44:39 +0530 Subject: [PATCH 1/6] bidirectional_bfs --- graph_algorithms/bidirectional_bfs.r | 153 +++++++++++++++++++++++++++ 1 file changed, 153 insertions(+) create mode 100644 graph_algorithms/bidirectional_bfs.r diff --git a/graph_algorithms/bidirectional_bfs.r b/graph_algorithms/bidirectional_bfs.r new file mode 100644 index 00000000..8daf6f38 --- /dev/null +++ b/graph_algorithms/bidirectional_bfs.r @@ -0,0 +1,153 @@ +# ============================================================== +# Bidirectional Breadth-First Search (BFS) Shortest Path Algorithm +# ============================================================== +# +# Description: +# Finds the shortest path between a source and target in an +# unweighted graph using Bidirectional BFS. +# +# Time Complexity: O(b^(d/2)) — much faster than normal BFS O(b^d) +# Space Complexity: O(V) +# +# Input: +# graph - adjacency list (list of integer vectors) +# source - integer (starting vertex) +# target - integer (destination vertex) +# +# Output: +# A list containing: +# path - vector of vertices representing the path +# distance - number of edges in the shortest path +# found - logical flag (TRUE if path found, else FALSE) +# +# Example usage at bottom of file. +# ============================================================== + +bidirectional_bfs <- function(graph, source, target) { + if (source == target) { + return(list(path = c(source), distance = 0, found = TRUE)) + } + + # Initialize BFS from both ends + visited_from_source <- setNames(rep(FALSE, length(graph)), names(graph)) + visited_from_target <- setNames(rep(FALSE, length(graph)), names(graph)) + + parent_from_source <- rep(NA, length(graph)) + parent_from_target <- rep(NA, length(graph)) + + queue_source <- c(source) + queue_target <- c(target) + + visited_from_source[source] <- TRUE + visited_from_target[target] <- TRUE + + meeting_node <- NA + + # Function to check intersection + get_intersection <- function() { + common <- which(visited_from_source & visited_from_target) + if (length(common) > 0) return(common[1]) + return(NA) + } + + # Main loop + while (length(queue_source) > 0 && length(queue_target) > 0) { + # Expand one level from source side + next_queue <- c() + for (u in queue_source) { + for (v in graph[[as.character(u)]]) { + if (!visited_from_source[v]) { + visited_from_source[v] <- TRUE + parent_from_source[v] <- u + next_queue <- c(next_queue, v) + } + } + } + queue_source <- next_queue + + # Check intersection + meeting_node <- get_intersection() + if (!is.na(meeting_node)) break + + # Expand one level from target side + next_queue <- c() + for (u in queue_target) { + for (v in graph[[as.character(u)]]) { + if (!visited_from_target[v]) { + visited_from_target[v] <- TRUE + parent_from_target[v] <- u + next_queue <- c(next_queue, v) + } + } + } + queue_target <- next_queue + + # Check intersection again + meeting_node <- get_intersection() + if (!is.na(meeting_node)) break + } + + if (is.na(meeting_node)) { + return(list(path = NULL, distance = Inf, found = FALSE)) + } + + # Reconstruct path from source → meeting_node + path1 <- c() + node <- meeting_node + while (!is.na(node)) { + path1 <- c(node, path1) + node <- parent_from_source[node] + } + + # Reconstruct path from meeting_node → target + path2 <- c() + node <- parent_from_target[meeting_node] + while (!is.na(node)) { + path2 <- c(path2, node) + node <- parent_from_target[node] + } + + full_path <- c(path1, path2) + return(list(path = full_path, distance = length(full_path) - 1, found = TRUE)) +} + +# ============================================================== +# Example Usage and Test +# ============================================================== + +cat("=== Bidirectional BFS Shortest Path ===\n") + +# Example Graph (Unweighted) +# 1 -- 2 -- 3 +# | | +# 4 -- 5 -- 6 + +graph <- list( + "1" = c(2, 4), + "2" = c(1, 3, 5), + "3" = c(2, 6), + "4" = c(1, 5), + "5" = c(2, 4, 6), + "6" = c(3, 5) +) + +cat("Graph adjacency list:\n") +for (v in names(graph)) { + cat("Vertex", v, "-> [", paste(graph[[v]], collapse = ", "), "]\n") +} + +cat("\nRunning Bidirectional BFS from 1 to 6...\n") +result <- bidirectional_bfs(graph, 1, 6) + +if (result$found) { + cat("Shortest Path Found!\n") + cat("Path:", paste(result$path, collapse = " -> "), "\n") + cat("Distance:", result$distance, "\n") +} else { + cat("No path found between source and target.\n") +} + return(list( + distances = distances, + predecessor = predecessor, + found = found + )) \ No newline at end of file From 4921341b2921457245427d4b465e0eb1478f28e6 Mon Sep 17 00:00:00 2001 From: Prathamesh Kalshetti Date: Sat, 18 Oct 2025 12:35:09 +0530 Subject: [PATCH 2/6] viterbi --- dynamic_programming/viterbi.r | 113 ++++++++++++++++++++++++++++++++++ 1 file changed, 113 insertions(+) create mode 100644 dynamic_programming/viterbi.r diff --git a/dynamic_programming/viterbi.r b/dynamic_programming/viterbi.r new file mode 100644 index 00000000..7d656562 --- /dev/null +++ b/dynamic_programming/viterbi.r @@ -0,0 +1,113 @@ +# ============================================================== +# Viterbi Algorithm — Hidden Markov Model (HMM) Decoding +# ============================================================== +# +# Description: +# The Viterbi algorithm finds the most probable sequence of +# hidden states (state path) that results in a given sequence of +# observed events in a Hidden Markov Model. +# +# Time Complexity: O(N * T) +# - N = number of hidden states +# - T = length of observation sequence +# +# Space Complexity: O(N * T) +# +# Input: +# states - vector of hidden states +# observations - vector of observed symbols +# start_prob - named vector of initial probabilities (state → prob) +# trans_prob - matrix of transition probabilities (from_state → to_state) +# emit_prob - matrix of emission probabilities (state → observation) +# +# Output: +# A list containing: +# best_path - most probable state sequence +# best_prob - probability of the best path +# +# Example usage provided at bottom of file. +# ============================================================== + +viterbi <- function(states, observations, start_prob, trans_prob, emit_prob) { + N <- length(states) + T_len <- length(observations) + + # Initialize matrices + V <- matrix(0, nrow = N, ncol = T_len) # probability table + path <- matrix(NA, nrow = N, ncol = T_len) # backpointer table + + # Initialization step + for (i in 1:N) { + V[i, 1] <- start_prob[states[i]] * emit_prob[states[i], observations[1]] + path[i, 1] <- 0 + } + + # Recursion step + for (t in 2:T_len) { + for (j in 1:N) { + probs <- V[, t - 1] * trans_prob[, states[j]] * emit_prob[states[j], observations[t]] + V[j, t] <- max(probs) + path[j, t] <- which.max(probs) + } + } + + # Termination step + best_last_state <- which.max(V[, T_len]) + best_prob <- V[best_last_state, T_len] + + # Backtrack the best path + best_path <- rep(NA, T_len) + best_path[T_len] <- best_last_state + + for (t in (T_len - 1):1) { + best_path[t] <- path[best_path[t + 1], t + 1] + } + + best_state_sequence <- states[best_path] + + return(list( + best_path = best_state_sequence, + best_prob = best_prob + )) +} + +# ============================================================== +# Example Usage and Test +# ============================================================== + +cat("=== Viterbi Algorithm — Hidden Markov Model ===\n") + +# Example: Weather HMM +# States: Rainy, Sunny +# Observations: walk, shop, clean +states <- c("Rainy", "Sunny") +observations <- c("walk", "shop", "clean") + +# Start probabilities +start_prob <- c(Rainy = 0.6, Sunny = 0.4) + +# Transition probabilities +trans_prob <- matrix(c( + 0.7, 0.3, # from Rainy to (Rainy, Sunny) + 0.4, 0.6 # from Sunny to (Rainy, Sunny) +), nrow = 2, byrow = TRUE) +rownames(trans_prob) <- states +colnames(trans_prob) <- states + +# Emission probabilities +emit_prob <- matrix(c( + 0.1, 0.4, 0.5, # Rainy emits (walk, shop, clean) + 0.6, 0.3, 0.1 # Sunny emits (walk, shop, clean) +), nrow = 2, byrow = TRUE) +rownames(emit_prob) <- states +colnames(emit_prob) <- observations + +# Observed sequence +obs_seq <- c("walk", "shop", "clean") + +cat("Observation sequence:", paste(obs_seq, collapse = ", "), "\n") +result <- viterbi(states, obs_seq, start_prob, trans_prob, emit_prob) + +cat("Most probable state sequence:\n") +cat(paste(result$best_path, collapse = " -> "), "\n") +cat("Probability of this sequence:", result$best_prob, "\n") From a0236f8c81c6356eee2f535bca6ed95a19bc8b4a Mon Sep 17 00:00:00 2001 From: Prathamesh Kalshetti Date: Mon, 20 Oct 2025 23:26:05 +0530 Subject: [PATCH 3/6] Update dynamic_programming/viterbi.r Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- dynamic_programming/viterbi.r | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dynamic_programming/viterbi.r b/dynamic_programming/viterbi.r index 7d656562..b51294af 100644 --- a/dynamic_programming/viterbi.r +++ b/dynamic_programming/viterbi.r @@ -23,7 +23,7 @@ # Output: # A list containing: # best_path - most probable state sequence -# best_prob - probability of the best path +# best_prob - probability of the best path # # Example usage provided at bottom of file. # ============================================================== From e73a1b8dcee3ac3ad75d5e68005fbb3e39d23264 Mon Sep 17 00:00:00 2001 From: Prathamesh Kalshetti Date: Mon, 20 Oct 2025 23:41:17 +0530 Subject: [PATCH 4/6] Update dynamic_programming/viterbi.r Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- dynamic_programming/viterbi.r | 60 ++++++++++++++++++----------------- 1 file changed, 31 insertions(+), 29 deletions(-) diff --git a/dynamic_programming/viterbi.r b/dynamic_programming/viterbi.r index b51294af..88b7a948 100644 --- a/dynamic_programming/viterbi.r +++ b/dynamic_programming/viterbi.r @@ -75,39 +75,41 @@ viterbi <- function(states, observations, start_prob, trans_prob, emit_prob) { # Example Usage and Test # ============================================================== -cat("=== Viterbi Algorithm — Hidden Markov Model ===\n") +if (!exists(".test_mode")) { + cat("=== Viterbi Algorithm — Hidden Markov Model ===\n") -# Example: Weather HMM -# States: Rainy, Sunny -# Observations: walk, shop, clean -states <- c("Rainy", "Sunny") -observations <- c("walk", "shop", "clean") + # Example: Weather HMM + # States: Rainy, Sunny + # Observations: walk, shop, clean + states <- c("Rainy", "Sunny") + observations <- c("walk", "shop", "clean") -# Start probabilities -start_prob <- c(Rainy = 0.6, Sunny = 0.4) + # Start probabilities + start_prob <- c(Rainy = 0.6, Sunny = 0.4) -# Transition probabilities -trans_prob <- matrix(c( - 0.7, 0.3, # from Rainy to (Rainy, Sunny) - 0.4, 0.6 # from Sunny to (Rainy, Sunny) -), nrow = 2, byrow = TRUE) -rownames(trans_prob) <- states -colnames(trans_prob) <- states + # Transition probabilities + trans_prob <- matrix(c( + 0.7, 0.3, # from Rainy to (Rainy, Sunny) + 0.4, 0.6 # from Sunny to (Rainy, Sunny) + ), nrow = 2, byrow = TRUE) + rownames(trans_prob) <- states + colnames(trans_prob) <- states -# Emission probabilities -emit_prob <- matrix(c( - 0.1, 0.4, 0.5, # Rainy emits (walk, shop, clean) - 0.6, 0.3, 0.1 # Sunny emits (walk, shop, clean) -), nrow = 2, byrow = TRUE) -rownames(emit_prob) <- states -colnames(emit_prob) <- observations + # Emission probabilities + emit_prob <- matrix(c( + 0.1, 0.4, 0.5, # Rainy emits (walk, shop, clean) + 0.6, 0.3, 0.1 # Sunny emits (walk, shop, clean) + ), nrow = 2, byrow = TRUE) + rownames(emit_prob) <- states + colnames(emit_prob) <- observations -# Observed sequence -obs_seq <- c("walk", "shop", "clean") + # Observed sequence + obs_seq <- c("walk", "shop", "clean") -cat("Observation sequence:", paste(obs_seq, collapse = ", "), "\n") -result <- viterbi(states, obs_seq, start_prob, trans_prob, emit_prob) + cat("Observation sequence:", paste(obs_seq, collapse = ", "), "\n") + result <- viterbi(states, obs_seq, start_prob, trans_prob, emit_prob) -cat("Most probable state sequence:\n") -cat(paste(result$best_path, collapse = " -> "), "\n") -cat("Probability of this sequence:", result$best_prob, "\n") + cat("Most probable state sequence:\n") + cat(paste(result$best_path, collapse = " -> "), "\n") + cat("Probability of this sequence:", result$best_prob, "\n") +} From 9d8d9d3ced22ad1bf389a240378b69aa80609e25 Mon Sep 17 00:00:00 2001 From: Prathamesh Kalshetti Date: Mon, 20 Oct 2025 23:41:59 +0530 Subject: [PATCH 5/6] Update dynamic_programming/viterbi.r Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- dynamic_programming/viterbi.r | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/dynamic_programming/viterbi.r b/dynamic_programming/viterbi.r index 88b7a948..89958005 100644 --- a/dynamic_programming/viterbi.r +++ b/dynamic_programming/viterbi.r @@ -59,8 +59,10 @@ viterbi <- function(states, observations, start_prob, trans_prob, emit_prob) { best_path <- rep(NA, T_len) best_path[T_len] <- best_last_state - for (t in (T_len - 1):1) { - best_path[t] <- path[best_path[t + 1], t + 1] + if (T_len > 1) { + for (t in (T_len - 1):1) { + best_path[t] <- path[best_path[t + 1], t + 1] + } } best_state_sequence <- states[best_path] From 411216bf076c6cc08b00d5f22cb4c676468fd8b3 Mon Sep 17 00:00:00 2001 From: Prathamesh Kalshetti Date: Mon, 20 Oct 2025 23:42:08 +0530 Subject: [PATCH 6/6] Update dynamic_programming/viterbi.r Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- dynamic_programming/viterbi.r | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/dynamic_programming/viterbi.r b/dynamic_programming/viterbi.r index 89958005..56105392 100644 --- a/dynamic_programming/viterbi.r +++ b/dynamic_programming/viterbi.r @@ -43,11 +43,13 @@ viterbi <- function(states, observations, start_prob, trans_prob, emit_prob) { } # Recursion step - for (t in 2:T_len) { - for (j in 1:N) { - probs <- V[, t - 1] * trans_prob[, states[j]] * emit_prob[states[j], observations[t]] - V[j, t] <- max(probs) - path[j, t] <- which.max(probs) + if (T_len > 1) { + for (t in 2:T_len) { + for (j in 1:N) { + probs <- V[, t - 1] * trans_prob[, states[j]] * emit_prob[states[j], observations[t]] + V[j, t] <- max(probs) + path[j, t] <- which.max(probs) + } } }