gKitGL
|
00001 00002 #ifndef _GK_CDF_H 00003 #define _GK_CDF_H 00004 00005 #include <cmath> 00006 #include <vector> 00007 #include <cassert> 00008 00009 #include "Sampler.h" 00010 00011 00012 namespace gk { 00013 00014 //! construction d'une fonction de repartition et choix aleatoire d'un element en fonction de la fonction de repartition. 00015 class Cdf 00016 { 00017 std::vector<float> m_cdf; 00018 float m_total; 00019 00020 public: 00021 //! constructeur, indique le nombre d'elements (optionnel). 00022 Cdf( const int reserve= 0 ) 00023 : 00024 m_cdf(), 00025 m_total(0.f) 00026 { 00027 if(reserve > 0) 00028 m_cdf.reserve(reserve); 00029 } 00030 00031 //! insere un element dans la fonction de repartition. 00032 void insert( const float v ) 00033 { 00034 assert( v != 0.f ); // la pdf doit etre strictement positive 00035 assert(!isnan(v)); 00036 assert(!isinf(v)); 00037 00038 // accumule les valeurs pour 'construire' la fonction de repartition. 00039 m_total= m_total + v; 00040 m_cdf.push_back(m_total); 00041 } 00042 00043 #if 1 00044 //! choisit un element, renvoie son indice et la probabilite de l'avoir choisi. 00045 //! utilise une recherche lineaire. 00046 float sampleSlow( int& id ) const 00047 { 00048 assert(m_cdf.empty() == false); 00049 00050 // choisit une valeur aleatoire entre 0 et 1 00051 const float r= drand48() * m_total; 00052 00053 // verifie le premier element 00054 id= 0; 00055 if(r < m_cdf[0]) 00056 return m_cdf[0] / m_total; 00057 00058 // recherche l'element i tel que cdf[i-1] < r*total < cdf[i] 00059 const int count= (int) m_cdf.size(); 00060 for(int i= 1; i < count; i++) 00061 { 00062 if(r < m_cdf[i]) 00063 { 00064 id= i; 00065 return (m_cdf[i] - m_cdf[i -1]) / m_total; 00066 } 00067 } 00068 00069 // renvoie le dernier element 00070 id= count -1; 00071 return (m_cdf[id] - m_cdf[id -1]) / m_total; 00072 } 00073 #endif 00074 00075 //! choisit un element, renvoie son indice et la probabilite de l'avoir choisi. 00076 //! utilise une recherche dichotomique dans l'ensemble de valeurs strictement croissant. 00077 float sample( const float u, int& id ) const 00078 { 00079 assert(m_cdf.empty() == false); 00080 00081 const float r= u * m_total; 00082 // recherche l'element i tel que cdf[i-1] < r*total < cdf[i] 00083 int p= 0; 00084 int q= (int) m_cdf.size(); 00085 while(p < q) 00086 { 00087 const int m= (p+q) / 2; 00088 if(m_cdf[m] < r) 00089 p= m+1; 00090 else 00091 q= m; 00092 } 00093 assert(p >= 0 && p < (int) m_cdf.size()); 00094 00095 // retrouve la valeur associee a l'element et renvoie la probabilite de l'avoir selectionne 00096 id= p; 00097 if(p > 0) 00098 return (m_cdf[p] - m_cdf[p -1]) / m_total; 00099 else 00100 return m_cdf[0] / m_total; 00101 } 00102 00103 float sample( Sampler& sampler, int& id ) const 00104 { 00105 // choisit une valeur aleatoire entre 0 et 1 00106 return sample(sampler.uniformFloat(), id); 00107 } 00108 00109 //! renvoie la probabilite d'avoir selectionne un element. 00110 float pdf( const int id ) const 00111 { 00112 assert(m_cdf.empty() == false); 00113 00114 if(id == 0) 00115 return m_cdf[0] / m_total; 00116 else 00117 return (m_cdf[id] - m_cdf[id -1]) / m_total; 00118 } 00119 00120 //! choisit un element uniformement, renvoie son indice et la probabilite de l'avoir choisi. 00121 float sampleUniform( Sampler& sampler, int &id ) const 00122 { 00123 assert(m_cdf.empty() == false); 00124 // choisit une valeur aleatoire entre 0 et n-1 00125 id= drand48() * ((int) m_cdf.size() -1); 00126 assert(id < (int) m_cdf.size()); 00127 // renvoie la probabilite d'avoir selectionne la valeur : 1 / n 00128 return 1.f / (float) m_cdf.size(); 00129 } 00130 00131 //! renvoie la probabilite d'avoir selection un element. 00132 float pdfUniform( const int id ) const 00133 { 00134 assert(m_cdf.empty() == false); 00135 return 1.f / (float) m_cdf.size(); 00136 } 00137 00138 //! renvoie le total de la fonction de repartition. 00139 float total( ) const 00140 { 00141 return m_total; 00142 } 00143 }; 00144 00145 } 00146 00147 #endif