// Copright (C) 1999-2006, Bernd Gaertner // $Revision: 1.3 $ // $Date: 2006/11/16 08:01:52 $ // // 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 2 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, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA, // or download the License terms from prep.ai.mit.edu/pub/gnu/COPYING-2.0. // // Contact: // -------- // Bernd Gaertner // Institute of Theoretical Computer Science // ETH Zuerich // CAB G32.2 // CH-8092 Zuerich, Switzerland // http://www.inf.ethz.ch/personal/gaertner #include #include #include #include #include // Functions // ========= inline double mb_sqr (double r) {return r*r;} // Class Declarations // ================== // smallest enclosing ball of a set of n points in dimension d template class Miniball; // smallest ball with a set of n <= d+1 points *on the boundary* template class Miniball_b; // point in dimension d template class Point; // Class Definitions // ================= // Miniball // -------- template class Miniball { public: // types typedef typename std::list >::iterator It; typedef typename std::list >::const_iterator Cit; private: // data members std::list > L; // internal point set Miniball_b B; // the current ball It support_end; // past-the-end iterator of support set // private methods void mtf_mb (It k); void pivot_mb (It k); void move_to_front (It j); double max_excess (It t, It i, It& pivot) const; public: // creates an empty ball Miniball() {} // copies p to the internal point set void check_in (const Point& p); // builds the smallest enclosing ball of the internal point set void build (); // returns center of the ball (undefined if ball is empty) Point center() const; // returns squared_radius of the ball (-1 if ball is empty) double squared_radius () const; // returns size of internal point set int nr_points () const; // returns begin- and past-the-end iterators for internal point set Cit points_begin () const; Cit points_end () const; // returns size of support point set; this set has the following properties: // - there are at most d+1 support points, // - all support points are on the boundary of the computed ball, and // - the smallest enclosing ball of the support point set equals the // smallest enclosing ball of the internal point set int nr_support_points () const; // returns begin- and past-the-end iterators for internal point set Cit support_points_begin () const; Cit support_points_end () const; // assesses the quality of the computed ball. The return value is the // maximum squared distance of any support point or point outside the // ball to the boundary of the ball, divided by the squared radius of // the ball. If everything went fine, this will be less than e-15 and // says that the computed ball approximately contains all the internal // points and has all the support points on the boundary. // // The slack parameter that is set by the method says something about // whether the computed ball is really the *smallest* enclosing ball // of the support points; if everything went fine, this value will be 0; // a positive value may indicate that the ball is not smallest possible, // with the deviation from optimality growing with the slack double accuracy (double& slack) const; // returns true if the accuracy is below the given tolerance and the // slack is 0 bool is_valid (double tolerance = 1e-15) const; }; // Miniball_b // ---------- template class Miniball_b { private: // data members int m, s; // size and number of support points double q0[d]; double z[d+1]; double f[d+1]; double v[d+1][d]; double a[d+1][d]; double c[d+1][d]; double sqr_r[d+1]; double* current_c; // refers to some c[j] double current_sqr_r; public: Miniball_b() {reset();} // access const double* center() const; double squared_radius() const; int size() const; int support_size() const; double excess (const Point& p) const; // modification void reset(); // generates empty sphere with m=s=0 bool push (const Point& p); void pop (); // checking double slack() const; }; // Point (inline) // ------------- template class Point { private: double coord [d]; public: // default Point() {} // copy from Point Point (const Point& p) { for (int i=0; i void Miniball::check_in (const Point& p) { L.push_back(p); } template void Miniball::build () { B.reset(); support_end = L.begin(); pivot_mb (L.end()); } template void Miniball::mtf_mb (It i) { support_end = L.begin(); if ((B.size())==d+1) return; for (It k=L.begin(); k!=i;) { It j=k++; if (B.excess(*j) > 0) { if (B.push(*j)) { mtf_mb (j); B.pop(); move_to_front(j); } } } } template void Miniball::move_to_front (It j) { if (support_end == j) support_end++; L.splice (L.begin(), L, j); } template void Miniball::pivot_mb (It i) { It t = ++L.begin(); mtf_mb (t); double max_e, old_sqr_r = -1; do { It pivot; max_e = max_excess (t, i, pivot); if (max_e > 0) { t = support_end; if (t==pivot) ++t; old_sqr_r = B.squared_radius(); B.push (*pivot); mtf_mb (support_end); B.pop(); move_to_front (pivot); } } while ((max_e > 0) && (B.squared_radius() > old_sqr_r)); } template double Miniball::max_excess (It t, It i, It& pivot) const { const double *c = B.center(), sqr_r = B.squared_radius(); double e, max_e = 0; for (It k=t; k!=i; ++k) { const double *p = (*k).begin(); e = -sqr_r; for (int j=0; j max_e) { max_e = e; pivot = k; } } return max_e; } template Point Miniball::center () const { return Point(B.center()); } template double Miniball::squared_radius () const { return B.squared_radius(); } template int Miniball::nr_points () const { return L.size(); } template typename Miniball::Cit Miniball::points_begin () const { return L.begin(); } template typename Miniball::Cit Miniball::points_end () const { return L.end(); } template int Miniball::nr_support_points () const { return B.support_size(); } template typename Miniball::Cit Miniball::support_points_begin () const { return L.begin(); } template typename Miniball::Cit Miniball::support_points_end () const { return support_end; } template double Miniball::accuracy (double& slack) const { double e, max_e = 0; int n_supp=0; Cit i; for (i=L.begin(); i!=support_end; ++i,++n_supp) if ((e = std::abs (B.excess (*i))) > max_e) max_e = e; // you've found a non-numerical problem if the following ever fails assert (n_supp == nr_support_points()); for (i=support_end; i!=L.end(); ++i) if ((e = B.excess (*i)) > max_e) max_e = e; slack = B.slack(); return (max_e/squared_radius()); } template bool Miniball::is_valid (double tolerance) const { double slack; return ( (accuracy (slack) < tolerance) && (slack == 0) ); } // Miniball_b // ---------- template const double* Miniball_b::center () const { return current_c; } template double Miniball_b::squared_radius() const { return current_sqr_r; } template int Miniball_b::size() const { return m; } template int Miniball_b::support_size() const { return s; } template double Miniball_b::excess (const Point& p) const { double e = -current_sqr_r; for (int k=0; k void Miniball_b::reset () { m = s = 0; // we misuse c[0] for the center of the empty sphere for (int j=0; j void Miniball_b::pop () { --m; } template bool Miniball_b::push (const Point& p) { int i, j; double eps = 1e-32; if (m==0) { for (i=0; i double Miniball_b::slack () const { double l[d+1], min_l=0; l[0] = 1; for (int i=s-1; i>0; --i) { l[i] = f[i]; for (int k=s-1; k>i; --k) l[i]-=a[k][i]*l[k]; if (l[i] < min_l) min_l = l[i]; l[0] -= l[i]; } if (l[0] < min_l) min_l = l[0]; return ( (min_l < 0) ? -min_l : 0); } // Point // ----- // Output template std::ostream& operator << (std::ostream& os, const Point& p) { os << "("; for (int i=0; i