Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
0% found this document useful (0 votes)
7 views

Algorithm[1]

The document is a lab file detailing experiments on the empirical analysis of sorting and searching algorithms, including bubble sort, linear search, and binary search. Each section outlines the aim, materials required, implementation details, theoretical background, and code for executing the algorithms, along with results and conclusions drawn from the experiments. The findings indicate that execution time increases with input size for all algorithms tested.

Uploaded by

s2789211
Copyright
© © All Rights Reserved
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
7 views

Algorithm[1]

The document is a lab file detailing experiments on the empirical analysis of sorting and searching algorithms, including bubble sort, linear search, and binary search. Each section outlines the aim, materials required, implementation details, theoretical background, and code for executing the algorithms, along with results and conclusions drawn from the experiments. The findings indicate that execution time increases with input size for all algorithms tested.

Uploaded by

s2789211
Copyright
© © All Rights Reserved
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
You are on page 1/ 19

Design & Analysis of Algorithm

LAB FILE

Name Ayush Samra


Class BE (IT-1)
Semester 5A
Group 2
Roll No. UE228032
DD
INDEX
S.No Experiment Date Pg No.

1. Empirical Analysis of bubble 1


sort
2. Empirical Analysis of linear 8
search
3. Empirical Analysis of binary 12
search
4. Fractional Knapsack

5.

DAA
2
EMPIRICAL ANALYSIS OF BUBBLE SORT

AIM: To study the change in time of execution as size of input increases and
to plot graph of it

MATERIALS REQUIRED: Laptop

IMPLEMENTATION DETAIL:
1. Execution Time: The code measures the time taken to sort an
array using the bubble sort algorithm. This is done using the time
module.
 time_taken = time.time() - time_start: Calculates the
elapsed time by subtracting the start time from the current time
after sorting is completed. This value represents the
execution time of the bubble sort function.
 time_start = time.time(): Records the start time before the
sorting begins.

2. Dynamic Arrays: The code uses lists in Python, which are


dynamic arrays. Lists are used to store the array of numbers to be
sorted and the results of each pass.
 data: A dynamic array (list) generated with a size determined by
user input.
 passes = []: An empty list initialized to store snapshots of the
array after each sorting pass. The passes list grows dynamically
as new states of the array are appended after each pass.

3. Random Number Generator: The code generates random


numbers to create the initial unsorted array.
 generate_random_numbers(size, value_range): This function
generates a list of random numbers.
 random.randint(*value_range): Generates a random integer
within the specified range. The *value_range syntax unpacks
the tuple value_range into two separate arguments (start and
end) for random.randint.
 [random.randint(*value_range) for _ in range(size)]: A
list comprehension that generates size random numbers within
the given range.

DAA
3
4. Recording Output of Multiple Rows:
The code stores the state of the array after each pass through the
bubble sort algorithm
 passes.append(arr[:]): After each pass of the bubble sort, the
current state of the array is appended to the passes list.
 arr[:]: Creates a shallow copy of the array arr. This ensures
that each entry in passes is a snapshot of the array at a specific
point in time, preserving the state of the array after each pass.

5. Plotting Graph:
The code can plot a graph to visualize the time complexity of the
bubble sort algorithm for different input sizes:
 input_sizes = list(range(500, 4000, 500)): Defines a list
of input sizes, starting from 500 to 3500 with a step of 500.
 results = []: Initializes an empty list to store the results.
 For each size in input_sizes, the code generates a list of
random numbers, sorts it using bubble sort, and records the time
taken:
 sizes, times = zip(*results): Unzips the results into two
separate lists: sizes (input sizes) and times (time taken).

THEORY:
Bubble Sort is the simplest sorting algorithm that works by repeatedly
swapping the adjacent elements if they are in the wrong order
In Bubble Sort algorithm, traverse from left and compare adjacent elements
and the higher one is placed at right side. In this way, the largest element is
moved to the rightmost end at first. This process is then continued to find the
second largest and place it and so on until the data is sorted

CODE:
import random
import time
import matplotlib.pyplot as plt

# Function to generate random numbers within a range


def generate_random_numbers(size, value_range):
return [random.randint(*value_range) for _ in range(size)]

# Bubble sort function with time measurement


def bubble_sort(arr):
n = len(arr)
time_start = time.time()
DAA
4
passes = []

for i in range(n):
swapped = False
for j in range(0, n-i-1):
if arr[j] > arr[j+1]:
arr[j], arr[j+1] = arr[j+1], arr[j]
swapped = True
passes.append(arr[:]) # Store a copy of the array after each pass
if not swapped:
break

time_taken = time.time() - time_start


return passes, time_taken

def main():
num_inputs = int(input("Enter the number of inputs: "))

data = generate_random_numbers(num_inputs, (1, 100))

# Perform bubble sort and measure time


passes, time_taken = bubble_sort(data)

print("Time taken to sort {} numbers: {:.8f}


seconds".format(num_inputs, time_taken))

print_random = input("Do you want to print the random numbers


before sorting? (yes/no): ").strip().lower()

if print_random == 'yes':
print("Random numbers before sorting: ", data)

print_passes = input("Do you want to print the array after each pass?
(yes/no): ").strip().lower()

if print_passes == 'yes':
for i, p in enumerate(passes):
print("Pass {}: {}".format(i+1, p))

# Prompt user to plot the graph


plot_graph = input("Do you want to plot the graph for multiple input
sizes? (yes/no): ").strip().lower()

if plot_graph == 'yes':
input_sizes = list(range(500, 4000, 500))
results = []

for size in input_sizes:


data = generate_random_numbers(size, (1, 10000))
DAA
5
_, time_taken = bubble_sort(data)
results.append((size, time_taken))

sizes, times = zip(*results)

plt.figure(figsize=(10, 6))
plt.plot(sizes, times, marker='o')
plt.title('Bubble Sort Time Complexity')
plt.xlabel('Input Array Size')
plt.ylabel('Time Taken (seconds)')
plt.grid(True)
plt.xticks(input_sizes)
plt.show()

if __name__ == "__main__":
main()

OUTPUT:
Enter the number of inputs: 1000

Time taken to sort 1000 numbers: 0.04572248 seconds

Enter the number of inputs: 10

Do you want to print the random numbers before sorting? (yes/no): yes

Random numbers before sorting: [2, 2, 19, 24, 28, 57, 57, 69, 76, 93]

Do you want to print the array after each pass? (yes/no): yes

Pass 1: [2, 24, 69, 2, 28, 57, 19, 76, 57, 93]

Pass 2: [2, 24, 2, 28, 57, 19, 69, 57, 76, 93]

Pass 3: [2, 2, 24, 28, 19, 57, 57, 69, 76, 93]

Pass 4: [2, 2, 24, 19, 28, 57, 57, 69, 76, 93]

Pass 5: [2, 2, 19, 24, 28, 57, 57, 69, 76, 93]

Pass 6: [2, 2, 19, 24, 28, 57, 57, 69, 76, 93]

Do you want to plot the graph for multiple input sizes? (yes/no): yes

DAA
6
CONCLUSION :

As we can see through factual and graphical way , as the size of input
increases, time of execution also increases
Initially the growth was small, but as input size increases, the time of
execution also started increasing rapidly
One more point to be noted is that more precise measurements of time
were given by online compiler for smaller output using same code
From this, we can conclude that as size of input increases, time of
execution also increases

DAA
7
EMPIRICAL ANALYSIS OF LINEAR SEARCH

AIM: To study the change in time of execution as size of input increases and
to plot graph of it

MATERIALS REQUIRED: Laptop

IMPLEMENTATION DETAIL:

1. Dynamic Array:
The dynamic array is created using random.sample, which
generates a list of unique random integers of a specified size. The
array's size is determined at runtime, and the elements are
dynamically generated to ensure they are unique.

2. Random Number Generator: The random module is used to


generate random numbers within a specified range.
 random.sample is used to create a dynamic array of unique
integers
 random.randint is used to select a random index within the
array for average case testing.

3. Execution Time:
 The measure_execution_time(arr, target) function calculates
the time taken to perform a linear search on the array.
 time.time() function to record the start and end times of the
search, and the difference between these times gives the
execution time.
This execution time is a measure of the algorithm's efficiency for
different scenarios (best case, worst case, and average case).

4. Tabular Data:
show_tabular_form(sizes, best_times, worst_times,
avg_times) function creates a DataFrame from the collected data
and prints it out. This tabular data helps in comparing the
performance across different scenarios and array sizes.

DAA
8
5. Plotting Graph:
plot_results(sizes, best_times, worst_times, avg_times)
function uses Matplotlib to visualize the execution times for best,
worst, and average cases across different array sizes. The graph
plots the array size on the x-axis and the execution time on the y-
axis, with different lines representing best, worst, and average
cases

THEORY:
Linear search is a straightforward algorithm that checks each element in a
list or array sequentially to find a target value. It continues until a match
is found or all elements have been checked. The time complexity of linear
search is O(n), where n is the number of elements.

CODE:
import random
import time
import matplotlib.pyplot as plt
import pandas as pd

def create_dynamic_unique_array(size):
return random.sample(range(size * 2), size)

def linear_search(arr, target):


for i in range(len(arr)):
if arr[i] == target:
return i
return -1

def measure_execution_time(arr, target):


linear_search(arr, target)
end_time = time.time()
return end_time

def main():
sizes = [1000, 2000, 5000, 10000, 20000, 50000]
best_exec_time = []
worst_exec_time = []
average_exec_time = []

for size in sizes:


arr = create_dynamic_unique_array(size)

DAA
9
best_case_target = arr[0]
best_case_time = measure_execution_time(arr, best_case_target)
best_exec_time.append(best_case_time)

worst_case_target = arr[-1]
worst_case_time = measure_execution_time(arr, worst_case_target)
worst_exec_time.append(worst_case_time)

average_case_target = arr[random.randint(1, size - 2)]


average_case_time = measure_execution_time(arr,
average_case_target)
average_exec_time.append(average_case_time)

print(f"Size: {size}")
print(f"Best Case Time: {best_case_time:.6f} seconds")
print(f"Worst Case Time: {worst_case_time:.6f} seconds")
print(f"Average Case Time: {average_case_time:.6f} seconds")
print()

plot_results(sizes, best_exec_time, worst_exec_time,


average_exec_time)
show_tabular_form(sizes, best_exec_time, worst_exec_time,
average_exec_time)

def plot_results(sizes, best_times, worst_times, avg_times):


plt.figure(figsize=(10, 6))
plt.plot(sizes, best_times, label='Best Case', marker='o')
plt.plot(sizes, worst_times, label='Worst Case', marker='o')
plt.plot(sizes, avg_times, label='Average Case', marker='o')
plt.xlabel('Array Size')
plt.ylabel('Execution Time (seconds)')
plt.title('Linear Search Execution Time')
plt.legend()
plt.grid(True)
plt.show()

def show_tabular_form(sizes, best_times, worst_times, avg_times):


data = {
'Size': sizes,
'BC Time': best_times,
'WC Time': worst_times,
'AC Time': avg_times
}
df = pd.DataFrame(data)
print(df)

if __name__ == "__main__":
main()
DAA
10
OUTPUT:

Size BC Time WC Time AC Time


1 1000 0.000000 0.000999 0.000000
2 2000 0.000000 0.001004 0.000000
3 5000 0.000000 0.001095 0.001005
4 10000 0.000000 0.005000 0.003000
5 20000 0.000000 0.010999 0.008000
6 50000 0.000000 0.014008 0.007001

DAA
11
EMPIRICAL ANALYSIS OF BINARY SEARCH

AIM: To study the change in time of execution as size of input increases and
to plot graph of it

MATERIALS REQUIRED: Laptop

IMPLEMENTATION DETAIL:

1. Dynamic Array:
The dynamic array is created using random.sample, which
generates a list of unique random integers of a specified size. The
array's size is determined at runtime, and the elements are
dynamically generated to ensure they are unique.

2. Random Number Generator: The random module is used to


generate random numbers within a specified range.
 random.sample is used to create a dynamic array of unique
integers
 random.randint is used to select a random index within the
array for average case testing.

3. Execution Time:
 The measure_execution_time(arr, target) function calculates
the time taken to perform a linear search on the array.
 time.time() function to record the start and end times of the
search, and the difference between these times gives the
execution time.
This execution time is a measure of the algorithm's efficiency for
different scenarios (best case, worst case, and average case).

4. Tabular Data:
DAA
12
show_tabular_form(sizes, best_times, worst_times,
avg_times) function creates a DataFrame from the collected data
and prints it out. This tabular data helps in comparing the
performance across different scenarios and array sizes.

5. Plotting Graph:
plot_results(sizes, best_times, worst_times, avg_times)
function uses Matplotlib to visualize the execution times for best,
worst, and average cases across different array sizes. The graph
plots the array size on the x-axis and the execution time on the y-
axis, with different lines representing best, worst, and average
cases

THEORY:
Binary search is an efficient algorithm for finding a target value in a sorted
array or list. It works by repeatedly dividing the search interval in half.
Starting with the entire array, the algorithm compares the target value to
the middle element. If the target is equal to the middle element, the
search ends. If the target is less, the search continues in the lower half; if
greater, in the upper half. This process continues until the target is found
or the search interval is empty. Binary search has a time complexity of
O(log n), making it much faster than linear search for large datasets.

CODE:
import time
import random
import matplotlib.pyplot as plt
from tabulate import tabulate

def binary_search(arr, target):


left, right = 0, len(arr) - 1
while left <= right:
mid = left + (right - left) // 2
if arr[mid] == target:
return mid
elif arr[mid] < target:
left = mid + 1
DAA
13
else:
right = mid - 1
return -1

def generate_random_array(size, lower_bound=0):


upper_bound = max(size * 2, 10000)
return random.sample(range(lower_bound, upper_bound), size)

def measure_execution_time(array, target):


start_time = time.perf_counter()
binary_search(array, target)
end_time = time.perf_counter()
return (end_time - start_time) * 1e3

sizes = [10, 100, 500, 1000, 10000, 50000, 100000, 1000000]

table_data = []

run_number = 1
for size in sizes:
random_array = generate_random_array(size)
random_array.sort()

best_case_time = measure_execution_time(random_array, random_array[(size


// 2)-1])

worst_case_time = measure_execution_time(random_array, random_array[0])

average_case_time = measure_execution_time(random_array,
random_array[size // 2 + size // 4])

table_data.append([run_number, size, best_case_time, average_case_time,


worst_case_time])
run_number += 1

headers = ["Runs", "Size of Array", "Best Case Time (ms)", "Average Case Time
(ms)", "Worst Case Time (ms)"]
print(tabulate(table_data, headers=headers, tablefmt="grid"))

plot_sizes = [row[1] for row in table_data]


plot_best_times = [row[2] for row in table_data]
plot_average_times = [row[3] for row in table_data]
plot_worst_times = [row[4] for row in table_data]
DAA
14
plt.plot(plot_sizes, plot_best_times, marker='o', label='Best Case')
plt.plot(plot_sizes, plot_average_times, marker='o', label='Average Case')
plt.plot(plot_sizes, plot_worst_times, marker='o', label='Worst Case')

plt.title('Binary Search Execution Time Analysis')


plt.xlabel('Array Size')
plt.ylabel('Execution Time (milliseconds)')
plt.legend()
plt.grid(True)
plt.show()

OUTPUT:

DAA
15
IMPLEMENTATION OF GREEDY STRATERGY
TO SOLVE FRACTIONAL KNAPSACK PROBLEM

AIM: To study greedy approach for solving problems

MATERIALS REQUIRED: Laptop

IMPLEMENTATION DETAIL:

1. Dynamic Multidimensional Array:

2. Take Profit / Weight ratio:

DAA
16
3. Applying Sorting:

4. Putting Items In Knapsack:

CODE:

#include <iostream>

using namespace std;

// Function to sort items based on profit/weight ratio in descending order


void sortItems(double profit[], double weight[], int n) {
for (int i = 0; i < n - 1; ++i) {
for (int j = 0; j < n - i - 1; ++j) {
double ratio1 = profit[j] / weight[j];
double ratio2 = profit[j+1] / weight[j+1];
if (ratio1 < ratio2) {
// Swap profits
double tempProfit = profit[j];
profit[j] = profit[j+1];
profit[j+1] = tempProfit;

// Swap weights
double tempWeight = weight[j];
weight[j] = weight[j+1];
weight[j+1] = tempWeight;
}
}
}
}

// Fractional Knapsack function


double fractional_Knapsack(double W, double profit[], double weight[],
double fraction[], int n) {
sortItems(profit, weight, n); // Sort items by profit/weight ratio

double final_value = 0.0;

DAA
17
for (int i = 0; i < n; ++i) {
if (weight[i] <= W) {
W -= weight[i];
final_value += profit[i];
fraction[i] = 1.0; // Take the whole item
} else {
fraction[i] = W / weight[i]; // Take a fraction of the item
final_value += profit[i] * fraction[i];
break;
}
}

return final_value;
}

int main() {
double W;
int n;

cout << "Enter the maximum weight capacity of the knapsack: ";
cin >> W;

cout << "Enter the number of items: ";


cin >> n;

double profit[100], weight[100], fraction[100]; // Arrays for profit,


weight, and fraction of items
for (int i = 0; i < n; ++i) {
fraction[i] = 0.0; // Initialize fraction array to 0
cout << "Enter profit for item " << i+1 << ": ";
cin >> profit[i];
cout << "Enter weight for item " << i+1 << ": ";
cin >> weight[i];
}

double max_profit = fractional_Knapsack(W, profit, weight, fraction, n);

cout << "\nThe maximum value in the knapsack is: " << max_profit <<
"\n";

DAA
18
cout << "Fraction of each item taken:\n";
for (int i = 0; i < n; ++i) {
cout << "Item " << i+1 << ": " << fraction[i] << "\n";
}

return 0;
}
OUTPUT:
Enter the maximum weight capacity of the knapsack: 20
Enter the number of items: 4
Enter profit for item 1: 10
Enter weight for item 1: 10
Enter profit for item 2: 30
Enter weight for item 2: 15
Enter profit for item 3: 40
Enter weight for item 3: 23
Enter profit for item 4: 40
Enter weight for item 4: 25

The maximum value in the knapsack is: 38.6957


Fraction of each item taken:
Item 1: 1
Item 2: 0.217391
Item 3: 0
Item 4: 0

DAA
19

You might also like