Найти все точки сетки внутри круга, упорядоченные по норме
Как бы вы решили проблему нахождения точек (целочисленной) сетки внутри круга с центром в начале оси, с результатами, упорядоченными по норме, как в расстоянии от центра, в C++?
Я написал реализацию, которая работает (да, я знаю, что это крайне неэффективно, но для моей проблемы что-либо еще было бы излишним). Я чрезвычайно новичок в C++, поэтому моей самой большой проблемой было найти структуру данных, способную
- быть способным к сортировке;
- возможность сохранять массив в одном из его элементов,
а не реализация алгоритма. Мой код выглядит следующим образом. Спасибо всем заранее!
typedef std::pair<int, int[2]> norm_vec2d;
bool norm_vec2d_cmp (norm_vec2d a, norm_vec2d b)
{
bool bo;
bo = (a.first < b.first ? true: false);
return bo;
}
int energy_to_momenta_2D (int energy, std::list<norm_vec2d> *momenta)
{
int i, j, norm, n=0;
int energy_root = (int) std::sqrt(energy);
norm_vec2d temp;
for (i=-energy_root; i<=energy_root; i++)
{
for (j =-energy_root; j<=energy_root; j++)
{
norm = i*i + j*j;
if (norm <= energy)
{
temp.first = norm;
temp.second[0] = i;
temp.second[1] = j;
(*momenta).push_back (temp);
n++;
}
}
}
(*momenta).sort(norm_vec2d_cmp);
return n;
}
2 ответа
Как бы вы решили проблему нахождения точек (целочисленной) сетки внутри круга с центром в начале оси, с результатами, упорядоченными по норме, как в расстоянии от центра, в C++?
Я бы не использовал std::pair
держать очки. Я бы создал свой собственный более описательный тип.
struct Point {
int x;
int y;
int square() const { return x*x + y*y; }
Point(int x = 0, int y = 0)
: x(x), y(y) {}
bool operator<(const Point& pt) const {
if( square() < pt.square() )
return true;
if( pt.square() < square() )
return false;
if( x < pt.x )
return true;
if( pt.x < x)
return false;
return y < pt.y;
}
friend std::ostream& operator<<(std::ostream& os, const Point& pt) {
return os << "(" << pt.x << "," << pt.y << ")";
}
};
Эта структура данных (вероятно) имеет точно такой же размер, как и два целых, она менее чем сопоставима, ее можно назначить и легко распечатать.
Алгоритм проходит через все действительные точки, которые удовлетворяют x=[0, радиусу] && y=[0,x] && (x,y) внутри круга:
std::set<Point>
GetListOfPointsInsideCircle(double radius = 1) {
std::set<Point> result;
// Only examine bottom half of quadrant 1, then
// apply symmetry 8 ways
for(Point pt(0,0); pt.x <= radius; pt.x++, pt.y = 0) {
for(; pt.y <= pt.x && pt.square()<=radius*radius; pt.y++) {
result.insert(pt);
result.insert(Point(-pt.x, pt.y));
result.insert(Point(pt.x, -pt.y));
result.insert(Point(-pt.x, -pt.y));
result.insert(Point(pt.y, pt.x));
result.insert(Point(-pt.y, pt.x));
result.insert(Point(pt.y, -pt.x));
result.insert(Point(-pt.y, -pt.x));
}
}
return result;
}
Я выбрал std::set
хранить данные по двум причинам:
- Он хранится в порядке сортировки, поэтому мне не нужно
std::sort
это и - Он отклоняет дубликаты, поэтому мне не нужно беспокоиться о точках, отражение которых одинаково
Наконец, использовать этот алгоритм очень просто:
int main () {
std::set<Point> vp = GetListOfPointsInsideCircle(2);
std::copy(vp.begin(), vp.end(),
std::ostream_iterator<Point>(std::cout, "\n"));
}
Всегда стоит добавить класс точек для такой геометрической задачи, так как обычно вам нужно решить более одного. Но я не думаю, что это хорошая идея перегрузить оператор 'less', чтобы удовлетворить первую возникшую потребность. Так как:
- Указание компаратора, где вы сортируете, прояснит, какой порядок вы хотите там.
- Указание компаратора позволит легко его изменить, не затрагивая ваш общий класс точек.
- Расстояние до источника не плохой порядок, но для сетки, но, вероятно, лучше использовать строки и столбцы (сначала отсортируйте по x, затем по y).
- Такой компаратор медленнее и, таким образом, будет замедлять любой другой набор точек, где вы даже не заботитесь о норме.
В любом случае, вот простое решение с использованием конкретного компаратора и попыткой немного оптимизировать:
struct v2i{
int x,y;
v2i(int px, int py) : x(px), y(py) {}
int norm() const {return x*x+y*y;}
};
bool r_comp(const v2i& a, const v2i& b)
{ return a.norm() < b.norm(); }
std::vector<v2i> result;
for(int x = -r; x <= r; ++x) {
int my = r*r - x*x;
for(int y = 0; y*y <= my; ++y) {
result.push_back(v2i(x,y));
if(y > 0)
result.push_back(v2i(x,-y));
}
}
std::sort(result.begin(), result.end(), r_comp);