Skip to content

Commit 2e5227e

Browse files
committed
feat: implement Longest Increasing Subsequence algorithm in R
1 parent a727c7c commit 2e5227e

File tree

1 file changed

+250
-0
lines changed

1 file changed

+250
-0
lines changed
Lines changed: 250 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,250 @@
1+
# Longest Increasing Subsequence (Dynamic Programming)
2+
#
3+
# The Longest Increasing Subsequence (LIS) problem is a classic dynamic programming problem.
4+
# Given an array of integers, find the length of the longest subsequence that is strictly
5+
# increasing. A subsequence is derived from the array by deleting some or no elements
6+
# without changing the order of the remaining elements.
7+
#
8+
# Time Complexity: O(n²) for basic DP, O(n log n) for optimized binary search version
9+
# Space Complexity: O(n) for both approaches
10+
#
11+
# Applications:
12+
# - Bioinformatics (DNA sequence analysis)
13+
# - Stock market analysis (longest upward trend)
14+
# - Scheduling problems
15+
# - Game theory (optimal play sequences)
16+
# - Data compression and pattern recognition
17+
18+
# Basic DP solution for Longest Increasing Subsequence
19+
longest_increasing_subsequence <- function(nums) {
20+
#' Find the length of the longest increasing subsequence using Dynamic Programming
21+
#' @param nums: Numeric vector of integers
22+
#' @return: List containing max length, DP array, and one possible LIS
23+
24+
n <- length(nums)
25+
26+
# Handle edge cases
27+
if (n == 0) {
28+
return(list(
29+
max_length = 0,
30+
dp_array = c(),
31+
lis_sequence = c(),
32+
dp_table = c()
33+
))
34+
}
35+
36+
if (n == 1) {
37+
return(list(
38+
max_length = 1,
39+
dp_array = c(1),
40+
lis_sequence = nums,
41+
dp_table = c(1)
42+
))
43+
}
44+
45+
# Initialize DP array: dp[i] = length of LIS ending at index i
46+
dp <- rep(1, n)
47+
48+
# Fill DP array
49+
for (i in 2:n) {
50+
for (j in 1:(i - 1)) {
51+
if (nums[j] < nums[i]) {
52+
dp[i] <- max(dp[i], dp[j] + 1)
53+
}
54+
}
55+
}
56+
57+
# Find maximum length
58+
max_length <- max(dp)
59+
60+
# Backtrack to find one possible LIS
61+
lis_sequence <- c()
62+
current_length <- max_length
63+
64+
for (i in n:1) {
65+
if (dp[i] == current_length) {
66+
lis_sequence <- c(nums[i], lis_sequence)
67+
current_length <- current_length - 1
68+
if (current_length == 0) break
69+
}
70+
}
71+
72+
return(list(
73+
max_length = max_length,
74+
dp_array = dp,
75+
lis_sequence = lis_sequence,
76+
dp_table = dp
77+
))
78+
}
79+
80+
# Optimized O(n log n) solution using binary search
81+
longest_increasing_subsequence_optimized <- function(nums) {
82+
#' Find the length of the longest increasing subsequence using binary search
83+
#' @param nums: Numeric vector of integers
84+
#' @return: Length of the longest increasing subsequence
85+
86+
n <- length(nums)
87+
88+
if (n == 0) return(0)
89+
if (n == 1) return(1)
90+
91+
# tails[i] stores the smallest tail of all increasing subsequences of length i+1
92+
tails <- c()
93+
94+
for (num in nums) {
95+
# Binary search for the position to replace or extend
96+
pos <- binary_search_insert_position(tails, num)
97+
98+
if (pos > length(tails)) {
99+
# Extend the sequence
100+
tails <- c(tails, num)
101+
} else {
102+
# Replace the element at position pos
103+
tails[pos] <- num
104+
}
105+
}
106+
107+
return(length(tails))
108+
}
109+
110+
# Helper function for binary search
111+
binary_search_insert_position <- function(arr, target) {
112+
#' Binary search to find the position where target should be inserted
113+
#' @param arr: Sorted numeric vector
114+
#' @param target: Value to insert
115+
#' @return: Position (1-indexed) where target should be inserted
116+
117+
if (length(arr) == 0) return(1)
118+
119+
left <- 1
120+
right <- length(arr)
121+
122+
while (left <= right) {
123+
mid <- left + (right - left) %/% 2
124+
125+
if (arr[mid] < target) {
126+
left <- mid + 1
127+
} else {
128+
right <- mid - 1
129+
}
130+
}
131+
132+
return(left)
133+
}
134+
135+
# Function to find all possible LIS sequences (simplified version)
136+
find_all_lis <- function(nums) {
137+
#' Find all possible longest increasing subsequences
138+
#' @param nums: Numeric vector of integers
139+
#' @return: List of all possible LIS sequences
140+
141+
n <- length(nums)
142+
if (n == 0) return(list())
143+
144+
# Calculate DP array
145+
dp <- rep(1, n)
146+
for (i in 2:n) {
147+
for (j in 1:(i - 1)) {
148+
if (nums[j] < nums[i]) {
149+
dp[i] <- max(dp[i], dp[j] + 1)
150+
}
151+
}
152+
}
153+
154+
max_length <- max(dp)
155+
156+
# For simplicity, return just one LIS (same as the main function)
157+
# Finding all possible LIS is complex and not essential for the algorithm demonstration
158+
result <- longest_increasing_subsequence(nums)
159+
return(list(result$lis_sequence))
160+
}
161+
162+
# Helper function to print DP table
163+
print_lis_dp <- function(dp_array, nums) {
164+
cat("DP Array for Longest Increasing Subsequence:\n")
165+
cat("Input Array:", paste(nums, collapse = ", "), "\n")
166+
cat("DP Array :", paste(dp_array, collapse = ", "), "\n")
167+
cat("Max Length :", max(dp_array), "\n\n")
168+
}
169+
170+
# ===========================
171+
# Example Usage & Testing
172+
# ===========================
173+
cat("=== Longest Increasing Subsequence (Dynamic Programming) ===\n\n")
174+
175+
# Test 1: Basic Example
176+
nums1 <- c(10, 9, 2, 5, 3, 7, 101, 18)
177+
cat("Test 1: Basic Example\n")
178+
cat("Input Array:", paste(nums1, collapse = ", "), "\n\n")
179+
180+
result1 <- longest_increasing_subsequence(nums1)
181+
print_lis_dp(result1$dp_array, nums1)
182+
cat("Maximum Length:", result1$max_length, "\n")
183+
cat("One LIS Sequence:", paste(result1$lis_sequence, collapse = ", "), "\n\n")
184+
185+
# Test 2: Optimized Version
186+
cat("Test 2: Optimized O(n log n) Version\n")
187+
max_len_opt <- longest_increasing_subsequence_optimized(nums1)
188+
cat("Maximum Length (Optimized):", max_len_opt, "\n")
189+
cat("Verification: Both methods match:", result1$max_length == max_len_opt, "\n\n")
190+
191+
# Test 3: All Possible LIS
192+
cat("Test 3: All Possible LIS Sequences\n")
193+
all_lis <- find_all_lis(nums1)
194+
cat("Total number of LIS sequences:", length(all_lis), "\n")
195+
for (i in seq_along(all_lis)) {
196+
cat("LIS", i, ":", paste(all_lis[[i]], collapse = ", "), "\n")
197+
}
198+
cat("\n")
199+
200+
# Test 4: Edge Cases
201+
cat("Test 4: Edge Cases\n")
202+
cat("Empty array:", longest_increasing_subsequence(c())$max_length, "\n")
203+
cat("Single element:", longest_increasing_subsequence(c(5))$max_length, "\n")
204+
cat("All same elements:", longest_increasing_subsequence(c(3, 3, 3, 3))$max_length, "\n")
205+
cat("Strictly decreasing:", longest_increasing_subsequence(c(5, 4, 3, 2, 1))$max_length, "\n")
206+
cat("Strictly increasing:", longest_increasing_subsequence(c(1, 2, 3, 4, 5))$max_length, "\n\n")
207+
208+
# Test 5: Larger Dataset
209+
cat("Test 5: Larger Dataset (n=20)\n")
210+
set.seed(42)
211+
nums_large <- sample(1:100, 20)
212+
cat("Input Array:", paste(nums_large, collapse = ", "), "\n\n")
213+
214+
result_large <- longest_increasing_subsequence(nums_large)
215+
cat("Maximum Length:", result_large$max_length, "\n")
216+
cat("One LIS Sequence:", paste(result_large$lis_sequence, collapse = ", "), "\n\n")
217+
218+
# Test 6: Performance Comparison
219+
cat("Test 6: Performance Comparison (n=1000)\n")
220+
n <- 1000
221+
nums_perf <- sample(1:1000, n)
222+
capacity <- 200
223+
224+
start_time <- Sys.time()
225+
res_opt <- longest_increasing_subsequence_optimized(nums_perf)
226+
opt_time <- as.numeric(Sys.time() - start_time, units = "secs")
227+
228+
cat("Optimized O(n log n) result:", res_opt, "\n")
229+
cat("Time taken:", sprintf("%.4f sec", opt_time), "\n")
230+
231+
# Verify correctness with basic DP (smaller sample for time comparison)
232+
nums_small <- nums_perf[1:100]
233+
start_time <- Sys.time()
234+
res_basic <- longest_increasing_subsequence(nums_small)
235+
basic_time <- as.numeric(Sys.time() - start_time, units = "secs")
236+
237+
cat("Basic O(n²) result (n=100):", res_basic$max_length, "\n")
238+
cat("Time taken:", sprintf("%.4f sec", basic_time), "\n")
239+
240+
# Test 7: Real-world Example - Stock Prices
241+
cat("Test 7: Real-world Example - Stock Price Trend\n")
242+
stock_prices <- c(100, 102, 98, 105, 103, 107, 110, 108, 112, 115, 113, 118, 120, 117, 125)
243+
cat("Stock Prices:", paste(stock_prices, collapse = ", "), "\n")
244+
245+
stock_result <- longest_increasing_subsequence(stock_prices)
246+
cat("Longest upward trend length:", stock_result$max_length, "\n")
247+
cat("Longest upward trend:", paste(stock_result$lis_sequence, collapse = ", "), "\n")
248+
cat("Percentage increase:",
249+
sprintf("%.2f%%", (stock_result$lis_sequence[length(stock_result$lis_sequence)] /
250+
stock_result$lis_sequence[1] - 1) * 100), "\n")

0 commit comments

Comments
 (0)