private static void quickPartition(float[] w, float[] x, int[] m) { int p = m[0]; int q = m[1]; int k = med3(x,p,(p+q)/2,q); float y = x[k]; int a=p,b=p; while (b<=c && x[b]<=y) { if (x[b]==y) swap(w,x,a++,b); ++b; swap(w,x,c,d--); --c; swap(w,x,b,c); ++b; --c; int s = Math.min(d-c,q-d); int t = q+1; swap(w,x,p,b-r,r); swap(w,x,b,t-s,s); m[0] = p+(b-a); // p --- m[0]-1 | m[0] --- m[1] | m[1]+1 --- q m[1] = q-(d-c); // x<y x=y x>y
_m[0] = p; _m[1] = q; quickPartition(w,x,_m); // partition into left, middle, right int pp = _m[0]; int qq = _m[1];
public void testUnweighted() { Random r = new Random(); int ntest = 100; for (int itest=0; itest<ntest; ++itest) { int n = 1+(r.nextBoolean()?r.nextInt(100):r.nextInt(10)); float[] f = randfloat(n); float[] w = fillfloat(1.0f,n); if (r.nextBoolean()) { for (int i=n/4; i<3*n/4; ++i) f[i] = f[0]; } MedianFinder mf = new MedianFinder(n); float ew = mf.findMedian(w,f); float eu = mf.findMedian(f); assertTrue(ew==eu); } }
/** * Returns the weighted median of the specified array of values. * @param w array of positive weights. * @param x array of values. * @return the weighted median. */ public float findMedian(float[] w, float[] x) { Check.argument(_n==w.length,"length of w is valid"); Check.argument(_n==x.length,"length of x is valid"); if (_w==null) _w = new float[_n]; copy(w,_w); copy(x,_x); if (_n<16) { return findMedianSmallN(_w,_x); } else { return findMedianLargeN(_w,_x); } }
private static void insertionSort(float[] w, float[] x, int p, int q) { for (int i=p+1; i<=q; ++i) for (int j=i; j>p && x[j-1]>x[j]; --j) swap(w,x,j,j-1); }
public void testWeighted() { Random r = new Random(); int ntest = 100; for (int itest=0; itest<ntest; ++itest) { int n = 1+(r.nextBoolean()?r.nextInt(100):r.nextInt(10)); float[] w = randfloat(n); float[] f = randfloat(n); // Weighted median using a slow complete sort. int[] k = rampint(0,1,n); quickIndexSort(f,k); float wsum = 0.0f; float wtotal = sum(w); int i; for (i=0; i<n && wsum<0.5f*wtotal; ++i) wsum += w[k[i]]; float qslow = f[k[i-1]]; // Weighted median using fast median finder. MedianFinder mf = new MedianFinder(n); float qfast = mf.findMedian(w,f); assertTrue(qslow==qfast); } }
private float findMedianSmallN(float[] w, float[] x) { // Insertion sort is quick for small n. for (int i=1; i<_n; ++i) for (int j=i; j>0 && x[j-1]>x[j]; --j) swap(w,x,j,j-1); // Half the sum of all weights = wh. For the median x, // we require wl(x) <= wh and wr(x) <= wh. float ws = 0.0f; for (int i=0; i<_n; ++i) ws += w[i]; float wh = 0.5f*ws; // Index one above upper bound for left sum of weights. int kl = 0; float wl = w[kl]; while (wl<wh) wl += w[++kl]; // Index one below lower bound for right sum of weights. int kr = _n-1; float wr = w[kr]; while (wr<wh) wr += w[--kr]; // The weighted median. if (kl==kr) return x[kl]; else return 0.5f*(x[kl]+x[kr]); }
for (int n=10; n<10000; n*=10) { System.out.println("n="+n); MedianFinder mf = new MedianFinder(n); int nq,rate; for (int ntrial=0; ntrial<3; ++ntrial) { sw.restart(); for (nq=0; sw.time()<maxtime; ++nq) q1 = mf.findMedian(w,f); sw.stop(); rate = (int)(1.0e-6*nq*n/sw.time()); sw.restart(); for (nq=0; sw.time()<maxtime; ++nq) q2 = mf.findMedian(f); sw.stop(); rate = (int)(1.0e-6*nq*n/sw.time());