int medianOfMedians(int A[], int p, int q, int k, int *pos) { if(p == q) { *pos = p; return A[p]; } int i, m, n, B[(q-p+1)/5+1], small, index, b = 0, j; for(i = p; i <= q; i+=5) { for(m = p; m < p+5 && m <=q; m++) { small = A[m]; index = m; for(n = m+1; n < p+5 && n <= q; n++) { if(small > A[n]) { small = A[n]; index = n; } } if(m != index) { A[index] = A[m]; A[m] = small; } } if(i+5 > q) { B[b] = A[(i+q)/2]; } else { B[b] = A[i+2]; } b++; } int pivot = medianOfMedians(B, 0, b-1, (b-1)/2, pos); int rank = partition(A, p, q, pivot); *pos = p+rank; if(k == rank) return pivot; else if(k < rank) return medianOfMedians(A, p, p+rank-1, k, pos); else return medianOfMedians(A, p+rank+1, q, (k-rank-1), pos); }
/** Linear worst-case time algorithm to find median in ar[left,right]. The * comparison function, cmp, is needed to compare elements. */ int selectMedian (void **ar, int(*cmp)(const void *,const void *), int left, int right) { int k = (right-left+1)/2; while (k > 0) { /* Make guess */ int idx = medianOfMedians (ar, cmp, left, right, 1); /** * Partition input array around the median of medians x. If kth * largest is found, return absolute index; otherwise narrow to * find kth smallest in A[left,pivotIndex-1] or (k-p)-th * in A[pivotIndex+1,right]. * */ int pivotIndex = partition (ar, cmp, left, right, idx); /* Note that k is in range 0 <=k <= right-left while the returned pivotIndex is in range left <= pivotIndex <= right. */ int p = left+k; if (p == pivotIndex) { return pivotIndex; } else if (p < pivotIndex) { right = pivotIndex-1; } else { k = k - (pivotIndex-left+1); left = pivotIndex+1; } } /* If we get here, then left=right, so just return one as median. */ return left; }
/** * Find suitable pivotIndex to use for ar[left,right] with closed bound * on both sides. * * 1. Divide the elements into floor(n/size) groups of size elements and * use _insertion on each group * 2. Pick median from each sorted groups (size/2 element). * 3. Use select recursively to find median x of floor(n/size) medians * found in step 2. This is known as the median-of-medians. */ static int medianOfMedians (void **ar, int(*cmp)(const void *,const void *), int left, int right, int gap) { int s, num; int span = groupingSize*gap; /* less than five? Insertion sort and return median. */ num = (right - left + 1) / span; if (num == 0) { _insertion (ar, cmp, left, right, gap); num = (right - left + 1)/gap; return left + gap*(num-1)/2; } /* set up all median values of groups of groupingSize elements */ for (s = left; s+span < right; s += span) { _insertion (ar, cmp, s, s + span-1, gap); } /* Recursively apply to subarray [left, s-1] with increased gap * if more than 'groupingSize' groupings remain. */ if (num < groupingSize) { /* find median of this reduced set. BASE CASE */ _insertion (ar, cmp, left+span/2, right, span); return left + num*span/2; } else { return medianOfMedians (ar, cmp, left+span/2, s-1, span); } }
static int medianOfMedians(double** a, int l, int r) { int i; int medianInd; int M=(r-l+1)/5; /* M = # 5-groups */ /* Note that M might be increased by one below */ if (M<=1){ /* recursion exit criterion */ insertionSort(a,l,r); medianInd=l+(r-l)/2; } else{ int medOfFive; for (i=0;i<M;i++){ medOfFive=medianOfFive(a,l+i*5); swap(a,l+i,medOfFive); /* move in the beginning */ } /* the last group (size < 5) */ int sizeLast=(r-l)+1-M*5; if (sizeLast>2){ insertionSort(a,l+M*5,r); swap(a,l+M,l+M*5+sizeLast/2); M++; } medianInd = medianOfMedians(a,l,l+M-1); } return medianInd; }
// Median of medians algorithm: find k th smallest number in the sub-array[l..r] int medianOfMedians(int *arr, const int &l, const int &r, const int &k) { int n = r - l + 1; // length of the sub array // if k is out of range, throw exception if (k <= 0 || k > n) throw std::invalid_argument("invalid k-th"); // divide sub-arrary[l..r] in groups of size GROUP_NUM (the last group length can be less than GROUP_NUM) // calculate median of each groups and store them in median array int const medianNum = static_cast<int>(ceil(static_cast<double>(n) / static_cast<double>(GROUP_NUM))); //int const medianNum = (n + 4) / GROUP_NUM; int median[medianNum]; int median_i; for (median_i = 0; median_i < n / GROUP_NUM; ++median_i) median[median_i] = findMedian(arr + l + median_i * GROUP_NUM, GROUP_NUM); if (median_i * GROUP_NUM < n) { // last group with elements less than GROUP_NUM median[median_i] = findMedian(arr + l + median_i * GROUP_NUM, n % GROUP_NUM); } // Find median of all medians recursively int medOfMedians; if (medianNum == 1) // first median is the median of medians since there's only one median medOfMedians = median[0]; else { // we don't need to consider odd and even array length cases, // since this median is used for partition medOfMedians = medianOfMedians(median, 0, medianNum - 1, medianNum / 2); } // get the medOfMedians's index int pivot_i; for (pivot_i = l; pivot_i < r; ++pivot_i) { if (arr[pivot_i] == medOfMedians) break; } // partition the array around median of medians int pos = partition(arr, l, r, pivot_i); if (pos - l + 1 == k) // if the pos is the kth number, return it return arr[pos]; if (pos - l + 1 > k) // if position is more than kth, find kth within left sub-array return medianOfMedians(arr, l, pos - 1, k); else // if position is less than kth, find kth within right sub-array return medianOfMedians(arr, pos + 1, r, k - pos + l - 1); }
int selectIdx(ll *nums, int left, int right, int k) { int pivotIdx; if(left == right) return k; pivotIdx = medianOfMedians(nums, left, right); pivotIdx = partition(nums, left, right, pivotIdx); if(pivotIdx == left || k == pivotIdx) return k; else if(k < pivotIdx) return selectIdx(nums, left, pivotIdx - 1, k); else return selectIdx(nums, pivotIdx + 1, right, k); }
/* changes order of elements in x and in w (weights)! */ static void median(double **xadd, int left, int right, int* res) { int i; int N = right - left + 1; int M = left + N/2; if (N % 2 == 0) {M --;} /* lower median */ /* int M = left + N/2; */ /* upper median */ int l = left; int r = right; int* momInds = new int[2]; int momInd, momIndL, momIndR; while (1>0){ momInd = medianOfMedians(xadd,l,r); partition(xadd,l,momInd,r,momInds); momIndL = momInds[0]; momIndR = momInds[1]; if (momIndL>M) {r=momIndL-1;} if (momIndR<M) {l=momIndR+1;} if ((momIndL<=M)&&(momIndR>=M)) {break;} } delete[] momInds; res[0]=momIndL; res[1]=momIndR; }
int process(int A[], int B[], int n, int k) { int pos1, pos2, k1, k2, min, max, a, b, i; if(k == 1) { k1 = medianOfMedians(A, 0, n-1, 0, &pos1); k2 = medianOfMedians(A, 0, n-1, 0, &pos2); if(k1 < k2) return k1; else return k2; } k1 = medianOfMedians(A, 0, n-1, k/2-1, &pos1); getchar(); if(k%2 == 0) k2 = medianOfMedians(B, 0, n-1, k/2-1, &pos2); else k2 = medianOfMedians(B, 0, n-1, k/2, &pos2); if(k1 > k2) { a = pos1-1; b = n-1; if(b < (pos2+1)) { return k1; } buildHeap(A, 0, (pos1-1), 0); buildHeap(B, (pos2+1), n-1, 1); while(k1 > k2) { if(b == pos2+1) { if(k1 > B[b]) { if(a >= 0) { max = extract(A, 0, &a, 0); if(B[b] < max) return max; else return B[b]; } else B[b]; } else return k1; } if(a < 0) { min = extract(B, (pos2+1), &b, 1); if(min < k1) return min; else return k1; } max = extract(A, 0, &a, 0); min = extract(B, (pos2+1), &b, 1); if(min < k1) { k1 = max; k2 = min; } else return k1; } return k2; } if(k1 < k2) { a = n-1; b = pos2-1; if(a < (pos1+1)) { return k2; } buildHeap(A, pos1+1, n-1, 1); buildHeap(B, 0, pos2-1, 0); while(k1 < k2) { if(a == pos1+1) { if(k2 > A[a]) { if(b >= 0) { max = extract(B, 0, &b, 0); if(A[a] < max) return max; else return A[a]; } else A[a]; } else return k2; } if(b < 0) { min = extract(A, (pos1+1), &a, 1); if(min < k2) return min; else return k2; } max = extract(B, 0, &b, 0); min = extract(A, (pos1+1), &a, 1); if(min < k2) { k2 = max; k1 = min; } else return k2; } return k1; } }