Skip to content

Commit 09f64d7

Browse files
committed
Complexity analysis and Proof of correctness using assertions
0 parents  commit 09f64d7

File tree

11 files changed

+268
-0
lines changed

11 files changed

+268
-0
lines changed
Binary file not shown.
Binary file not shown.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
from time import time
2+
3+
4+
def factors(number):
5+
start_time = time()
6+
output = [] # output array to store factors
7+
8+
if number == 1:
9+
return output # 1 is not a prime
10+
11+
# Reduce the number until it can be divided by 2
12+
while number % 2 == 0: # worst case runs log_2(N) times, O (log_2 N)
13+
output.append(2)
14+
number = number // 2
15+
16+
# For each number from 3 to sqrt n, keep dividing number whenever possible and add to factors
17+
# since even numbers are already taken care of by previous loop, increment by 2
18+
for i in range(3, int(number ** (1 / 2)) + 1): # worst case runs sqrt N times, O (sqrt N)
19+
while number % i == 0: # worst case runs log_3 N times, otherwise log_i N
20+
output.append(i)
21+
number = number // i
22+
i += 2
23+
24+
if number > 2: # number hasn't been divided by anything, it's prime
25+
output.append(number)
26+
27+
time_taken = time() - start_time
28+
times.append(time_taken)
29+
return output
30+
31+
32+
def verify(number, factors_list):
33+
product = 1
34+
for factor in factors_list:
35+
product *= factor
36+
return number == product
37+
38+
39+
if __name__ == '__main__':
40+
times = []
41+
numbers = [6, 60, 840, 7560, 83160, 720720, 8648640, 73513440, 735134400, 6983776800, 97772875200, 963761198400,
42+
9316358251200, 97821761637600, 866421317361600, 8086598962041600, 74801040398884800, 897612484786617600]
43+
# numbers = [170141183460469231731687303715884105727]
44+
for num in numbers:
45+
factors_list = factors(number=num)
46+
# print(verify(number=num, factors_list=factors_list))
47+
48+
for i in range(0, len(numbers)):
49+
print('{} \t {}'.format(numbers[i], times[i] * 1000))
50+
51+
# factors_list = factors(number=1)
52+
# print(verify(number=1, factors_list=factors_list))
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
def bubble_sort(a):
2+
swap, j = 1, len(a)
3+
# Assertion 0: a[0], ..., a[n - 1] is an array of integers with at least 1 element
4+
while swap == 1:
5+
# Assertion 1: swap = 1 && a'[j] <= ... <= a'[n - 1] && {a’[j], ...,a’[n - 1]} = {a[j], ..., a[n - 1]}
6+
swap = 0
7+
j = j - 1
8+
for i in range(j):
9+
# Assertion 2: a[i] is pointing to max a[0]...a[i]
10+
print(i, j)
11+
print(a)
12+
if a[i] > a[i + 1]:
13+
a[i], a[i + 1], swap = a[i + 1], a[i], 1
14+
# Assertion 3: a’[0] <= ...<= a’[n - 1] & {a’[0], ..., a’[n - 1]} = {a[0], ..., a[n - 1]}
15+
16+
17+
if __name__ == '__main__':
18+
input = [3, 5, 2, 9, 1]
19+
# input = [9, 6, 3, 1, 0]
20+
# input = [1, 2, 3, 4, 5]
21+
# input = [5, 4, 3, 2, 1]
22+
bubble_sort(input)
23+
print(input)
Binary file not shown.
Binary file not shown.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
import random
2+
from time import time
3+
4+
5+
# Time Complexity: O(n log n)
6+
# Space Complexity: O(log n) stack space for recursion + O(n) array after merge
7+
def merge_sort_recursive(numbers, low, high):
8+
if low < high:
9+
middle = (low + high) // 2
10+
merge_sort_recursive(numbers, low, middle)
11+
merge_sort_recursive(numbers, middle + 1, high)
12+
merge(numbers, low, middle, high)
13+
14+
15+
def merge(numbers, low, mid, high):
16+
i = low
17+
j = mid + 1
18+
sorted_nums = []
19+
while i <= mid and j <= high:
20+
if numbers[i] < numbers[j]:
21+
sorted_nums.append(numbers[i])
22+
i += 1
23+
else:
24+
sorted_nums.append(numbers[j])
25+
j += 1
26+
27+
while i <= mid:
28+
sorted_nums.append(numbers[i])
29+
i += 1
30+
while j <= high:
31+
sorted_nums.append(numbers[j])
32+
j += 1
33+
numbers[low:high + 1] = sorted_nums[:]
34+
35+
36+
# Time Complexity: O(n log n); worst case O(n^2) when list is already sorted
37+
# Space Complexity: O(log n) stack space for recursion; O(n) stack in worst case
38+
def quick_sort_recursive(numbers, low, high):
39+
if low >= high:
40+
return
41+
partition_index = partition(numbers, low, high)
42+
quick_sort_recursive(numbers, low, partition_index - 1)
43+
quick_sort_recursive(numbers, partition_index, high)
44+
45+
46+
def partition(numbers, low, high):
47+
pivot = numbers[(low + high) // 2] # Take middle element as pivot
48+
while low <= high:
49+
while numbers[low] < pivot:
50+
low += 1
51+
while numbers[high] > pivot:
52+
high -= 1
53+
if low <= high:
54+
numbers[low], numbers[high] = numbers[high], numbers[low]
55+
# swap(numbers, low, high)
56+
low += 1
57+
high -= 1
58+
return low # new partition position, every left is smaller than pivot, every right is bigger than pivot
59+
60+
61+
if __name__ == '__main__':
62+
merge_time = []
63+
quick_time = []
64+
for x in range(100, 10100, 100):
65+
nums = random.sample(range(1, 10100), x)
66+
nums_copy = nums[:]
67+
# print("before merge sort:", nums_copy)
68+
69+
# merge_sort_recursive(nums, 0, len(nums) - 1)
70+
start_time = time()
71+
merge_sort_recursive(nums, 0, len(nums) - 1)
72+
time_taken = time() - start_time
73+
merge_time.append(time_taken * 1000)
74+
# print("after merge sort:", nums)
75+
76+
# print("before quick sort:", nums_copy)
77+
# quick_sort_recursive(nums_copy, 0, len(nums_copy) - 1)
78+
start_time = time()
79+
quick_sort_recursive(nums_copy, 0, len(nums_copy) - 1)
80+
time_taken = time() - start_time
81+
quick_time.append(time_taken * 1000)
82+
# print("after quick sort:", nums_copy)
83+
84+
for y in range(len(merge_time)):
85+
print(merge_time[y], quick_time[y])
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
def bubble_sort(a):
2+
swap, j = 1, len(a)
3+
# Assertion 0: a[0], ..., a[n - 1] is an array of integers with at least 1 element
4+
while swap == 1:
5+
# Assertion 1: swap = 1 && a'[j] <= ... <= a'[n - 1] && {a’[j], ...,a’[n - 1]} = {a[j], ..., a[n - 1]}
6+
swap = 0
7+
j = j - 1
8+
for i in range(j):
9+
# Assertion 2: a[i] is pointing to max a[0]...a[i]
10+
print(i, j)
11+
print(a)
12+
if a[i] > a[i + 1]:
13+
a[i], a[i + 1], swap = a[i + 1], a[i], 1
14+
# Assertion 3: a’[0] <= ...<= a’[n - 1] & {a’[0], ..., a’[n - 1]} = {a[0], ..., a[n - 1]}
15+
16+
17+
if __name__ == '__main__':
18+
input = [3, 5, 2, 9, 1]
19+
# input = [9, 6, 3, 1, 0]
20+
# input = [1, 2, 3, 4, 5]
21+
# input = [5, 4, 3, 2, 1]
22+
bubble_sort(input)
23+
print(input)
Binary file not shown.
Binary file not shown.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
import random
2+
from time import time
3+
4+
5+
# Time Complexity: O(n log n)
6+
# Space Complexity: O(log n) stack space for recursion + O(n) array after merge
7+
def merge_sort_recursive(numbers, low, high):
8+
if low < high:
9+
middle = (low + high) // 2
10+
merge_sort_recursive(numbers, low, middle)
11+
merge_sort_recursive(numbers, middle + 1, high)
12+
merge(numbers, low, middle, high)
13+
14+
15+
def merge(numbers, low, mid, high):
16+
i = low
17+
j = mid + 1
18+
sorted_nums = []
19+
while i <= mid and j <= high:
20+
if numbers[i] < numbers[j]:
21+
sorted_nums.append(numbers[i])
22+
i += 1
23+
else:
24+
sorted_nums.append(numbers[j])
25+
j += 1
26+
27+
while i <= mid:
28+
sorted_nums.append(numbers[i])
29+
i += 1
30+
while j <= high:
31+
sorted_nums.append(numbers[j])
32+
j += 1
33+
numbers[low:high + 1] = sorted_nums[:]
34+
35+
36+
# Time Complexity: O(n log n); worst case O(n^2) when list is already sorted
37+
# Space Complexity: O(log n) stack space for recursion; O(n) stack in worst case
38+
def quick_sort_recursive(numbers, low, high):
39+
if low >= high:
40+
return
41+
partition_index = partition(numbers, low, high)
42+
quick_sort_recursive(numbers, low, partition_index - 1)
43+
quick_sort_recursive(numbers, partition_index, high)
44+
45+
46+
def partition(numbers, low, high):
47+
pivot = numbers[(low + high) // 2] # Take middle element as pivot
48+
while low <= high:
49+
while numbers[low] < pivot:
50+
low += 1
51+
while numbers[high] > pivot:
52+
high -= 1
53+
if low <= high:
54+
numbers[low], numbers[high] = numbers[high], numbers[low]
55+
# swap(numbers, low, high)
56+
low += 1
57+
high -= 1
58+
return low # new partition position, every left is smaller than pivot, every right is bigger than pivot
59+
60+
61+
if __name__ == '__main__':
62+
merge_time = []
63+
quick_time = []
64+
for x in range(100, 10100, 100):
65+
nums = random.sample(range(1, 10100), x)
66+
nums_copy = nums[:]
67+
# print("before merge sort:", nums_copy)
68+
69+
# merge_sort_recursive(nums, 0, len(nums) - 1)
70+
start_time = time()
71+
merge_sort_recursive(nums, 0, len(nums) - 1)
72+
time_taken = time() - start_time
73+
merge_time.append(time_taken * 1000)
74+
# print("after merge sort:", nums)
75+
76+
# print("before quick sort:", nums_copy)
77+
# quick_sort_recursive(nums_copy, 0, len(nums_copy) - 1)
78+
start_time = time()
79+
quick_sort_recursive(nums_copy, 0, len(nums_copy) - 1)
80+
time_taken = time() - start_time
81+
quick_time.append(time_taken * 1000)
82+
# print("after quick sort:", nums_copy)
83+
84+
for y in range(len(merge_time)):
85+
print(merge_time[y], quick_time[y])

0 commit comments

Comments
 (0)