Analyzing shell sort algorithm (big O) - java

This is the shell sort algorithm.
void shellSort(int array[], int n){
for (int gap = n/2; gap > 0; gap /= 2){
for (int i = gap; i < n; i += 1) {
int temp = array[i];
int j;
for (j = i; j >= gap && array[j - gap] > temp; j -= gap){
array[j] = array[j - gap];
}
array[j] = temp;
}
}
}
I'm certain that the outer loop of this algorithm runs logn times but I'm not sure with the middle loop and the innermost loop. This site https://stackabuse.com/shell-sort-in-java/ said that the middle loop runs n-gap times while the innermost loop runs i/gap but I'm not so sure about that. Please help me understand how the middle and innermost loop runs in this algorithm, thank you so much for anyone helping me this.

These are the loops in the algorithm:
for (int gap = n/2; gap > 0; gap /= 2) {
for (int i = gap; i < n; i += 1) {
for (j = i; j >= gap && array[j - gap] > temp; j -= gap) {
}
}
}
Let's start with loop over i. It starts at gap and goes to n with increment of 1. The next loop over j starts at current i and goes down by gap until it becomes smaller than gap. So, the loop over j executes once for i between gap and 2*gap, twice for i between 2*gap and 3*gap, three time for i between 3*gap and 4*gap and so on.
That means that the j loop will execute once for gap different values of i, twice for gap different values of i, three times for gap different values of i, etc.
The maximum value for i is n, so the loop over j can execute at maximum j_max = (n - gap)/gap times. Total number of executions of the j loop is
1+1+...+1+1 + 2+2+...+2+2 + 3+3+...+3+3 + .... + j_max+j_max+...+j_max+j_max
|_________| |_________| |_________| |_________________________|
gap times gap times gap times gap times
This sum is equal to
gap*(sum from 1 to j_max) = gap * j_max(j_max + 1) / 2 = O(gap * ((n-gap)/gap)^2) = O((n-gap)^2/gap)
This will be repeated for different values of gap in the outer loop, so the complexity is O-big of
sum((n-gap)^2/gap, for gap = n/2, n/4, n/8, ...., 4, 2, 1)
Expanding:
(n^2 - 2*n*gap + gap^2)/gap = n^2*(1/gap) - 2*n + gap
The first term is equal to n squared multiplied by the following values:
1/(n/2), 1/(n/4), 1/(n/8), ..... 1/4, 1/2, 1/1
or
2/n, 4/n, 8/n, ....., n/n
This is a sum of powers of two divided by n, so the first term gives in total
n^2/n * 2^(log2 n) = n^2
The second term is -2*n summed log2 n times, so complexity is
n*log2 n
The last term is sum of gaps, so it's sum of powers of two and its complexity is n.
Combining all together we get the worst case complexity as O(n^2).

In each iteration, the middle loop starts at gap and ends at n. So the total number of iterations would be n - gap
The inner gap starts at i. In each iteration, it gets reduced by gap.
Suppose i = 15 and gap = 3, then values of j in subsequent iterations would be 15,12,9,6,3. Which is 5 iterations. Therefore i/gap iterations in the worst case.

The formula for finding number of terms in an arithmetic sequence is
(last term - first term) / difference + 1
for (int i = gap; i < n; i += 1)
starts at i = gap and exits when i == n. Last value i can take is n - 1.
Every loop, i is incremented by 1, so this loop gets executed (n - 1 - gap) / 1 + 1 = n - gap times
for (j = i; j >= gap && array[j - gap] > temp; j -= gap)
This loop starts at j = 1 and exits when j < gap (ignore the second part by assuming worst case senario). The last value j can take is gap.
Every loop, j is decremented by gap, so this loop gets executed (i - gap)/gap + 1 = i

Related

How to add zeros to the end of value of Long object [duplicate]

for(i=0; i<array.length; i++){
sum = 4 * 5;
}
What I'm trying to do is add ((array.length - 1) - i) 0's to the value of sum. For this example assume array length is 3. sum equals 20. So for the first iteration of the loop i want to add ((3 - 1) - 0) 0's to the value of sum, so sum would be 2000. The next iteration would be ((3 - 1) - 1) 0's. so sum would equal 200 and so on. I hope what I am trying to achieve is clear.
So my questions are:
Is it possible to just shift an int to add extra digits? My search thus far suggests it is not.
If not, how can i achieve my desired goal?
Thankyou for reading my question and any help would be greatly apreciated.
You can just multiply it by 10 however many times.
200 * 10 = 2000
etc
So in your case, you'd have to use a for loop until the end of the array and multiply sum every iteration. Be careful though, because the max value of an int is 2^31, so it of surpasses that, it will roll back to 0
You can add n zeroes to the end of a number, sum by multiplying sum by 10 * n.
int sum = 20;
for (int i = 0; i < ary.length; ++i) {
int zeroesToAdd = ary.length - 1 - i
sum *= (zeroesToAdd > 0) ? zeroesToAdd * 10 : 1
}
System.out.println("Sum after loop: " + sum);
for(int i=array.length; i>0; i--){
sum = 20;
for(int j=0; j < (i - 1); j++)
{
sum *= 10;
}
}
Use inner loop to multiply by 10 the number of times i is for that iteration. You would need to reset sum in your outer loop each time.
You will want to check your for-loop condition: i>array.length. Since i starts at 0, this loop will not run unless the array's length is also 0 (an empty array). The correct condition is i < array.length.
This "shift" you want can be achieved by creating a temporary variable inside the loop that is equal to the sum times 10i. In Java's Math library, there is a pow(a,b) function that computes ab. With that in mind, what you want is something like this:
int oldSum = 4 * 5;
for (int i = 0; i < array.length; i++) {
int newSum = oldSum * Math.pow(10,i);
}
Multiply by 10 instead, and use < (not >) like
int sum = 20;
int[] array = { 1, 2, 3 };
for (int i = 0; i < array.length; i++) {
int powSum = sum;
for (int j = array.length - 1; j > i; j--) {
powSum *= 10;
}
System.out.println(powSum);
}
Output is (as requested)
2000
200
20
For array of length = n; you will end up adding (n - 1) + (n - 2) + ... + 2 + 1 + 0 zeros for i = 0, 1, ... n-2, n-1 respectively.
Therefore, number of zeros to append (z) = n * (n-1) / 2
So the answer is sum * (10 ^ z)
[EDIT]
The above can be used to find the answer after N iteration. (I miss read the question)
int n = array.length;
long sum = 20;
long pow = Math.pow(10, n-1); // for i = 0
for (int i = 0; i < n; i++) {
System.out.println(sum*pow);
pow /= 10;
}

Calculating Big O complexity of these algorithms?

I am trying to figure out the Big O notation of the following 2 algorithms below but am having trouble.
The first one is:
public static int fragment3 (int n){
int sum = 0;
for (int i = 1; i <= n*n; i *= 4)
for (int j = 0; j < i*i; j++)
sum++;
return sum;
} //end fragment 3
The answer should be O(n^4). When I try to do it myself this is what I get:
I look at the first for loop and think it runs n^2 logn times. Then for the inner for loop, it runs n times + the run time of the outer loop which is n^3 logn times. I know this is wrong but just don't get it.
For the code fragment below, the answer is O(n^9).
public static int fragment6(int n) {
int sum = 0;
for(int i=0; i < n*n*n; i++) {
if(i%100 == 0) {
for(int j=0; j < i*i; j += 10)
sum++;
} // if
else {
for(int k=0; k <= i; k++)
sum++;
} // else
} // outer loop
return sum;
} // fragment 6
When I attempt it I get: n^3 for the outer for loop. for the if statement I get n, for the second for loop I get n + the other for loop and if statement, making it n^5. Finally, I get n for the final for loop and everything adds up to O(n^6).
What am I doing wrong and what is the correct way to get its O(n^9) complexity?
For the first one.
Let's look at the inner loop..
At the first iteration of the outer loop (i=1) it runs 1 time. At the second iteration (i=4) it runs 16 (4*4) times. At the third iteration (i=16) it runs 256 (16*16) times. In general, at the (k+1)-th iteration of the outer loop inner loop runs times, as at that iteration. So the total number of iterations will be
Now, how many numbers in that sum we will have? To determine that we should have a look at the outer loop. In it i grows as , until it reaches . So the total number of iterations is .
This means that the total number of runs of inner loop is
(by dropping all the numbers from the sum but the last one).
Now we know, that the inner loop runs at least times, so we are not faster than O(n^4).
Now,
Solving for N,
where C is a constant, so we're not slower than O(n^4).
Your approach to computing big-O is flat-out wrong, and you've made computation errors.
In some common cases you can take the worst case number of iterations and multiply them together, but this isn't a sound method and fails for cases like this:
for (i = 1; i < n; i *= 2) {
for (j = 0; j < i; j++) {
sum++;
}
}
Here, the outer loop runs log_2(n) times, and the inner loop worst case is n iterations. So the wrong method that you're using will tell you that the complexity of this code is O(n log n).
The correct method is to count accurately the number of iterations, and approximate at the end only. The number of iterations is actually:
1 + 2 + 4 + 8 + ... + 2^k
where 2^k is the largest power of two less than n. This sum is 2^(k+1) - 1, which is less than 2n. So the accurate complexity is O(n).
Applying this idea to your first example:
for (int i = 1; i <= n*n; i *= 4)
for (int j = 0; j < i*i; j++)
sum++
i takes the values 4^0, 4^1, 4^2, ..., 4^k where 4^k is the largest power of 4 less than or equal to n^2.
The inner loop executes i^2 times for a given value of i.
So overall, the inner sum++ is executed this many times:
(4^0)^2 + (4^1)^2 + ... + (4^k)^2
= 2^0 + 4^2 + ... + 4^2k
= 16^0 + 16^1 + ... + 16^k
= (16^k - 1) / 15
Now by definition of k we have n^2/4 < 4^k <= n^2. So n^4/16 < 4^2k <= n^4, and since 16^k = 4^2k, we get that the total number of times the inner loop is executed is O(16^k) = O(n^4).
The second example can be solved using a similar approach.
First case:
Last run of the inner-loop with i = n^2, runs for n^4. The outer-loop up to n^2, but using exponential growth. For summation the sum over all inner-loop runs but the last is less than the last run. So inner-loop is essentially contributing O(1).
Second case:
100 % n == 0 does not matter really in O thinking
else part does not matter, it is much less than main part
outer-loop runs from 0 to n^3 => n^3
inner-loop runs from 0 to n^6 => n^6
outer-loop times inner-loop => n^9

Why is the Big-O of this algorithm N^2*log N

Fill array a from a[0] to a[n-1]: generate random numbers until you get one that is not already in the previous indexes.
This is my implementation:
public static int[] first(int n) {
int[] a = new int[n];
int count = 0;
while (count != n) {
boolean isSame = false;
int rand = r.nextInt(n) + 1;
for (int i = 0; i < n; i++) {
if(a[i] == rand) isSame = true;
}
if (isSame == false){
a[count] = rand;
count++;
}
}
return a;
}
I thought it was N^2 but it's apparently N^2logN and I'm not sure when the log function is considered.
The 0 entry is filled immediately. The 1 entry has probability 1 - 1 / n = (n - 1) / n of getting filled by a random number. So we need on average n / (n - 1) random numbers to fill the second position. In general, for the k entry we need on average n / (n - k) random numbers and for each number we need k comparisons to check if it's unique.
So we need
n * 1 / (n - 1) + n * 2 / (n - 2) + ... + n * (n - 1) / 1
comparisons on average. If we consider the right half of the sum, we see that this half is greater than
n * (n / 2) * (1 / (n / 2) + 1 / (n / 2 - 1) + ... + 1 / 1)
The sum of the fractions is known to be Θ(log(n)) because it's an harmonic series. So the whole sum is Ω(n^2*log(n)). In a similar way, we can show the sum to be O(n^2*log(n)). This means on average we need
Θ(n^2*log(n))
operations.
This is similar to the Coupon Collector problem. You pick from n items until you get one you don't already have. On average, you have O(n log n) attempts (see the link, the analysis is not trivial). and in the worst case, you examine n elements on each of those attempts. This leads to an average complexity of O(N^2 log N)
The algorithm you have is not O(n^2 lg n) because the algorithm you have may loop forever and not finish. Imagine on your first pass, you get some value $X$ and on every subsequent pass, trying to get the second value, you continue to get $X$ forever. We're talking worst case here, after all. That would loop forever. So since your worst case is never finishing, you can't really analyze.
In case you're wondering, if you know that n is always both the size of the array and the upper bound of the values, you can simply do this:
int[] vals = new int[n];
for(int i = 0; i < n; i++) {
vals[i] = i;
}
// fischer yates shuffle
for(int i = n-1; i > 0; i--) {
int idx = rand.nextInt(i + 1);
int t = vals[idx];
vals[idx] = vals[i];
vals[i] = t;
}
One loop down, one loop back. O(n). Simple.
If I'm not mistaken, the log N part comes from this part:
for(int i = 0; i < count; i++){
if(a[i] == rand) isSame = true;
}
Notice that I changed n for count because you know that you have only count elements in your array on each loop.

Add 0's to the end of an integer

for(i=0; i<array.length; i++){
sum = 4 * 5;
}
What I'm trying to do is add ((array.length - 1) - i) 0's to the value of sum. For this example assume array length is 3. sum equals 20. So for the first iteration of the loop i want to add ((3 - 1) - 0) 0's to the value of sum, so sum would be 2000. The next iteration would be ((3 - 1) - 1) 0's. so sum would equal 200 and so on. I hope what I am trying to achieve is clear.
So my questions are:
Is it possible to just shift an int to add extra digits? My search thus far suggests it is not.
If not, how can i achieve my desired goal?
Thankyou for reading my question and any help would be greatly apreciated.
You can just multiply it by 10 however many times.
200 * 10 = 2000
etc
So in your case, you'd have to use a for loop until the end of the array and multiply sum every iteration. Be careful though, because the max value of an int is 2^31, so it of surpasses that, it will roll back to 0
You can add n zeroes to the end of a number, sum by multiplying sum by 10 * n.
int sum = 20;
for (int i = 0; i < ary.length; ++i) {
int zeroesToAdd = ary.length - 1 - i
sum *= (zeroesToAdd > 0) ? zeroesToAdd * 10 : 1
}
System.out.println("Sum after loop: " + sum);
for(int i=array.length; i>0; i--){
sum = 20;
for(int j=0; j < (i - 1); j++)
{
sum *= 10;
}
}
Use inner loop to multiply by 10 the number of times i is for that iteration. You would need to reset sum in your outer loop each time.
You will want to check your for-loop condition: i>array.length. Since i starts at 0, this loop will not run unless the array's length is also 0 (an empty array). The correct condition is i < array.length.
This "shift" you want can be achieved by creating a temporary variable inside the loop that is equal to the sum times 10i. In Java's Math library, there is a pow(a,b) function that computes ab. With that in mind, what you want is something like this:
int oldSum = 4 * 5;
for (int i = 0; i < array.length; i++) {
int newSum = oldSum * Math.pow(10,i);
}
Multiply by 10 instead, and use < (not >) like
int sum = 20;
int[] array = { 1, 2, 3 };
for (int i = 0; i < array.length; i++) {
int powSum = sum;
for (int j = array.length - 1; j > i; j--) {
powSum *= 10;
}
System.out.println(powSum);
}
Output is (as requested)
2000
200
20
For array of length = n; you will end up adding (n - 1) + (n - 2) + ... + 2 + 1 + 0 zeros for i = 0, 1, ... n-2, n-1 respectively.
Therefore, number of zeros to append (z) = n * (n-1) / 2
So the answer is sum * (10 ^ z)
[EDIT]
The above can be used to find the answer after N iteration. (I miss read the question)
int n = array.length;
long sum = 20;
long pow = Math.pow(10, n-1); // for i = 0
for (int i = 0; i < n; i++) {
System.out.println(sum*pow);
pow /= 10;
}

How to count the number of operations in a loop and give a theta characterization

I just want to make sure if I am doing this correct. I am trying to count the number of operations performed for the worst case scenario in java
int sum = 0;
for (int i = 0; i < n; i++ )
sum++;
Is the number of operations 2+3n or 3+3n?
I got the answer from counting int sum = 0 and int i = 0 for the "2" and i < n, i++, and sum++ as the "3n". Or is it a 3 rather than a 2 because I have to count i < n before going through the loop?
But either way, is the theta characterization going to be Θ(n)?
Now what if there is a nested for loop like this:
int sum = 0;
for (int i = 0; i < n; i++ )
for (int a = 0; a < i; a++)
sum++;
would it be 3+n*(6a+2) = 6na+2n+3? with Θ(n^2)?
if i change the inner for loop from a < i to a < i*i, would the equation still hold as above or change?
Maybe it's easier to count the number of executions of each statement if there's only one per line:
int sum = 0; // 1 time
int i = 0; // 1 time
while (i < n) { // n+1 times
sum++; // n times
i++; // n times
}
Hence, T(n) = 3*n+3 = Θ(n).
int sum = 0; // 1 time
int i = 0; // 1 time
while (i < n) { // n+1 times
int a = 0; // n times
while (a < i) { // 1 + 2 + ... + n = n*(n+1)/2 times
sum++; // 0 + 1 + ... + n-1 = n*(n-1)/2 times
a++; // 0 + 1 + ... + n-1 = n*(n-1)/2 times
}
i++; // n times
}
Hence, T(n) = 3*n+3 + n*(n-1) + n*(n+1)/2 = Θ(n^2).
I would it count as 3+3n, because when n = 0 then you execute the following 3 commands:
int sum = 0;
int i = 0;
i < n
Now when n != 0 then you execute the declarations once (2) and for each execution of the loop each command once (3n) and the final comparison (which fails; 1). That makes 3+3n.
And yes, that would be Θ(n) (and O(n) and o(n)).
Yes, exactly. Look here for a mathematical definition.
It doesn't matter if you use 2+3n or 3+3n. You have lim_n->infty ( (3+3n)/n ) = 3 (both lim sup and lim inf are the same here). Because of that limites (which is greater 0 and not infinity), you know that is Big Theta n.
In your second example, you cannot use the inner-loop variables (a or i). The amount of sum++ operations:
When i == 0: zero sum++s are executed.
When i == 1: exactly one sum++ get's executed (when a==0).
When i == 2: 2 sum++s (a==0 and a==1)
When i == 3: 3 sum++s (a==0, a==1 and a==2)
...
When i == n-1: n-1 sum++s (a==0, a==1, ... and finally a==n-1)
That are all sum++s in your code. So let's sum them together:
0 + 1 + 2 + ... + n - 1
That is the same as (n-1)(n-2)/2.
I.e. we have Θ(n^2 + n). The same thing for a++ and a < i (well, one more to be exact but that doesn't matter). The amount of i++ ops is just n. So you end up with Θ(n^2).
It'll be 3+3n, because the comparison runs for each value of i from 0 to n inclusive. I'd says that's O(n).

Categories