Gnome sort is faster than quick sort? - algorithm

I decided to delve into sorting algorithms and implemented a few such as bubble, selection, gnome, insertion, merge and quick sort in python. However when i ran them and compared the times, gnome sort which is O(n^2) was faster than quicksort which has a O(nlogn) i believe. Question: Can someone please explain to me why my gnome sort algorithm is faster than my quick and merge sort algorithms and ways to optimize my them so that they sort faster than the less efficient algorithms. Thank you. (P.S. if you've never heard of gnome sort here's a link that will help Wikipedia Gnome Sort Algorithm)
# Sorting lib
# author: Aniekan Umoren
# course: LEAP EngTech
# date started: 2016-08-09
# last modified: 2016-08-16
import time
from random import randrange
def bubble_sort(arr):
k = 0
n = len(arr)
for numSweeps in range(n-1):
for i in range(n-k-1):
if arr[i] > arr[i+1]:
temp = arr[i]
arr[i] = arr[i+1]
arr[i+1] = temp
k += 1
return arr
def selection_sort(arr):
n = len(arr)
k = 0
for numSweeps in range(n-1):
minimum = arr[k]
for i in range(k,n):
if arr[i] < minimum:
temp = arr[k]
arr[k] = arr[i]
arr[i] = temp
minimum = arr[k]
k += 1
return arr
def gnome_sort(arr):
pos = 0
while pos < len(arr):
if pos == 0 or arr[pos] >= arr[pos-1]:
pos += 1
else:
temp = arr[pos]
arr[pos] = arr[pos-1]
arr[pos-1] = temp
pos -= 1
return arr
def insertion_sort(arr):
for i in range(len(arr)):
x = arr[i]
j = i -1
while j >= 0 and arr[j] > x:
arr[j+1] = arr[j]
j -= 1
arr[j+1] = x
return arr
def merge(arr1,arr2):
arr3 = []
size1 = len(arr1); size2 = len(arr2)
i1 = 0; i2 = 0
while i1 < size1 or i2 < size2: #both list aren't empty
if i1 == size1:
arr3.append(arr2[i2])
i2 += 1
elif i2 == size2:
arr3.append(arr1[i1])
i1 += 1
elif arr1[i1] <= arr2[i2]:
arr3.append(arr1[i1])
i1 += 1
elif arr2[i2] < arr1[i1]:
arr3.append(arr2[i2])
i2 += 1
return arr3
def merge_sort(Arr, start, end):
if start < end:
# size = (start + end + 1)
mid = (start+end)//2
arr1 = merge_sort(Arr,start, mid)
arr2 = merge_sort(Arr, mid+1, end)
Arr[start:end+1] = merge(arr1, arr2)
return Arr[start:end+1]
def partition(Arr, start, end):
rand = start + randrange(end-start)
temp = Arr[start]
Arr[start] = Arr[rand]
Arr[rand] = temp
i = start + 1
for j in range(start+1, end+1):
if Arr[j] < Arr[start]:
temp = Arr[i]
Arr[i] = Arr[j]
Arr[j] = temp
i += 1
temp = Arr[start]
Arr[start] = Arr[i-1]
Arr[i-1] = temp
return (Arr,i-1)
def quick_sort(Arr, start, end):
if start < end:
part_result = partition(Arr, start, end)
Arr = part_result[0]
piv_pos = part_result[1]
quick_sort(Arr, start, piv_pos-1)
quick_sort(Arr, piv_pos+1, end)
if end == len(Arr)-1:
return Arr
def main():
start_time = time.time()
li1 = [3, 1234, 123, 214, 21, 124, 125, 213,
34, 354, 2345,62, 34, 623, 34, 34, 53465,
346, 346434, 537373, 5347,73, 234, 62, 36,
27, 247, 4742, 47472, 24, 742, 57, 24, 7245, 24]
li2 = [3, 21, 24, 24, 27, 34, 34, 34, 34, 36,
57, 62, 62, 73, 123, 124, 125, 213, 214,
234, 247, 346, 354, 623, 724, 742, 1234,
2345, 4742, 5347, 7245, 47472, 53465, 346434, 537373]
li3 = sorted(li2, reverse = True)
li4 = [3,5,26,42,2,6]
for i in range(10000):
result = bubble_sort(li1)
print("BUBBLE SORT: %s seconds" % (time.time() - start_time))
start_time = time.time()
for i in range(10000):
result = gnome_sort(li1)
print("GNOME SORT: %s seconds" % (time.time() - start_time)) # why is gnome sort sooo fast if it has a O(n**2)
start_time = time.time()
for i in range(10000):
result = selection_sort(li1)
print("SELECTION SORT: %s seconds" % (time.time() - start_time))
start_time = time.time()
for i in range(10000):
result = insertion_sort(li1)
print("INSERTION SORT: %s seconds" % (time.time() - start_time))
size = len(li1)
start_time = time.time()
for i in range(10000):
result = merge_sort(li1, 0, size-1)
print("MERGE SORT: %s seconds" % (time.time() - start_time))
size = len(li1)
start_time = time.time()
for i in range(10000):
result = quick_sort(li1, 0, size-1)
print("QUICK SORT: %s seconds" % (time.time() - start_time))
start_time = time.time()
for i in range(10000):
result = sorted(li1)
print("TIM SORT: %s seconds" % (time.time() - start_time))
main()

Your sorts all mutate the input instead of returning a new list, so after the first sort, the input is sorted and stays sorted. Gnome sort is O(n) on already-sorted input.

Related

Why "studentRequired < m" returns true in minimum allocation problem

In Book allocation problem in isPossible() function why even if studentsRequired are less than m (students we need to allocate books for ) results the curr_min to be a viable sollution and we return true ? Does't it mean that books are not allocated to all the students ?
for example if the array is [5, 82, 52, 66, 16, 37, 38, 44, 1, 97, 71, 28, 37, 58, 77, 97, 94, 4, 9]
with m = 16. Maximum value studentsRequired gets is 13. does't it mean that only 13 students got the books but 16 should have ?
here is the code in JS, for other languages ( cpp, java, python ) please get to this GFG page
function isPossible(arr, n, m, curr_min) {
let studentsRequired = 1;
let curr_sum = 0;
for (let i = 0; i < n; i++) {
if (arr[i] > curr_min) return false;
if (curr_sum + arr[i] > curr_min) {
studentsRequired++;
curr_sum = arr[i];
if (studentsRequired > m) return false;
} else {
curr_sum += arr[i];
}
}
return true;
}
function findPages(arr, n, m) {
let sum = 0;
if (n < m) return -1;
for (let i = 0; i < n; i++) sum += arr[i];
let start = 0,
end = sum;
let result = Number.MAX_VALUE;
while (start <= end) {
let mid = Math.floor((start + end) / 2);
if (isPossible(arr, n, m, mid)) {
result = Math.min(result, mid);
end = mid - 1;
} else start = mid + 1;
}
return result;
}
const ans = findPages(
[5, 82, 52, 66, 16, 37, 38, 44, 1, 97, 71, 28, 37, 58, 77, 97, 94, 4, 9],
19,
16
);
console.log("Ans : ", ans);
What i think is happening =>
When i dry run the code for test case arr = [10,20,30,40] with m = 4, the result is S1 = [10,20], S2 = [30], S3 = [40] and we did not allocated a book to S4.
Are we assuming that students with more books (like S1 had [10,20]) can transfer there last book, 20 in this case to there right student ? And then S2 will have [20,30] so 30 will be then transferred to S3 and in the end S3 will tranfer its last book to S4 making the condition "Every student must have a book" true as now S1 = [10], S = [20], S3 = [30], S4 = [40]? Or its something else ?

Python Quicksort Debugging

I have implemented this quicksort but I seem to have bug that I can not fix, would someone mind taking a quick look at it?
The output for the example I give is close to the answer but some indices are misplaced.
def partition(array, pivot, start, end):
# move pivot to the end
temp = array[pivot]
array[pivot] = array[end]
array[end] = temp
i = start
j = end - 1
while(i < j):
# check from left for element bigger than pivot
while(i < j and array[end] > array[i]):
i = i + 1
# check from right for element smaller than pivot
while(i < j and array[end] < array[j]):
j = j - 1
# if we find a pair of misplaced elements swap them
if(i < j):
temp = array[i]
array[i] = array[j]
array[j] = temp
# move pivot element to its position
temp = array[i]
array[i] = array[end]
array[end] = temp
# return pivot position
return i
def quicksort_helper(array, start, end):
if(start < end):
pivot = (start + end) / 2
r = partition(array, pivot, start, end)
quicksort_helper(array, start, r - 1)
quicksort_helper(array, r + 1, end)
def quicksort(array):
quicksort_helper(array, 0, len(array) - 1)
array = [6, 0, 5, 1, 3, 4, -1, 10, 2, 7, 8, 9]
quicksort(array)
print array
I have a feeling the answer will be obvious but I can not find it.
Desired output:
[-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
Actual output:
[-1, 0, 2, 3, 1, 4, 5, 6, 7, 8, 9, 10]
The critical repair is in the inner while loops, where you march i and j toward each other. If all you're worried about is swapping the correct non-pivot elements, the logic you posted is fine. However, that first loop needs to be
while(i <= j and array[end] > array[i]):
i = i + 1
to ensure that i has the correct value for swapping the pivot element into the middle. Otherwise, you can swap it one element to the left of its proper position, which is why your sort fails.
You can also use Python's multiple assignment for a cleaner swap:
while(i < j):
# check from left for element bigger than pivot
while(i <= j and array[end] > array[i]):
i = i + 1
# check from right for element smaller than pivot
while(i < j and array[end] < array[j]):
j = j - 1
# if we find a pair of misplaced elements swap them
if(i < j):
array[i], array[j] = array[j], array[i]

What's wrong with my selection sort algorithm?

The answer might be obvious to the trained eye, but I've been hitting the books for a few hours now, my eyes are straining, and I can't seem to see the bug.
Below are two implementations of selection sort I wrote, and neither is sorting the input correctly. You can play with this code on an online interpreter.
def selection_sort_enum(array)
n = array.length - 1
0.upto(n - 1) do |i|
smallest = i
(i + 1).upto(n) do |j|
smallest = j if array[j] < array[i]
end
array[i], array[smallest] = array[smallest], array[i] if i != smallest
end
end
def selection_sort_loop(array)
n = array.length - 1
i = 0
while i <= n - 1
smallest = i
j = i + 1
while j <= n
smallest = j if array[j] < array[i]
j += 1
end
array[i], array[smallest] = array[smallest], array[i] if i != smallest
i += 1
end
end
Here's the test of the first implementation, selection_sort_enum:
puts "Using enum:"
a1 = [*1..10].shuffle
puts "Before sort: #{a1.inspect}"
selection_sort_enum(a1)
puts "After sort: #{a1.inspect}"
Here's the test of the second implementation, selection_sort_loop:
puts "Using while:"
a2 = [*1..10].shuffle
puts "Before sort: #{a2.inspect}"
selection_sort_enum(a2)
puts "After sort: #{a2.inspect}"
Here's the output of the first implementation, selection_sort_enum:
Using enum:
Before sort: [7, 5, 2, 10, 6, 1, 3, 4, 8, 9]
After sort: [4, 3, 1, 9, 5, 2, 6, 7, 8, 10]
Here's the output of the second implementation, selection_sort_loop:
Using while:
Before sort: [1, 10, 5, 3, 7, 4, 8, 9, 6, 2]
After sort: [1, 2, 4, 3, 6, 5, 7, 8, 9, 10]
In both the code snippets you are comparing with index i instead of index smallest.
This should work :
def selection_sort_enum(array)
n = array.length - 1
0.upto(n - 1) do |i|
smallest = i
(i + 1).upto(n) do |j|
smallest = j if array[j] < array[smallest]
end
array[i], array[smallest] = array[smallest], array[i] if i != smallest
end
end
def selection_sort_loop(array)
n = array.length - 1
i = 0
while i <= n - 1
smallest = i
j = i + 1
while j <= n
smallest = j if array[j] < array[smallest]
j += 1
end
array[i], array[smallest] = array[smallest], array[i] if i != smallest
i += 1
end
end
Output :
Using enum:
Before sort: [5, 6, 7, 9, 2, 4, 8, 1, 10, 3]
After sort: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
Using while:
Before sort: [6, 5, 9, 2, 1, 3, 10, 4, 7, 8]
After sort: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
Link to solution : http://ideone.com/pKLriY
def selection_sort_enum(array)
n = array.length - 1
0.upto(n) do |i| # n instead of (n - 1)
smallest_index = i
(i + 1).upto(n) do |j|
smallest_index = j if array[j] < array[i]
end
puts "#{array}", smallest_index
array[i], array[smallest_index] = array[smallest_index], array[i] if i != smallest_index
end
end
You might be interested in this:
def selection_sort_enum(array)
n = array.length - 1
0.upto(n - 1) do |i|
smallest = i
(i + 1).upto(n) do |j|
smallest = j if array[j] < array[i]
end
array[i], array[smallest] = array[smallest], array[i] if i != smallest
end
array # <-- added to return the modified array
end
def selection_sort_loop(array)
n = array.length - 1
i = 0
while i <= n - 1
smallest = i
j = i + 1
while j <= n
smallest = j if array[j] < array[i]
j += 1
end
array[i], array[smallest] = array[smallest], array[i] if i != smallest
i += 1
end
array # <-- added to return the modified array
end
require 'fruity'
ARY = (1 .. 100).to_a.shuffle
compare do
_enum { selection_sort_enum(ARY.dup) }
_loop { selection_sort_loop(ARY.dup) }
end
Which results in:
# >> Running each test once. Test will take about 1 second.
# >> _enum is faster than _loop by 3x ± 1.0

Generating Ascending Sequence 2^p*3^q

I was interested in implementing a specific Shellsort method I read about that had the same time complexity as a bitonic sort. However, it requires the gap sequence to be the sequence of numbers [1, N-1] that satisfy the expression 2^p*3^q for any integers p and q. In layman's terms, all the numbers in that range that are only divisible by 2 and 3 an integer amount of times. Is there a relatively efficient method for generating this sequence?
Numbers of that form are called 3-smooth. Dijkstra studied the closely related problem of generating 5-smooth or regular numbers, proposing an algorithm that generates the sequence S of 5-smooth numbers by starting S with 1 and then doing a sorted merge of the sequences 2S, 3S, and 5S. Here's a rendering of this idea in Python for 3-smooth numbers, as an infinite generator.
def threesmooth():
S = [1]
i2 = 0 # current index in 2S
i3 = 0 # current index in 3S
while True:
yield S[-1]
n2 = 2 * S[i2]
n3 = 3 * S[i3]
S.append(min(n2, n3))
i2 += n2 <= n3
i3 += n2 >= n3
Simplest I can think of is to run a nested loop over p and q and then sort the result. In Python:
N=100
products_of_powers_of_2and3 = []
power_of_2 = 1
while power_of_2 < N:
product_of_powers_of_2and3 = power_of_2
while product_of_powers_of_2and3 < N:
products_of_powers_of_2and3.append(product_of_powers_of_2and3)
product_of_powers_of_2and3 *= 3
power_of_2 *= 2
products_of_powers_of_2and3.sort()
print products_of_powers_of_2and3
result
[1, 2, 3, 4, 6, 8, 9, 12, 16, 18, 24, 27, 32, 36, 48, 54, 64, 72, 81, 96]
(before sorting the products_of_powers_of_2and3 is
[1, 3, 9, 27, 81, 2, 6, 18, 54, 4, 12, 36, 8, 24, 72, 16, 48, 32, 96, 64]
)
Given the size of products_of_powers_of_2and3 is of the order of log2N*log3N the list doesn't grow very fast and sorting it doesn't seem particularly inefficient. E.g. even for N = 1 million, the list is very short, 142 items, so you don't need to worry.
You can do it very easy in JavaScript
arr = [];
n = 20;
function generateSeries() {
for (let i = 0; i < n; i++) {
for (let j = 0; j < n; j++) {
arr.push(Math.pow(2, i) * Math.pow(3, j))
}
}
sort();
}
function sort() {
arr.sort((a, b) => {
if (a < b) {return -1;}
if (a > b) {return 1;}
return 0;
});
}
function solution(N) {
arr = [];
if (N >= 0 && N <= 200 ) {
generateSeries();
console.log("arr >>>>>", arr);
console.log("result >>>>>", arr[N]);
return arr[N];
}
}
N = 200
res =[]
a,b = 2,3
for i in range(N):
for j in range(N):
temp1=a**i
temp2=b**j
temp=temp1*temp2
if temp<=200:
res.append(temp)
res = sorted(res)
print(res)

Generating integer partition by its number

I'm trying to generate decent partition of given integer number N numbered K in lexicographical order, e.g. for N = 5, K = 3 we got:
5 = 1 + 1 + 1 + 1 + 1
5 = 1 + 1 + 1 + 2
5 = 1 + 1 + 3
5 = 1 + 2 + 2
5 = 1 + 4
5 = 2 + 3
5 = 5
And the third one is 1 + 1 + 3.
How can I generate this without generating every partition(in C language, but most of all I need algorithm)?
Going to find maximal number in partition(assuming we can find number of partitions d[i][j], where i is number and j is maximal integer in its partition), then decrease the original number and number we are looking for. So yes, I'm trying to use dynamic programming. Still working on code.
This doesn't work at all:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
FILE *F1, *F2;
main()
{
long long i, j, p, n, k, l, m[102][102];
short c[102];
F1 = fopen("num2part.in", "r");
F2 = fopen ("num2part.out", "w");
n = 0;
fscanf (F1, "%lld %lld", &n, &k);
p = 0;
m[0][0] = 1;
for ( i = 0; i <= n; i++)
{
for (j = 1; j <= i; j++)
{
m[i][j] = m[i - j][j] + m[i][j - 1];
}
for (j = i + 1; j <= n; j++)
{
m[i][j] = m[i][i];
}
}
l = n;
p = n;
j = n;
while (k > 0)
{
while ( k < m[l][j])
{
if (j == 0)
{
while (l > 0)
{
c[p] = 1;
p--;
l--;
}
break;
}
j--;
}
k -=m[l][j];
c[p] = j + 1;
p--;
l -= c[p + 1];
}
//printing answer here, answer is contained in array from c[p] to c[n]
}
Here is some example Python code that generates the partitions:
cache = {}
def p3(n,val=1):
"""Returns number of ascending partitions of n if all values are >= val"""
if n==0:
return 1 # No choice in partitioning
key = n,val
if key in cache:
return cache[key]
# Choose next value x
r = sum(p3(n-x,x) for x in xrange(val,n+1))
cache[key]=r
return r
def ascending_partition(n,k):
"""Generate the k lexicographically ordered partition of n into integer parts"""
P = []
val = 1 # All values must be greater than this
while n:
# Choose the next number
for x in xrange(val,n+1):
count = p3(n-x,x)
if k >= count:
# Keep trying to find the correct digit
k -= count
elif count: # Check that there are some valid positions with this digit
# This must be the correct digit for this location
P.append(x)
n -= x
val = x
break
return P
n=5
for k in range(p3(n)):
print k,ascending_partition(n,k)
It prints:
0 [1, 1, 1, 1, 1]
1 [1, 1, 1, 2]
2 [1, 1, 3]
3 [1, 2, 2]
4 [1, 4]
5 [2, 3]
6 [5]
This can be used to generate an arbitrary partition without generating all the intermediate ones. For example, there are 9253082936723602 partitions of 300.
print ascending_partition(300,10**15)
prints
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 3, 3, 4, 4, 4, 4, 4, 4, 5, 7, 8, 8, 11, 12, 13, 14, 14, 17, 17, 48, 52]
def _yieldParts(num,lt):
''' It generate a comination set'''
if not num:
yield ()
for i in range(min(num,lt),0,-1):
for parts in _yieldParts(num-i,i):
yield (i,)+parts
def patition(number,kSum,maxIntInTupple):
''' It generates a comination set with sum of kSum is equal to number
maxIntInTupple is for maximum integer can be in tupple'''
for p in _yieldParts(number,maxIntInTupple):
if(len(p) <=kSum):
if(len(p)<kSum):
while len(p) < kSum:
p+=(0,)
print p
patition(40,8,40)
Output:
-------
(40,0,0,0,0,0,0,0)
(39,1,0,0,0,0,0,0)
.
.
.
.

Resources