0

I am trying to write a variation of insertion sort. In my algorithm, the swapping of values doesn't happen when finding the correct place for item in hand. Instead, it uses a lookup table (an array containing "links" to smaller values in the main array at corresponding positions) to find the correct position of the item. When we are done with all n elements in the main array, we haven't actually changed any of the elements in the main array itself, but an array named smaller will contain the links to immediate smaller values at positions i, i+1, ... n in correspondence to every element i, i+1, ... n in the main array. Finally, we iterate through the array smaller, starting from the index where the largest value in the main array existed, and populate another empty array in backward direction to finally get the sorted sequence.

Somewhat hacky/verbose implementation of the algorithm just described:

public static int [] sort (int[] a) {
    int length = a.length;

    int sorted [] = new int [length];
    int smaller [] = new int [length];

    //debug helpers
    long e = 0, t = 0;

    int large = 0;
    smaller[large] = -1;

    here:
    for (int i = 1; i < length; i++) {

        if (a[i] > a[large]) {
            smaller[i] = large;
            large = i;
            continue;
        }

        int prevLarge = large;
        int temp = prevLarge;

        long st = System.currentTimeMillis();
        while (prevLarge > -1 && a[prevLarge] >= a[i]) {
            e++;
            if (smaller[prevLarge] == -1) {
                smaller[i] = -1;
                smaller[prevLarge] = i;
                continue here;
            }

            temp = prevLarge;
            prevLarge = smaller[prevLarge];
        }
        long et = System.currentTimeMillis();

        t += (et - st);

        smaller[i] = prevLarge;
        smaller[temp] = i;
    }

    for (int i = length - 1; i >= 0; i--) {
        sorted[i] = a[large];
        large = smaller[large];
    }

    App.print("DevSort while loop execution: " + (e));
    App.print("DevSort while loop time: " + (t));

    return sorted;
}

The variables e and t contain the number of times the inner while loop is executed and total time taken to execute the while loop e times, respectively.

Here is a modified version of insertion sort:

public static int [] sort (int a[]) {
    int n = a.length;

    //debug helpers
    long e = 0, t = 0;

    for (int j = 1; j < n; j++) {
        int key = a[j];
        int i = j - 1;

        long st = System.currentTimeMillis();
        while ( (i > -1) && (a[i] >= key)) {
            e++;

            // simply crap
            if (1 == 1) {
                int x = 0;
                int y = 1;
                int z = 2;
            }

            a[i + 1] = a[i];
            i--;
        }
        long et = System.currentTimeMillis();

        t += (et - st);

        a[i+1] = key;
    }

    App.print("InsertSort while loop execution: " + (e));
    App.print("InsertSort while loop time: " + (t));

    return a;
}

if block inside the while loop is introduced just to match the number of statements inside the while loop of my "hacky" algorithm. Note that two variables e and t are introduced also in the modified insertion sort.

The thing that's confusing is that even though the while loop of insertion sort runs exactly equal number of times the while loop inside my "hacky" algorithm, t for insertion sort is significantly smaller than t for my algorithm.

For a particular run, if n = 10,000:

Total time taken by insertion sort's while loop: 20ms

Total time taken by my algorithm's while loop: 98ms

if n = 100,000;

Total time taken by insertion sort's while loop: 1100ms

Total time taken by my algorithm's while loop: 25251ms

In fact, because the condition 1 == 1 is always true, insertion sort's if block inside the while loop must execute more often than the one inside while loop of my algorithm. Can someone explain what's going on?

Two arrays containing same elements in the same order are being sorted using each algorithm.

  • Consider updating your question with your measurements (what is "worse"?). Also, is there stil a (substantial) difference when you run both algorithms, say, millions of times? Also performing sorts of large arrays. – Bart Kiers Feb 23 '18 at 19:07
  • As the input size grows, the time taken by my algorithm compared to the time taken by insertion sort is even more significant. Including running times in the question. –  Feb 23 '18 at 19:10
  • Your technique only has benefits if the array being sorted has objects that take substantial time to move (i.e. each object occupies a substantial amount of memory). But since your input is an array of `int`, moving things around in the input array is just as fast as moving things around in the `smaller` array. – user3386109 Feb 23 '18 at 20:54
  • I think the compiler is intelligent enough to see that you just assign 3 local variables and strips it completely in the 2nd example. Also there is something called branch prediction, the compiler will see that it just has to execute something everytime (if it would be used later) and the if isn't really needed. In other words the two loops are not really comparable, they are completely different. – maraca Feb 23 '18 at 21:22
  • @user3386109 that's what I thought. It should be at least as fast as insertion sort. But it turns out to be very much slower than that. –  Feb 24 '18 at 01:31
  • 2
    @DevashishJaiswal My previous comment was based solely on your description of the algorithm. Now that I've studied the code, I'm going to say that the difference in speed is due to the fact that your implementation is very [cache unfriendly](https://stackoverflow.com/questions/16699247). Specifically, when the code updates the links in the `smaller` array, it's jumping all over the place in memory. In contrast, when the insertion sort moves things, it does so using sequential memory addresses. So the insertion sort has much better cache performance. – user3386109 Feb 24 '18 at 02:21
  • @user3386109 Thanks for the link. Now it's clear why my algorithm is not efficient. –  Feb 24 '18 at 02:45

0 Answers0