javaalgorithmmedian-of-medians

Median of Medians algorithm error


I'm implementing a select-kth algorithm using the Median of Medians pivot method. Specifically, I'm following the pseudocode listed here.. However, my code crashes (error discussed below), I see why it crashes, but I don't understand what I can do about it.

The Error

Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 32017219
at Selection.partition(Selection.java:173)

The Reason
In the wiki link, the method select will return a value if left == right. However, when the mutual recursion is called from the return statement of pivot, that means it will return a value (and not an index) back to the parent and the parent will set that value as the pivotIndex.

The Code

public static int alg5(int[] a, int left, int right, int k) {
    if (left == right) {
        return a[left];
    }

    while (true) {
        int pivotIndex = pivot(a, left, right);         
        pivotIndex = partition(a, left, right, pivotIndex);

        if (k == pivotIndex) {
            return a[k];
        } else if (k < pivotIndex) {
            right = pivotIndex - 1;
        } else {
            left = pivotIndex + 1;
        }

    }
}

public static int pivot(int[] a, int left, int right) {
    for (int i = left; i <= right; i = i + 5) {
        int subRight = i + 4;
        if (subRight > right) {
            subRight = right;
        }

        int median5 = partition5(a, i, subRight);
        swap(a, median5, (int)(Math.floor(i / 5)));
    }

    return alg5(a, left, (int)(left + Math.ceil((right - left) / 5) - 1), ((right - left) / 10));
}

public static int partition5(int[] a, int left, int right) {
    Arrays.sort(a);

    return ((a.length - 1) / 2);
}

public static int partition(int[] a, int left, int right, int pivotIndex) {
    int pivotValue = a[pivotIndex];
    a = swap(a, pivotIndex, right);
    int storeIndex = left;

    for (int i = left; i <= right; i++) {
        int aOfi = a[i];
        if (a[i] < pivotValue) {
            swap(a, storeIndex, i);
            storeIndex++;
        }
    }

    swap(a, right, storeIndex);

    return storeIndex;
}

I completely understand why my code isn't working, I just don't understand how to fix it since it looks to be exactly what the algorithm specifies. Pointers are greatly appreciated!


Solution

  • There are quite a few mistakes:

    1. The method pivot should not modify the array a. It should just find a pivot for the future partition. A dirty fix is to call pivot(a.clone(), left, right);. (You shouldn't do this, just to give you an idea.)

    2. Math.ceil((right - left) / 5) is an integer division. You should cast them to floats: Math.ceil(((float)(right - left)) / 5f).

    3. In partition5, you sort the whole array a! You should just do comparisons to find the index of the median between a[left] and a[right].

    4. At some point you might have right < left, so the first line of alg5 you should write if (left >= right)