Time complexity modified bubblesort - java

Have this java code for bubblesort:
public void sort() {
for(int i = 1; i < getElementCount() ; ++i) {
for(int j = getElementCount()-1; j >= i; j--) {
if (cmp(j,j-1) < 0) swap(j, j-1);
}
}
}
the method "cmp" and "swap" are as follows:
public int cmp(int i, int j) {
return get(i).intValue()-get(j).intValue();
}
public void swap(int i, int j) {
Integer tmp = get(i);
set(i, get(j));
set(j, tmp);
}
I have now written an improved version of the Bubblesort where the sorting method "sort()" looks like this:
public void sort() {
boolean done = false;
for(int i = 1; i < getElementCount() && !done; ++i) {
done = true;
for(int j = getElementCount()-1; j >= i; j--) {
if (cmp(j,j-1) < 0) {
swap(j, j-1);
done = false;
}
}
}
}
Can anyone explain how to compute the time complexity of the latter algorithm? I'm thinking it's comparing n elements one time, and therefore it has complexity O(1) in its best case, and O(n^2) in it's worst case scenario, but I don't know if I'm right and would like to know how to think on this issue.

The complexity tells the programmer how long time it takes to process the data.
The O(1) complexity says that no matter how many elements it will only take one operation.
Insert a value in an array would have O(1)
E.g.
array[100] = value;
In your best case you will have to loop throught the entire array and compare each element.
Your complexity code is then O(n) where n is number of elements in the array.
In the worst case you will have to run through the array once for each element, that would give a complexity of O(n*n)

I just looked over what you've done and it is exactly the same as the one you had previously listed. You have set a boolean condition done = false and then you are checking the negation of it which will always evaluate to true - exactly the same logic as before. You can remove done in your code and you will see that it runs exactly the same. Just like before, you will have a best case complexity of O(n) and a worst case complexity of O(n^2). There is no way any sorting algorithm is O(1) as at the very least you at least have to move through the list once which gives O(n).

Worst Case : If the array is sorted in the sorted reverse order (descending) , it will show the worst time complexity of O(N^2).
Best Case : If the array is in sorted order, then the inner loop will go through each element at least once so it is O(N) - > (If you break out of the loop using the information in done, which is not present in the code).
At no point can it be a O(1) (In fact it is mathematically impossible to get a lower function than O(N) as the lower bounds for sorting is Omega(N) for comparison based sorts)
Omega(N) is the lowest possible function, as for comparison you have to see all elements at least once.

The best way is to represent your loops using Sigma notation like the following (General Case):
'c' here refers to the constant time of if, cmp, and swap that execute inside the innermost loop.
For the best case (modified bubble sort), the running time should look like this:

Related

Java - Time Complexity O(N**2)

I'm practicing on Codility. There is an Easy Level question, yet I'm stuck on performance. The test result analysis marks this code as O(N**2), but obviously there are not any nested loops. Can anyone tell me why my code is O(N**2)?
public static int solution(int X, int[] A) {
List<Integer> temp = new ArrayList<>();
for (int i = 1; i <= X; i++ ){
temp.add(i);
}
int counter = 0;
int res = -1;
for (int i: A){
if (temp.contains(i)) {
temp.remove(new Integer(i));
}
if (temp.size() == 0){
res= counter;
break;
}
counter++;
}
if (temp.size() != 0){
res = -1;
}
return res;
}
That is because of the use of contains. temp is an ArrayList, and contains performs a linear lookup. Within a for loop, that will become O(N2).
The test result analysis marks this code as O(N**2), but obviously
there are not any nested loops. Can anyone tell me why my code is
O(N**2)?
Asymptotic complexity is not (just) about loop counting. You have a loop over the elements of A, and within it you invoke temp.contains(), and conditionally temp.remove(). For an ArrayList, the cost of each of these is proportional to the number of elements in temp.
Overall, then, if N is A.length then the asymptotic complexity of your method is O(X * N). Codility's analysis seems not quite right, but if X cannot be taken as bounded by a constant then your code nevertheless is more complex than O(N). If Codility performed an heuristic analysis that introduced an artificial relationship between X and N, then subject to that functional relationship, your method could indeed be O(N2).
You can definitely do better. Your method appears to be computing the length of the smallest initial sub-array of A that contains all of the integers between 1 and X. To do this efficiently, you do need some kind of mechanism to track what values have been seen, but containment in a List is an expensive choice for that. Consider instead an array of boolean to track which specific values have been seen, and a separate count of how many have been seen:
boolean[] seen = new boolean[X + 1];
int numSeen = 0;
With that in hand, loop over the elements of A, and for each element i that is in the range 1 ... X, test seen[i] (costs O(1) per test). If true, do nothing. If false, set seen[i] to true (O(1)), increment numSeen (O(1)), and test whether numSeen has reached X (O(1)). Return the number of elements that have to be examined before numSeen reaches X, or -1 if numSeen never does reach X. (Details left as an exercise.)
With that, every loop iteration performs O(1) work regardless of any bound on X, and that O(1) work is in fact cheap, which is a different consideration. Overall: O(N), and pretty efficient, too.
The other loop is hidden in the remove method of ArrayList. The remove method of ArrayList is O(N) because it has to shift the elements to fill the gap.
for (int i: A){ // ===> O(N)
if (temp.contains(i)) {
temp.remove(new Integer(i)); //===> O(N)
}
if (temp.size() == 0){
res= counter;
break;
}
counter++;
}

What counts as a comparison in algorithm analysis?

MAIN QUESTION: When keeping track of comparisons, what actually counts as a comparison? Should I only count comparisons between array items since that's what the algorithm is meant for or is it more widely accepted to count every single comparison?
Currently, I am trying to wrap my head around the fact that I'm told that the theoretical number of comparisons for the worst case bubble sort algorithm is as follows:
Amount of comparisons:
(N-1) + (N-2) + (N-3) + ... + 2 + 1 = (N*(N-1))/2 = (N^2-N)/2 < N^2
So according to the formula (N^2-N)/2, with an input size (N) of 10, I would get a total of 45 comparisons. However, it is mentioned that this calculation only applies to the comparison operation in the inner loop of this pseudo code:
for i:=1 to N-1 do
{
for j:=0 to N-i do
{
if A[j] > A[j+1] // This is the comparison that's counted.
{
temp := A[j]
A[j] := A[j+1]
A[j+1] := temp
}
}
}
Now in Java, my code looks like this:
public int[] bubble(int[] array)
{
int comparisons = 0;
int exchanges = 0;
int temp;
int numberOfItems = array.length;
boolean cont = true;
comparisons++; // When pass == numberOfItems, a comparison will be made by the for loop that wouldn't otherwise be counted.
for (int pass=1; pass != numberOfItems; pass++)
{
comparisons = comparisons + 2; // Counts both the outer for loop comparison and the if statement comparison.
if (cont) // If any exchanges have taken place, cont will be true.
{
cont = false;
comparisons++; // Counts the inner for loop comparison
for (int index = 0; index != (numberOfItems - pass); index++)
{
comparisons++; // Counts the if statement comparison.
if (array[index] > array[index+1])
{
temp = array[index];
array[index] = array[index+1];
array[index+1] = temp;
cont = true;
exchanges++;
} // end inner if
} // end inner for
}
else
{
break; // end outer if
}
}
System.out.println("Comparisons = " + comparisons + "\tExchanges = " + exchanges);
return array;
}
After performing the worst case scenario on my code (using an array with 10 elements that are in the reverse order), I have gotten a total of 73 comparisons. This seems like a crazy high overshoot of the theoretical result which was 45 comparisons. This feels right to me though since I've accounted for all for loops and if statements.
Any help is greatly appreciated!
EDIT: I have noticed an error in my total comparison count for my inner loop. I wound up counting the inner loop twice before, but now it is fixed. Instead of getting 118 comparisons, I now get 73. However, the question still stands.
When measuring the number of comparisons in a sort, you only count comparisons between the array items. You count them whether or not they're actually in the array when you compare them.
The idea is that, instead of simple integers, the array might contain things that take a long time to compare. An array of of strings, for example, can be bubble-sorted using N(N-1)/2 string comparions, even though a single string comparison might require many other operations, including many comparisons of individual characters.
Measuring the performance of a sorting algorithm in terms of the number of comparisons makes the measurement independent of the type of things being sorted.
In evaluating sorting algorithms, it is common to count all comparisons between array elements as having equivalent cost, while ignoring comparisons between things like array indices. The basic concept is that in order for sorting operations to remain distinctly different from radix partitioning, the size of the items being sorted would need to increase as the number of them increased. Suppose, for example, one had an array of 1,000,000,000 char values and wanted to sort them. While one could use Quicksort, bubble sort, or something else, a faster approach would simply be to use an int[65536] and count how many of each value there are. Even if one needed to sort items which had char keys, the best way to do that would be to determine where to place the last item with a key of 0 (the number of items with a key of zero, minus one), where to place the last item with a key of 1 (number of items with keys of 0 or 1, minus one), etc. All such operations would take time proportional to the number of items plus the number of possible key values, without any lg(N) factor.
Note that if one ignores "bookkeeping" costs, algorithms like Quicksort aren't quite optimal. A sorting algorithm which is designed to maximize the amount of information gained from each comparison may do slightly fewer comparisons. Unless comparisons are very expensive, however, such a sorting algorithm would likely waste more time being "smart" than it would have spent being "stupid".
One issue I haven't seen discussed much, though I would think it could offer significant benefit in many real-world cases, would be optimizing sequences of comparisons between items that are known to be in a narrow range. If while performing a Quicksort on a series of thousand-character path names, one is processing a partition whose entries are all known to between two names that share the first 950 characters, there would be no need to examine the first 950 characters of any names in that partition. Such optimizations would not likely be meaningful in big-O terms unless key length was a parameter, but in the real world I would expect it could sometimes have an order-of-magnitude impact.
the comparison variable should only be incremented after the if statement has been reached in the execution of the code. The if statement is only reached if the condition stated in the outer and inner for loop have been met therefore the code should be like this.
Also dont forget to change the condition in the for loops from using != to <= The new java code:
public int[] bubble(int[] array)
{
int comparisons = 0;
int exchanges = 0;
int temp;
int numberOfItems = array.length;
boolean cont = true;
for (int pass=1; pass <= numberOfItems; pass++)
{
if (cont) // If any exchanges have taken place, cont will be true.
{
cont = false;
for (int index = 0; index <= (numberOfItems - pass); index++)
{
if (array[index] > array[index+1])
{ comparison++;
temp = array[index];
array[index] = array[index+1];
array[index+1] = temp;
cont = true;
exchanges++;
} // end inner if
} // end inner for
}
}
comparison++; // here you increment by one because you must also count the comparison that failed
System.out.println("Comparisons = " + comparisons + "\tExchanges = " + exchanges);
return array;
}

Using Selection Sort to Sort a Nearly Sorted Array?

I was playing around with this website (https://www.toptal.com/developers/sorting-algorithms/) and clicked on "Play Nearly Sorted". I noticed that the Selection Sort algorithm was considerably slower than the others. Can you please explain why this is so, and possibly point me to a link with more information on this. Thanks!
You'll notice selection sort actually just takes the same amount of steps regardless of the initial ordering of the data.
Selection sort can be described like this (pseudocode, assumes data is a list of length n and indices run from 1 to n inclusive)
for pass = 1 to n {
//at each pass, find the minimal item in the list (that we haven't already sorted)
indexMin = pass
for item = pass + 1 to n {
if data[item] < data[indexMin] {
indexMin = item
}
}
swap data[pass] and data[indexMin]
}
Like it's name suggests, it repeatedly (n times in fact) selects the smallest element, and then puts it in its proper position. This creates a sorted sublist at the beginning running from position 1 to position pass at each pass.
As you can see, selection sort has no capability of terminating early. It just takes a blind shotgun approach at sorting. It will always runs exactly n passes, even on an array that's already completely sorted.
Other algorithms often "know" when they're done sorting, and can terminate early. As Tim's comment says, selection sort is a brute force approach that will never run any faster than n^2
To fully understand the runtime of common sorting algorithms, it requires you to read through their pseudo code. In a "nearly sorted case," selection sort is the slowest because no matter what, its running time is always O(n^2), which runs in a polynomial time. Polynomial time is considered as the slowest among all the time complexity presented in the website you attached. Here is the code for selection sort:
public static void selectionSort(int [] A) {
for(int i = 0; i < A.length - 1; i++) {
int min = i;
for(int j = i + 1; j < A.length; j++){
if(A[j] < A[min])
min = j;
}
}
swap(A, i, min);
}
It always runs with these two "for" loops regardless the how much sorted the array A is. Regarding other algorithms, they are relatively "smarter" (faster) with the initial array A if it is somehow or nearly sorted. You can ask yourself in another way around; why insertion sort is so fast in a "nearly sorted array?" Hope this helps!

Time complexity of a method that checks a sublist in a linked list

I need to write a method public int subList (CharList list)
that gets a list and returns how many times this list exist.
For example:
my list is a b c d a b g e
parameter list ita b it will return 2.
my list is b b b b
parameter list is b b it will return 3.
The method should be as efficient as possible.
Currently my problem is I don't know what do they mean when they say as efficient as possible, If I loop through the list n times, and everytime I find the same character I loop on both list and go back to where I was it will be O(n^2)? is there a better way to make is O(n) or below?
This is in effective searching string in a string, which is O(n) complexity
http://en.wikipedia.org/wiki/Knuth%E2%80%93Morris%E2%80%93Pratt_algorithm
and as you find first occurrence you can just keep looking for next occurrence in remaining list so it's still O(n) to find all occurrence
Why O(n^2)? It is O(n) because the you need to iterate only once through the list.
Lets do this with a char[] to simplify the explanation.
The simple approach is as follows:
public int countSublists (char[] list, char[] sublist)
int count = 0;
for (i = 0; i < list.length; i++) {
for (j = 0; j <= sublist.length; j++) {
if (j = sublist.length) {
count++;
} else if (i + j > list.length || list[i + j] != sublist[j]) {
break;
}
}
}
return count;
}
This has worst-case complexity of O(N*M) where N is the length of list and M is the length of sublist. And best-case complexity of O(N) ... when there are no instances of the first character of sublist in list.
There are various other algorithms that give better performance ... down to (I think) O(N/M) best-case. The general idea is that you use the value of the character at list[i + j] when there is a mismatch to allow you to skip some characters.
You can find details of various advanced search algorithms are linked from the Wikipedia page on String Searching algorithms ... which also includes a summary of the respective algorithm complexity.
But the thing to note is that the advanced search algorithms all involve some precompution steps, whose complexity is some function of M. If N is small enough, the cost of the precomputation may outweigh the saving at search time. (On the other hand, if you are repeatedly counting the same sublist in different lists, and you can reuse the precomputed tables, then you can amortize the precomputation cost ...)

Efficiently determine the parity of a permutation

I have an int[] array of length N containing the values 0, 1, 2, .... (N-1), i.e. it represents a permutation of integer indexes.
What's the most efficient way to determine if the permutation has odd or even parity?
(I'm particularly keen to avoid allocating objects for temporary working space if possible....)
I think you can do this in O(n) time and O(n) space by simply computing the cycle decomposition.
You can compute the cycle decomposition in O(n) by simply starting with the first element and following the path until you return to the start. This gives you the first cycle. Mark each node as visited as you follow the path.
Then repeat for the next unvisited node until all nodes are marked as visited.
The parity of a cycle of length k is (k-1)%2, so you can simply add up the parities of all the cycles you have discovered to find the parity of the overall permutation.
Saving space
One way of marking the nodes as visited would be to add N to each value in the array when it is visited. You would then be able to do a final tidying O(n) pass to turn all the numbers back to the original values.
I selected the answer by Peter de Rivaz as the correct answer as this was the algorithmic approach I ended up using.
However I used a couple of extra optimisations so I thought I would share them:
Examine the size of data first
If it is greater than 64, use a java.util.BitSet to store the visited elements
If it is less than or equal to 64, use a long with bitwise operations to store the visited elements. This makes it O(1) space for many applications that only use small permutations.
Actually return the swap count rather than the parity. This gives you the parity if you need it, but is potentially useful for other purposes, and is no more expensive to compute.
Code below:
public int swapCount() {
if (length()<=64) {
return swapCountSmall();
} else {
return swapCountLong();
}
}
private int swapCountLong() {
int n=length();
int swaps=0;
BitSet seen=new BitSet(n);
for (int i=0; i<n; i++) {
if (seen.get(i)) continue;
seen.set(i);
for(int j=data[i]; !seen.get(j); j=data[j]) {
seen.set(j);
swaps++;
}
}
return swaps;
}
private int swapCountSmall() {
int n=length();
int swaps=0;
long seen=0;
for (int i=0; i<n; i++) {
long mask=(1L<<i);
if ((seen&mask)!=0) continue;
seen|=mask;
for(int j=data[i]; (seen&(1L<<j))==0; j=data[j]) {
seen|=(1L<<j);
swaps++;
}
}
return swaps;
}
You want the parity of the number of inversions. You can do this in O(n * log n) time using merge sort, but either you lose the initial array, or you need extra memory on the order of O(n).
A simple algorithm that uses O(n) extra space and is O(n * log n):
inv = 0
mergesort A into a copy B
for i from 1 to length(A):
binary search for position j of A[i] in B
remove B[j] from B
inv = inv + (j - 1)
That said, I don't think it's possible to do it in sublinear memory. See also:
https://cs.stackexchange.com/questions/3200/counting-inversion-pairs
https://mathoverflow.net/questions/72669/finding-the-parity-of-a-permutation-in-little-space
Consider this approach...
From the permutation, get the inverse permutation, by swapping the rows and
sorting according to the top row order. This is O(nlogn)
Then, simulate performing the inverse permutation and count the swaps, for O(n). This should give the parity of the permutation, according to this
An even permutation can be obtained as the composition of an even
number and only an even number of exchanges (called transpositions) of
two elements, while an odd permutation be obtained by (only) an odd
number of transpositions.
from Wikipedia.
Here's some code I had lying around, which performs an inverse permutation, I just modified it a bit to count swaps, you can just remove all mention of a, p contains the inverse permutation.
size_t
permute_inverse (std::vector<int> &a, std::vector<size_t> &p) {
size_t cnt = 0
for (size_t i = 0; i < a.size(); ++i) {
while (i != p[i]) {
++cnt;
std::swap (a[i], a[p[i]]);
std::swap (p[i], p[p[i]]);
}
}
return cnt;
}

Categories