Why is the space complexity of stooge sort O(n)? - space-complexity

Stooge sort is a recursive sorting algorithm and it doesn't use any temp arrays.
So why is the space complexity O(n) and not O(1)?
def stoogesort(arr, l, h):
if l >= h:
return
# If first element is smaller
# than last, swap them
if arr[l]>arr[h]:
t = arr[l]
arr[l] = arr[h]
arr[h] = t
# If there are more than 2 elements in
# the array
if h-l + 1 > 2:
t = (int)((h-l + 1)/3)
# Recursively sort first 2 / 3 elements
stoogesort(arr, l, (h-t))
# Recursively sort last 2 / 3 elements
stoogesort(arr, l + t, (h))
# Recursively sort first 2 / 3 elements
# again to confirm
stoogesort(arr, l, (h-t))
Does the recursion have something to do with the space complexity of O(n)?

Related

how can we find the xor of elements of an array over the range l,r with a given number x provided there are multiple queries of such type?

For the above questioned i went through some of the query optimization techniques like square root decomposition , binary indexed tree but they don't help in solving my problem optimally . If anybody can suggest an query optimization technique through which i can solve this problem please do suggest.
You can do that in constant time, using a O(n) space, where n is the size of your array. The initial construction takes O(n).
Given an array A, you first need to build the array XOR-A, in this way:
XOR-A[0] = A[0]
For i from 1 to n-1:
XOR-A[i] = XOR-A[i-1] xor A[i]
Then you can answer a query on the range (l, r) as follows:
get_xor_range(l, r, XOR-A):
return XOR-A[l-1] xor XOR-A[r]
We use the fact that for any x, x xor x = 0. That's what makes the job here.
Edit: Sorry I did not understand the problem well at first.
Here is a method to update the array in O(m + n) time, and O(n) space, where n is the size of the array and m the number of queries.
Notation: the array is A, of size n, the queries are (l_i, r_i, x_i), 0 <= i < m
L <- array of tuple (l_i, x_i) sorted in ascending order by the l_i
R <- array of tuple (r_i, x_i) sorted in ascending order by the r_i
X <- array initialised at 0 everywhere, of size `n`
idx <- 0
l, x <- L[idx]
for j from 0 to n-1 do:
while l == j do:
X[j] <- X[j] xor x
idx ++
l, x <- L[idx]
if j < n then X[j+1] <- X[j]
idx <- 0
r, x <- R[idx]
for j from 0 to n-1 do:
while r == j do:
X[j] <- X[j] xor x
idx ++
r, x <- R[idx]
if j < n then X[j+1] <- X[j]
for j from 0 to n-1 do:
A[j] <- A[j] xor X[j]

what could be the better time complexity for the below code?

Input: Array A consisting n distinct integers, and an integer k
Output: an integer l
l = 0
for i = 1 to n
for j = 1 to n
if A[i] == A[j] + k then l = l + 1
endfor
endfor
return l
How to reduce the complexity lower than O(n^2)
well, I just have ended up with a good solution better than O(n2) I think.
First Build a BST O(N log N)
and then Search O(Log N ).
If I can use any data structures, I choose hash-set structure for storing distinct values. Hash-set can typically create with O(n) time complexity and can access with O(1) time complexity.
B =hashset(A) // requires O(n) time
for i = 1 to n
if B.contains(A[i]+k) then l= l+1
endfor // also requires n×O(1) = O(n) time
return l
I think this code has O(n) time complexity.
I would start by sorting the list of values, then assuming k is positive, you know 2 things:
in your inner loop, you only need to search from A[i+1] and up, as you know that only values after A[i] can be greater than A[i]
you stop the searching process in the inner loop once A[j] > A[i]+k, as you know that all further values will also be greater than A[i]+k
For a small set of data, the overhead of the O(log(n)) search would probably result in the algorithm being more expensive than your n^2 approach. However, as the data grows, that overhead will decrease relative to the n^2, and the substantially smaller set of comparisons will give you an overall gain.
Obviously the same principle could be applied if k was negative, you just have to swizzle round some logic, or simply negate both k AND your values.
Also - just noticed, as your integers are distinct, you can also break out of the inner loop if you find a match, as you know there can only be 1 match per entry.
That's what I'd do anyway!
Edit: Here's some example code:
{
Input: Array A consisting n distinct integers, and a positive integer k
Output: an integer l
A = sortascending(A)
l = 0
for i = 1 to n
#calculate target value
target = A[i]+k
#iterate through remaining entries
for j = i+1 to n
if target == A[j] then
#got a match, inc l then bail out
l++
break
elseif target < A[j]
#now impossible for any further data points
#to match, so bail out
break
endif
endfor
endfor
return l
}
-Chris

Number of solutions for a particular subset sum

Let's say we have a set : {1, 2, ..., n}.
How many subsets of order R exist S = {a_i1, a_i2, ...a_iR} that sum up to a certain number S?. What is the recursion for this problem?
Just define method to solve original problem. Parameters it receives are:
max number to use (n),
subset size (R),
subset sum (S),
and returns number of combinations.
To implement this method, first we have to check is it possible to make this request. It is not possible to fulfill task if:
subset size is larger than number of possible elements (R > n)
maximal possible sum is smaller than S. n + (n-1) + ... + (n-R+1) < S => R*((n-R) + (R+1)/2) < S
After that it is enough to try all possibilities for larger element that will go in subset. In python style it should be implemented like:
def combinations(n, R, S):
if R > n or R*((n-R) + (R+1)/2) < S:
return 0
c = 0
for i in xrange(R, n+1): # try i as maximal element in subset. It can go from R to n
# recursion n is i-1, since i is already used
# recursion R is R-1, since we put i in a set
# recursion S is S-i, since i is added to a set and we are looking for sum without it
c += combinations(i-1, R-1, S-i)
return c

Proof time complexity

I'm trying to determine the complexity of this two functions, where D in an integer and list is a list of integers:
def solve(D, list):
for element in List:
doFunc(element, D, list)
def doFunc(element, D, list):
quantityx = 0
if(D > 0):
for otherElement in list:
if otherElement == element:
quantityx += 1
return quantityx + (doFunc ((element+1), (D-1), list))
return 0
Intuitively, I think it has a O(n²) where n is the quantity of elements of list, but I'd like to proof it in a formal way.
First observation: solve calls doFunc, but not the other way around. Therefore, the complexity of solve will depend on the complexity of doFunc, but not the other way around. We need to figure out the complexity of doFunc first.
Let T(E, D, N) be the time complexity of doFunc as a function of E, D and the number of elements N in the list. Every time doFunc is called, we do N iterations of the loop and then invoke doFunc with E+1, D-1, and the list unchanged. Based on this, we know that the time complexity of doFunc is given by the following recursive formula:
T(E, D, N) = aN + b + T(E+1, D-1, N)
Here, a and b are some constants to be determined.
Now we need a base case for this recursive formula. Our base case, the only time we don't recurse, is when D <= 0. Assuming that D is non-negative, this means D = 0 is the base case. We get the following additional requirement:
T(E, 0, N) = c
Here, c is some constant to be determined.
Putting this all together, we can list out a few values for different values of D and see if we can identify a pattern:
D T(E, D, N)
0 c
1 c + b + aN
2 c + 2b + 2aN
3 c + 3b + 3aN
...
k c + kb + kaN
Based on this, we can guess that T(E, D, N) = c + Db + aDN for some constants a, b, c. We can see that this formula satisfies the base case and we can check that it also satisfies the recursive part (try this). Therefore, this is our function.
Assuming E, D and N are all independent and vary freely, the time complexity of doFunc is best rendered as O(c + Db + aDN) = O(DN).
Since solve calls doFunc once for each element in the list, its complexity is simply N times that of doFunc, i.e., O(DN^2).

Time complexity of an iteration algorithm

I have an iteration algorithm, where at each iteration the amount of computation decrease gradually. Here is an illustration of my algorithm:
Input size: n and Total iteration = k
iter 1: time taken -> f1 * n
iter 2: time taken -> f2 * n
iter 3: time taken -> f3 * n
...
iter k: time taken -> fk * n
where f1 > f2 > f3 >...> fk and 0 <= f1, f2,...,fk <= 1
Question: What is the time complexity of this algorithm? is it Big-O(klog n)
Update:
I think the question seems vague. I'll explain it in words:
Input for my algorithm is n and I'll run it over k iterations. but on each iteration the input size reduces by a factor which is unknown. there is no pattern in the reduction.
eg :
iter 1: input size = n (always n)
iter 2: input size = n/2 (can change)
iter 3: input size = n/5 (can change)
iter 4: input size = n/8 (can change)
...
iter k: input size = n/10 (can change)
The given information is not enough, all we can determine is that the complexity is O((f1+ ... + fk)*n)1.
Why? I'll show with an example, of two cases for fi - each giving different complexity:
Case 1: fi = 1/2^i
In this case, we get n * 1/2 + n* 1/4 + ... + n*1/2^k < n, and the algorithm is O(n)
Case 2: fi = 1/i
In this case, we get a harmonic series: n * 1/2 + n*1/3 + ... + n*1/k = n(1/2+1/3+...+1/k) = O(nlogk)
EDIT:
based on your comments and edit, it seems that the worst case for the algorithm to run as described (if I understood you correctly) is:
iter1 -> n ops
iter2 -> n/2 ops
iter3 -> n/3 ops
...
iterk -> n/k ops
If this is indeed the case, it matches the described case2, the total run time is an harmonic series: n + n/2 + n/3 + .. + n/k = n(1 + 1/2 + 1/3 + ... + 1/k), which is O(nlogk).
(1) Strictly mathematically speaking - big O is an upper asymptotic bound, and since fi <= 1, we can deduce the algorithm is O(nk), but it is NOT a strict bound, as the examples show - different fi values can give different strict bounds.
EDIT
More specifically:
If the denominators of your example:
iter 1: input size = n (always n)
iter 2: input size = n/2 (can change)
iter 3: input size = n/5 (can change)
iter 4: input size = n/8 (can change)
...
iter k: input size = n/10 (can change)
are strictly integers, then it is O(n*log k ).
Here's why. For a sequence Xn to be O(Yn), there must exists some M, a real number, and m, an integer, such that Xn < M*|Yn| for all n > m.
Now consider the sequence K = {1, 1/2, 1/3, ... 1/k}. Also consider the sequence N = {1, 2, 3, 4...}.
Now let's let Yn = N^t * K (that's the outer left product of N and K). This sequence Yn is always greater than your sequence, regardless of the values of the fi's.
So Xn < 1 * |Yn|, where Yn is the harmonic series times n. As amit pointed out, Yn falls into O(n*log k), so Xn does also. Since we couldn't have bounded Xn any closer above, our best limiting approximation for Xn is also O(n*log k).