tags:

views:

212

answers:

5

I have a divide and conquer method to find the i th smallest element from an array. Here is the code:

public class rand_select{
    public static int Rand_partition(int a[], int p, int q, int i) {
        //smallest in a[p..q]
        if ( p==q) return a[p];
        int r=partition (a,p,q);
        int k=r-p+1;
        if (i==k) return a[r];
        if (i<k){
            return Rand_partition(a,p,r-1,i);
        }
        return Rand_partition(a,r-1,q,i-k);
    }

    public static void main(String[] args) {
        int a[]=new int []{6,10,13,15,8,3,2,12};
        System.out.println(Rand_partition(a,0,a.length-1,7));
    }

    public static  int partition(int a[],int p,int q) {
        int  m=a[0];
        while (p < q) {
            while (p < q && a[p++] < m) {
                p++;
            }
            while (q > p && a[q--] > m) {
                q--;
            }
            int t = a[p];
            a[p] = a[q];
            a[q] = t;
        }
        int k=0;
        for (int i=0; i < a.length; i++) {
            if ( a[i]==m){
                k=i;
            }
        }
        return k;
    }
}

However, I get an exception when run: java.lang.ArrayIndexOutOfBoundsException.

+2  A: 

No clue what your bug is (I dislike Java :)

The simple solution (O(n) average, O(n^2) worst case) to this problem is copy the source to a nice simple implementation of qsort and make it only recurse on the side that contains the position you care about. It should be about 5 lines of code different so it should be easy to do.

If i is small there is a O(n + log(n)*i*log(i)) solution):

int FindI(int[] array, int i)
{
    int[] tmp = array[0..i].dup; // copy first i elements;

    sort(tmp);                   // sort, low to high

    foreach(j in array[i..$])    // loop over the rest
       if(j < tmp[0])
       {
          tmp[0] = j;
          sort(tmp);
       }
    return tmp[0];
}
BCS
+3  A: 

Assuming this isn't homework where you need do it this way, and it's not in the critical path (which is a likely guess), just sort the array and grab the value at index i.

public static getIthSmallest(final int[] myArray, final int i) {
    if (i < 0) {
        System.err.println("You're going to get an ArrayIndexOutOfBoundsException.");
    }
    if (i >= myArray.length) {
        System.err.println("You're going to get an ArrayIndexOutOfBoundsException.");
    }
    Arrays.sort(myArray);
    return myArray[i];
}
Hank Gay
+1; Premature optimization yada yada yada. Until you know it doesn't work, sorting the whole thing is the fool proof way to go. And if you then find you also need the next largest as well...
BCS
This defeats the entire purpose: davit is trying to learn how to implement this algorithm, not just how to solve a problem for work/something else.
polygenelubricants
Based on the copy-paste nature of the code (in this question and others), I'm not sure davit is trying to learn anything. Especially given the way this question has/had the exact same problems as a previous question (poor formatting, bad variable names, not including a line number, not Googling for what ArrayIndexOutOfBoundsException is, etc.) I strongly suspect that they are unmarked homework questions. If that is the case, I don't want to fix the code. If this is actually a case where davit just needs to get a problem solved, I provided a way to do that.
Hank Gay
A: 

You can use public static T min(Collection coll, Comparator comp) in Collections.

Istao
How would you use that to find the 100,000th element in a 1M element collection?
BCS
Collections.min(my1Mcollection, myComparator); I suppose.
Istao
This doesn't answer the question - he's looking to find the second, or third, or *i-th* smallest value, not the smallest.
BlueRaja - Danny Pflughoeft
mmmm... it's my poor english, sorry. I've understanded "the min element in 1M".But I see my answer marked OK, so I understand nothing, nothing, nothing.Cheers, and sorry.
Istao
+5  A: 

I was able to fix a few bugs. A minor one is this line:

  return Rand_partition(a,r-1,q,i-k);
                           ^

Instead, you want this:

  return Rand_partition(a,r+1,q,i-k);
                           ^

That's because you have partitioned a[p..q] into three parts as follows:

  a[p..r-1], a[r], a[r+1..q]

Your original code handles the a[r] and a[p..r-1] case fine, but messes up on the a[r+1..q] by using r-1 instead.


I was also able to correct and simplify partition:

public static  int partition(int a[],int p,int q){
    int  m=a[p]; // not m[0], you want to partition m[p..q]!!!
    while ( p<q){
        while (p<q && a[p] <m){ // don't do p++ here!
            p++;
        }
        while (q>p && a[q]>m){ // don't do q-- here!
            q--;
        }
        int t=a[p];
        a[p]=a[q];
        a[q]=t;
    }
    return p; // no need to search!!!
}

Your original code had extraneous p++ and q--. Also, the search for where the pivot is is unnecessary. It's where p and q meet.


On naming conventions

Please follow Java naming conventions:

Class names should be nouns, in mixed case with the first letter of each internal word capitalized. Methods should be verbs, in mixed case with the first letter lowercase, with the first letter of each internal word capitalized.

Related questions


On array declarations

Also, do not make a habit of declaring arrays like this:

int x[];

You should instead put the brackets with the type, rather than with the identifier:

int[] x;

Related questions

polygenelubricants
+1  A: 

The algorithm you're attempting to implement is called Quickselect. Here is a link to working code using a median-of-three partitioning strategy.

Adamski