C++程序  |  307行  |  8.8 KB

class Vector {
   std::vector<double> k;
   int N;
public:
   explicit Vector(int size): k(size) {     
      N = size;
      for (int i = 0; i < N; i++)
         k[i] = 0.0;
   }
  
   inline int GetSize() const { return N; }
  
   inline double& operator[] (int ix) {
      CHECK(ix < N && ix >= 0);
      return k[ix];
   }
  
   inline const double& operator[] (int ix) const {
      CHECK(ix < N && ix >= 0);
      return k[ix];
   }
  
   inline Vector operator+ (const Vector & other) const {
      CHECK(other.GetSize() == N);
      Vector ret(N);
      for (int i = 0; i < N; i++)
         ret[i] = k[i] + other[i];
      return ret;
   }
  
   inline Vector operator- (const Vector & other) const {
      CHECK(other.GetSize() == N);
      Vector ret(N);
      for (int i = 0; i < N; i++)
         ret[i] = k[i] - other[i];
      return ret;
   }

   inline Vector operator* (double factor) const {
      Vector ret(N);
      for (int i = 0; i < N; i++)
         ret[i] = k[i] * factor;
      return ret;
   }
  
   inline double DotProduct (const Vector & other) const {
      CHECK(other.GetSize() == N);
      double ret = 0.0;
      for (int i = 0; i < N; i++)
         ret += k[i] * other[i];
      return ret;
   }
  
   inline double Len2 () const {
      double ret = 0.0;
      for (int i = 0; i < N; i++)
         ret += k[i] * k[i];
      return ret;     
   }
  
   inline double Len () const { return sqrt(Len2()); }
  
   string ToString () const {
      char temp[1024] = "(";
      for (int i = 0; i < N; i++)
         sprintf(temp, "%s%s%.1lf", temp, i == 0 ? "" : ", ", k[i]);
      return (std::string)temp + ")";
   }
  
   inline void Copy(const Vector & from) {
      CHECK(from.GetSize() == N);
      for (int i = 0; i < N; i++)
         k[i] = from[i];
   }
};

class Matrix {
   int M;
   std::vector<double*> data;
public:
   /*
    * This matrix can grow its "N" i.e. width
    * See http://en.wikipedia.org/wiki/Matrix_(mathematics)
    */
  
   Matrix (int M_, int N) {
      M = M_;
      for (int i = 0; i < N; i++) {
         IncN();
      }
   }
  
   ~Matrix() {
      for (int i = 0; i < data.size(); i++)
         delete [] data[i];
   }

   inline int GetM() const { return M; }
  
   inline int GetN() const { return data.size(); }
   inline void IncN() {
      double * k = new double[M];
      for (int i = 0; i < M; i++)
         k[i] = 0.0;
      data.push_back(k);
   }
  
  
   inline double& At(int i, int j) {
      CHECK(i < M && i >= 0);
      CHECK(j < data.size() && j >= 0);
      // Note the reverse order of indices!
      return data[j][i];
   }
  
   inline const double& At(int i, int j) const {
      CHECK(i < M && i >= 0);
      CHECK(j < data.size() && j >= 0);
      // Note the reverse order of indices!
      return data[j][i];
   }
  
   Vector MultiplyRight (const Vector & v) const {
      int N = data.size();
      CHECK(v.GetSize() == N);
      Vector ret(M);
      for (int i = 0; i < M; i++)
         for (int j = 0; j < N; j++)
            ret[i] += v[j] * At(i,j);
      return ret;
   }
  
   Vector MultiplyLeft (const Vector & v_to_transpose) const {
      int N = data.size();
      CHECK(v_to_transpose.GetSize() == M);
      Vector ret(N);
      for (int i = 0; i < M; i++)
         for (int j = 0; j < N; j++)
            ret[j] += v_to_transpose[i] * At(i,j);
      return ret;     
   }
  
   string ToString() const {
      string ret = "";
      for (int i = 0; i < M; i++) {
         ret += "[";
         for (int j = 0; j < GetN(); j++) {
            char temp[128] = "";
            sprintf(temp, "%s%.1lf", j == 0 ? "" : ", ", At(i,j));
            ret += temp;
         }
         ret += "]\n";
      }
      return ret;
   }
};

Vector EstimateParameters(const Matrix & perf_m, const Vector & stats_v, double rel_diff, int * iter_count = NULL)
{
   /*
    *  Goal: find Vector<N> parameters:
    *   (perf_m * parameters - stats_v)^2 -> min
    * With the following limitations:
    *    parameters[i] >= 0
    * "Stop rule":
    *    for every i=0...M
    *    -> |stats_v[i] - (perf_m * parameters)[i]| < rel_diff * stats_v[i]
    * Algorithm used is a gradient descend
    */
   int N = perf_m.GetN(), M = perf_m.GetM();
   CHECK(stats_v.GetSize() == M);
   Vector current(N);

   // First, let's find out those lines in matrix having only one non-zero coefficient
   // and if we have any, decrease the dimension of our matrix
   {
      int count_easy_param = 0;
      std::vector<int> parameters_set(N); // N (line#) -> M (row#) of the only nonzero element; -1 if 0/2+
      for (int n = 0; n < N; n++) {
         parameters_set[n] = -1;
      }
      // we may detect & assert unresolvable conflicts like the following
      // |1|       |1|   
      // |1| * p = |2|
      // |1|       |3|

      // Find out those 0000*0000 lines
      for (int m = 0; m < M; m++) {
         int zero_id = -1; // -1 = not found, -2 = found twice, else - idx
         for (int n = 0; n < N; n++) {
            const double & m_n = perf_m.At(m,n);
            if (m_n != 0.0) {
               if (zero_id == -1) {
                  zero_id = n;
               } else if (zero_id >= 0) {
                  zero_id = -2;
                  break;
               }
            }
         }
         if (zero_id >= 0) {
            CHECK(parameters_set[zero_id] == -1);
            parameters_set[zero_id] = m;
            count_easy_param++;
            current[zero_id] = stats_v[m] / perf_m.At(m, zero_id);
         }
      }

      if (count_easy_param > 0) {
         //printf("tmp = %s\n", current.ToString().c_str());
 
         //printf("\n\nDecreasing the dimensions!\n");
         std::vector<int> new_m_to_old(M - count_easy_param),
                          new_n_to_old(N - count_easy_param);
         for (int m = 0; m < M - count_easy_param; m++) {
            // see increments later
            new_m_to_old[m] = m;
         }
         int cur_n = 0;
         for (int n = 0; n < N; n++) {
            if (parameters_set[n] == -1) {
               new_n_to_old[cur_n] = n;
               cur_n++;
            } else {
               for (int m = parameters_set[n]; m < M - count_easy_param; m++) {
                  new_m_to_old[m]++;
               }
            }
         }

         Vector auto_stats = perf_m.MultiplyRight(current), // we have these stats for sure ...
                diff_stats = stats_v - auto_stats; // ... and we need to get these stats more
     
         // Formulate the sub-problem (sub-matrix & sub-stats)
         Matrix new_m(M - count_easy_param, N - count_easy_param);
         Vector new_stats(M - count_easy_param);
         for (int m = 0; m < M - count_easy_param; m++) {
            new_stats[m] = diff_stats[new_m_to_old[m]];
            CHECK(new_stats[m] >= 0.0);
            for (int n = 0; n < N - count_easy_param; n++)
               new_m.At(m,n) = perf_m.At(new_m_to_old[m], new_n_to_old[n]);
         }
        
         // Solve the submatrix and put the results back
         Vector new_param = EstimateParameters(new_m, new_stats, rel_diff, iter_count);
         for (int n = 0; n < N - count_easy_param; n++) {
            current[new_n_to_old[n]] = new_param[n];
         }
         return current;
      }
   }

   bool stop_condition = false;
   double prev_distance = stats_v.Len2();

   //printf("Initial distance = %.1lf\n", prev_distance);
   while (stop_condition == false) {
      (*iter_count)++;
      Vector m_by_curr(M);
      m_by_curr.Copy(perf_m.MultiplyRight(current));
      Vector derivative(N); // this is actually "-derivative/2" :-)
      derivative.Copy(perf_m.MultiplyLeft(stats_v - m_by_curr));

      if (derivative.Len() > 1000.0) {
         derivative = derivative * (1000.0 / derivative.Len());
      }

      // Descend according to the gradient
      {
         Vector new_curr(N);
         double step = 1024.0;
         double new_distance;
         for (int i = 0; i < N; i++) {
            if (current[i] + derivative[i] * step < 0.0) {
               step = - current[i] / derivative[i];
            }
         }
         do {
            new_curr = current + derivative*step;
            step /= 2.0;
            new_distance = (perf_m.MultiplyRight(new_curr) - stats_v).Len2();
            if (step < 0.00001) {
               stop_condition = true;
            }
            (*iter_count)++;
         } while (new_distance >= prev_distance && !stop_condition);
        
         prev_distance = new_distance;
         current = new_curr;
         //printf ("Dist=%.1lf, cur=%s\n", new_distance, current.ToString().c_str());
      }
     
      // Check stop condition
      if (stop_condition == false)
      {
         stop_condition = true;
         Vector stats_est(M);
         stats_est.Copy(perf_m.MultiplyRight(current));
         for (int i = 0; i < M; i++)
            if (fabs(stats_v[i] - stats_est[i]) > rel_diff * stats_v[i])
               stop_condition = false;
      }
   }
  
   return current;
}