Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions documentation/boruvka_mst.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Boruvka's Minimum Spanning Tree (MST)

This document describes the Boruvka MST implementation located at `R/graph_algorithms/boruvka_mst.r`.

## Description

The implementation builds a Minimum Spanning Tree for an undirected weighted graph using Boruvka's method. The graph is represented by a list with `V` (number of vertices) and `edges` (a data.frame with columns `u`, `v`, `w`). Vertex indices are 1-based to match other algorithms in the repository.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please remove separate docs


## Usage

In an R session:

source('graph_algorithms/boruvka_mst.r')

From command line using Rscript:

Rscript -e "source('R/graph_algorithms/boruvka_mst.r')"

## Complexity

- Time complexity: Depends on implementation details; this simple version iterates until components merge.
- Space complexity: O(V + E)

## Notes

- This implementation prioritizes clarity and repository consistency. For large graphs, more optimized data structures and path compression in union-find should be used.
29 changes: 29 additions & 0 deletions documentation/fast_fourier_transform.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Fast Fourier Transform (FFT)

This file documents the recursive Cooley-Tukey FFT implementation added to `R/mathematics/fast_fourier_transform.r`.

## Description

The `fft_recursive` function computes the discrete Fourier transform (DFT) of a numeric or complex vector using a divide-and-conquer Cooley-Tukey algorithm. If the input length is not a power of two, it is zero-padded to the next power of two.

## Usage

In an R session:

source('mathematics/fast_fourier_transform.r')
fft_recursive(c(0, 1, 2, 3))

From the command line with Rscript:

Rscript -e "source('R/mathematics/fast_fourier_transform.r'); print(fft_recursive(c(0,1,2,3)))"

## Complexity

Time complexity: O(n log n) for inputs with length a power of two; otherwise dominated by padding to next power of two.

Space complexity: O(n) additional space for recursive calls.

## Notes

- The function returns a complex vector of the same length (after padding) as the input.
- This implementation is primarily educational; production code should prefer the optimized `fft` function available in base R.
27 changes: 27 additions & 0 deletions documentation/hamiltonian_path.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Hamiltonian Path (Backtracking)

This document describes the Hamiltonian Path backtracking implementation in `R/graph_algorithms/hamiltonian_path.r`.

## Description

The `hamiltonianPath` function searches for a Hamiltonian Path in an undirected graph represented by an adjacency matrix. It uses backtracking to attempt to build a path that visits every vertex exactly once.

## Usage

In an R session:

source('graph_algorithms/hamiltonian_path.r')

From command line using Rscript:

Rscript -e "source('R/graph_algorithms/hamiltonian_path.r')"

## Complexity

- Time complexity: O(n!) in the worst case (backtracking over permutations).
- Space complexity: O(n) for path storage and recursion.

## Notes

- The implementation assumes an undirected graph given as an adjacency matrix with 0/1 entries.
- For production use on larger graphs, consider heuristics or approximation algorithms; the problem is NP-complete.
27 changes: 27 additions & 0 deletions documentation/push_relabel.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Push-Relabel (Preflow-Push) Maximum Flow

This document describes the Push-Relabel implementation located at `R/graph_algorithms/push_relabel.r`.

## Description

The `push_relabel` function implements the Push-Relabel algorithm (also known as Preflow-Push) for computing maximum flow in a directed graph represented by a capacity matrix. It uses the highest-label selection rule by moving active vertices to the front of the list when relabeled.

## Usage

In an R session:

source('graph_algorithms/push_relabel.r')

From command line using Rscript:

Rscript -e "source('R/graph_algorithms/push_relabel.r')"

## Complexity

- Time complexity: O(V^3) for the generic implementation; improvements (gap relabeling, global relabel) reduce this significantly for practical graphs.
- Space complexity: O(V^2) for flow and capacity matrices.

## Notes

- The implementation uses 1-based indexing to be consistent with other algorithms in the repository.
- For large graphs consider adding optimizations such as gap relabeling or global relabeling.
142 changes: 142 additions & 0 deletions graph_algorithms/boruvka_mst.r
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
# Boruvka's Minimum Spanning Tree (MST) — improved R translation
#
# Converted from an improved Python implementation: adds path compression in
# union-find, returns whether a union happened, and guards against infinite
# loops on disconnected graphs.

create_graph <- function(V) {
list(V = as.integer(V), edges = data.frame(u = integer(), v = integer(), w = double(), stringsAsFactors = FALSE))
}

add_edge <- function(graph, u, v, w) {
# Append an edge. Vertices are 1-based indices for consistency.
graph$edges <- rbind(graph$edges, data.frame(u = as.integer(u), v = as.integer(v), w = as.numeric(w), stringsAsFactors = FALSE))
graph
}

boruvka_mst <- function(graph) {
V <- as.integer(graph$V)
edges <- graph$edges

# Union-Find arrays
parent <- seq_len(V)
rank <- rep(0L, V)

find_set <- function(i) {
# Iterative find with path compression
root <- i
while (parent[root] != root) {
root <- parent[root]
}
# path compression
j <- i
while (parent[j] != root) {
nextj <- parent[j]
parent[j] <<- root
j <- nextj
}
root
}

union_set <- function(x, y) {
xroot <- find_set(x)
yroot <- find_set(y)
if (xroot == yroot) return(FALSE)
if (rank[xroot] < rank[yroot]) {
parent[xroot] <<- yroot
} else if (rank[xroot] > rank[yroot]) {
parent[yroot] <<- xroot
} else {
parent[yroot] <<- xroot
rank[xroot] <<- rank[xroot] + 1L
}
TRUE
}

num_trees <- V
mst_weight <- 0
mst_edges <- data.frame(u = integer(), v = integer(), w = double(), stringsAsFactors = FALSE)

# Edge case: empty graph
if (nrow(edges) == 0) {
cat("No edges in graph.\n")
return(invisible(list(edges = mst_edges, total_weight = 0)))
}

while (num_trees > 1) {
cheapest <- rep(NA_integer_, V)

# For every edge, check components and record cheapest edge for each component
for (i in seq_len(nrow(edges))) {
u <- edges$u[i]
v <- edges$v[i]
w <- edges$w[i]
set_u <- find_set(u)
set_v <- find_set(v)

if (set_u == set_v) next

if (is.na(cheapest[set_u]) || edges$w[cheapest[set_u]] > w) {
cheapest[set_u] <- i
}
if (is.na(cheapest[set_v]) || edges$w[cheapest[set_v]] > w) {
cheapest[set_v] <- i
}
}

any_added <- FALSE

# Add the cheapest edges to MST
for (node in seq_len(V)) {
idx <- cheapest[node]
if (is.na(idx)) next

u <- edges$u[idx]
v <- edges$v[idx]
w <- edges$w[idx]
set_u <- find_set(u)
set_v <- find_set(v)

if (set_u != set_v) {
if (union_set(set_u, set_v)) {
mst_weight <- mst_weight + w
mst_edges <- rbind(mst_edges, data.frame(u = u, v = v, w = w, stringsAsFactors = FALSE))
num_trees <- num_trees - 1L
any_added <- TRUE
}
}
}

# If no edges were added in this pass, the graph is disconnected
if (!any_added) {
cat("Graph appears disconnected; stopping. No spanning tree exists that connects all vertices.\n")
break
}
}

cat("Edges in MST:\n")
if (nrow(mst_edges) > 0) {
for (i in seq_len(nrow(mst_edges))) {
cat(mst_edges$u[i], "--", mst_edges$v[i], "==", mst_edges$w[i], "\n")
}
} else {
cat("(none)\n")
}
cat("Total weight of MST:", mst_weight, "\n")

invisible(list(edges = mst_edges, total_weight = mst_weight))
}

# Example usage and test
cat("=== Boruvka's MST Algorithm (improved) ===\n")
g <- create_graph(4)
g <- add_edge(g, 1, 2, 10)
g <- add_edge(g, 1, 3, 6)
g <- add_edge(g, 1, 4, 5)
g <- add_edge(g, 2, 4, 15)
g <- add_edge(g, 3, 4, 4)

cat("Graph edges:\n")
print(g$edges)
cat("\nComputing MST...\n")
boruvka_mst(g)
96 changes: 96 additions & 0 deletions graph_algorithms/edmonds_karp.r
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
# Edmonds-Karp Maximum Flow (Ford-Fulkerson with BFS)
#
# This R implementation follows the repository style: clear header, example
# usage, and 1-based vertex indexing. The algorithm computes maximum flow
# in a directed graph given by a capacity matrix. It returns the max flow and
# the residual capacity matrix.
#
# Time Complexity: O(V * E^2) in the naive implementation; Edmonds-Karp is O(V * E^2)
# Space Complexity: O(V^2) for the capacity/residual matrix

# BFS to find an augmenting path. Returns list(found, parent)
bfs_ek <- function(capacity, source, sink) {
n <- nrow(capacity)
visited <- rep(FALSE, n)
parent <- rep(-1L, n)

# Simple queue implementation using preallocated vector
queue <- integer(n)
front <- 1L
rear <- 1L
queue[rear] <- source
visited[source] <- TRUE

while (front <= rear) {
u <- queue[front]
front <- front + 1L
for (v in seq_len(n)) {
if (!visited[v] && capacity[u, v] > 0) {
rear <- rear + 1L
queue[rear] <- v
visited[v] <- TRUE
parent[v] <- u
if (v == sink) {
return(list(found = TRUE, parent = parent))
}
}
}
}
list(found = FALSE, parent = parent)
}

# Edmonds-Karp main function
edmonds_karp <- function(capacity, source, sink) {
# Ensure capacity is a numeric matrix and uses 1-based indexing for vertices
cap <- as.matrix(capacity)
n <- nrow(cap)
if (source < 1 || source > n || sink < 1 || sink > n) stop("source/sink out of range")

max_flow <- 0

repeat {
res <- bfs_ek(cap, source, sink)
if (!res$found) break
parent <- res$parent

# Find minimum residual capacity along the path
path_flow <- Inf
s <- sink
while (s != source) {
u <- parent[s]
path_flow <- min(path_flow, cap[u, s])
s <- u
}

# Update residual capacities along the path
v <- sink
while (v != source) {
u <- parent[v]
cap[u, v] <- cap[u, v] - path_flow
cap[v, u] <- cap[v, u] + path_flow
v <- parent[v]
}

max_flow <- max_flow + path_flow
}

invisible(list(max_flow = max_flow, residual = cap))
}

# Example usage
cat("=== Edmonds-Karp Maximum Flow (R) ===\n")
# Graph represented as capacity matrix (6x6)
graph <- matrix(c(
0, 16, 13, 0, 0, 0,
0, 0, 10, 12, 0, 0,
0, 4, 0, 0, 14, 0,
0, 0, 9, 0, 0, 20,
0, 0, 0, 7, 0, 4,
0, 0, 0, 0, 0, 0
), nrow = 6, byrow = TRUE)

# Note: Python example used 0-based indices; this R example uses 1-based indices
source <- 1L
sink <- 6L
res <- edmonds_karp(graph, source, sink)
cat("Maximum Flow:", res$max_flow, "\n")
Loading