diff extra/NaN/src/kth_element.cpp @ 7232:7df85b226263 octave-forge

add kth_element and improve median (performance gain)
author schloegl
date Tue, 08 Jun 2010 20:58:47 +0000
parents
children f8f0a8e59cc4
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/extra/NaN/src/kth_element.cpp	Tue Jun 08 20:58:47 2010 +0000
@@ -0,0 +1,146 @@
+//-------------------------------------------------------------------
+//   C-MEX implementation of kth element - this function is part of the NaN-toolbox. 
+//
+//   usage: x = kth_element(X,k)
+//          returns sort(X)(k)
+//
+//   
+//
+//   This program is free software; you can redistribute it and/or modify
+//   it under the terms of the GNU General Public License as published by
+//   the Free Software Foundation; either version 3 of the License, or
+//   (at your option) any later version.
+//
+//   This program is distributed in the hope that it will be useful,
+//   but WITHOUT ANY WARRANTY; without even the implied warranty of
+//   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//   GNU General Public License for more details.
+//
+//   You should have received a copy of the GNU General Public License
+//   along with this program; if not, see <http://www.gnu.org/licenses/>.
+//
+//
+// Input:
+// - X  data vector, must be double/real 
+//        data might be reorded (partially sorted) in place, and NaN's are removed. 
+// - k  which element should be selected
+//
+// Output:
+//      x = sort(X)(k)  
+//
+//    $Id$
+//    Copyright (C) 2010 Alois Schloegl <a.schloegl@ieee.org>
+//    This function is part of the NaN-toolbox
+//    http://biosig-consulting.com/matlab/NaN/
+//
+//-------------------------------------------------------------------
+
+
+#include <inttypes.h>
+#include <math.h>
+#include <stdlib.h>
+#include <string.h>
+#include "mex.h"
+
+
+#ifdef tmwtypes_h
+  #if (MX_API_VER<=0x07020000)
+    typedef int mwSize;
+  #endif 
+#endif 
+
+
+/*
+   http://en.wikipedia.org/wiki/Selection_algorithm
+ */ 
+static size_t partition(double array[], size_t left, size_t right, size_t pivotIndex)
+{
+        double temp;
+        double pivotValue = array[pivotIndex];
+        array[pivotIndex] = array[right];
+        array[right] = pivotValue;
+        size_t storeIndex = left;
+        for (size_t i = left; i <= right - 1; ++i ) {
+                if (array[i] <= pivotValue) {
+                        temp = array[i];
+                        array[i] = array[storeIndex];
+                        array[storeIndex] = temp;
+                        ++storeIndex;
+                }
+        }
+        temp = array[storeIndex];
+        array[storeIndex] = array[right];
+        array[right] = temp;
+        return storeIndex;
+}
+
+ 
+static void findFirstK(double array[], size_t left, size_t right, size_t k)
+{
+        size_t pivotNewIndex = 0;
+        if (right > left) {
+                size_t pivotIndex = (left + right) / 2;
+                pivotNewIndex = partition(array, left, right, pivotIndex);
+                if (pivotNewIndex > k)
+                        findFirstK(array, left, pivotNewIndex - 1, k);
+                else if (pivotNewIndex < k)
+                        findFirstK(array, pivotNewIndex + 1, right, k);
+        }
+}
+ 
+
+void mexFunction(int POutputCount,  mxArray* POutput[], int PInputCount, const mxArray *PInputs[]) 
+{
+    	mwIndex j, k, n;	// running indices 
+    	mwSize  szK, szX; 
+    	double 	*Y,*X,*K; 
+
+	// check for proper number of input and output arguments
+	if (PInputCount != 2) {
+		mexPrintf("KTH_ELEMENT returns the K-th smallest element of vector X\n\n");
+		mexPrintf("usage:\tx = kth_element(X,k)\n");
+		mexPrintf("\nNote, the elements in X are modified in place. Do not use kth_element directely unless you know what you do. You are warned.\n");
+		
+	    	mexPrintf("\nsee also: median, quantile\n\n");
+	        mexErrMsgTxt("KTH_ELEMENT requires 2 input arguments\n");
+	}        
+	if (POutputCount > 2)
+	        mexErrMsgTxt("KTH_ELEMENT has 1 output arguments.");
+
+	// get 1st argument
+	if (mxIsComplex(PInputs[0]))
+		mexErrMsgTxt("complex argument not supported (yet). ");
+	if (!mxIsDouble(PInputs[0]) || !mxIsDouble(PInputs[1]))
+		mexErrMsgTxt("input arguments must be of type double . ");
+	// TODO: support of complex, and integer data	
+		
+
+	szK = mxGetNumberOfElements(PInputs[1]);
+	K = (double*)mxGetData(PInputs[1]);
+
+	szX = mxGetNumberOfElements(PInputs[0]);
+	X = (double*)mxGetData(PInputs[0]);
+
+	for (j=0, k=0; k<szX; k++) {
+		if (j<k) X[j] = X[k];
+		if (!mxIsNaN(X[k])) j++;
+	}
+	for ( k=j; k<szX; k++) X[k] = 0.0/0.0; // needed when X contains NaN's and kth_element is called several times on the same data.  
+
+	/*********** create output arguments *****************/
+
+	POutput[0] = mxCreateDoubleMatrix(mxGetM(PInputs[1]),mxGetN(PInputs[1]),mxREAL);
+	Y = (double*) mxGetData(POutput[0]);
+	for (k=0; k < szK; k++) {
+		n = K[k]-1;       // convert to zero-based indexing 
+		if (n >= j || n < 0)
+			Y[k] = 0.0/0.0;
+		else {
+        		findFirstK(X, 0, j-1, n);
+        		Y[k] = X[n];
+		}	
+	}
+
+	return; 
+}
+