繁体   English   中英

如何在数据文件上找到最接近第一个点的点? C ++

[英]How to find the closest point to the first point on the data file? C++

我的数据文件如下所示:

x              y               z
0.068472     -0.024941       0.028884
....         ....            ....
continued, there are more than 100 points. 

我想在所有数据点中找到最接近点1的点(在这种情况下为(0.068472,-0.024941,0.028884)。下面是我读取文件的代码,我应该添加什么函数来找到最接近点1的点?我应该使用最小函数来找到点1与另一个点之间的最小距离吗?但是我不确定如何在代码中编写此函数。

// Program to read an input file 

#include <iostream>
#include <fstream>
#include <string>
#include <algorithm>
using namespace std;

int main() {
    const int MAXI = 1000;
    double x, y, z, xcoordinates[MAXI], ycoordinates[MAXI], zcoordinates[MAXI];
    int i, count;

    ifstream inFile("input-week6-ad-q4-2.txt"); // ifstream function to read the file
    string line, c; // To read the characters

    if (inFile.is_open()) {
        getline(inFile, line); // To read the header of the input file then     discard it
        getline(inFile, line);

        i = 0;
        count = 0;
        while (inFile >> x >> y >> z) {
            xcoordinates[count] = x;
            ycoordinates[count] = y;
            zcoordinates[count] = z;
            count = count + 1;
        }

        for (i = 0; i < count; i++) {
            cout << xcoordinates[i] << " " << ycoordinates[i] << " " << zcoordinates[i] << "\n";
        }

        inFile.close();
    } else {
        cout << "The file could not be opened." << "\n"; // To check for any error
    }

    system("pause");
    return 0;
}

这些评论提供了正确的方向。 如果要编写的最小距离查找器是C ++,则应从简单的2d 类开始,然后通过添加第3个坐标来派生一个类来处理该类中的3d点。 如果您只是要使用单独的x, y, z坐标和三个单独的double数组-您最好使用C语言编写程序。

为2d点编写基类一点都不困难。 为了从中派生3d类,您需要注意的唯一事情就是将坐标成员声明为protected:保护成员protected:这样2d点类的所有受保护成员都可以作为3d类中的受保护成员使用(类成员)默认情况下是私有的,除非有朋友,否则永远无法访问基础的私有成员)

那么最小的二维点基类是什么样的呢? 好了,您将需要x, y坐标,并且需要一个默认的构造函数以0.0 when the class is constructed, a constructor to takexy设置为0.0 when the class is constructed, a constructor to take x and y values, and then a couple of accessor functions to get the x and y用于距离功能的`值。

最小的二维点类可以是:

/* 2D Cartesian Coordinate Point */
class point2_t {
  protected:        /* allows derived class access to x, y when inherited */
    double x, y;    /* private members would not be accessible */
  public:
    point2_t () { x = 0.0, y = 0.0; }   /* constructors */
    point2_t (const double a, const double b) : x{a}, y{b} { }
    const double& getx () const { return x; }   /* access functions */
    const double& gety () const { return y; }
    double dist (const point2_t& p) {           /* distance function */
        return sqrt ((x-p.getx()) * (x-p.getx()) +
                     (y-p.gety()) * (y-p.gety()));
    }
};

这将使您可以使用值初始化2d点,获取当前设置的值,然后计算与其他2d点的距离。 尽管这很好用,但仍然需要从文件中读取xy值,然后通过将坐标传递给构造函数来创建一个点。 (您还可以编写一个setx(double x)和相应的sety()以允许您更改x, y值)

能够cin >> point;真是太好了cin >> point; 并让它自动设置x, y值,并能够cout << point; 输出坐标。 您可以通过重载<<>>运算符来实现。 这使得读取和输出坐标数据非常方便。 为此,您可以将以下内容添加为成员函数:

    /* overload output and input operators */
    friend std::ostream& operator << (std::ostream& os, const point2_t& p) {
        os << "(" << p.x << ", " << p.y << ")";
        return os;
    }
    friend std::istream& operator >> (std::istream& is, point2_t& p) {
        is >> p.x >> p.y;
        return is;
    }

一旦编写了2d点类,您要做的就是从中派生3d点类,并添加z坐标和相应的函数来处理所有三个坐标而不是两个坐标。 从包括该基类的受保护成员的基类派生一个类的基本形式是:

class derived : public base {
    /* additions */
};

从2d点类到3d点类(包括重载<<>>运算符)的简单派生可能是:

/* 3D Cartesian Coordinate Point derived from 2D point class */
class point_t: public point2_t {
  protected:
    double z;   /* add z coordinate */
  public:
    point_t () { point2_t (0.0, 0.0); z = 0.0; };   /* default construct */
    /* construct with initializer list */
    point_t (const double a, const double b, const double c) :
                point2_t (a, b), z{c} {}
    const double& getz () const { return z; }       /* add getz accessor */
    double dist (const point_t& p) {                /* extend distance */
        return sqrt ((x-p.getx()) * (x-p.getx()) +
                     (y-p.gety()) * (y-p.gety()) +
                     (z-p.getz()) * (z-p.getz()));
    }
    /* extend operators */
    friend std::ostream& operator << (std::ostream& os, const point_t& p) {
        os << "(" << p.x << ", " << p.y << ", " << p.z << ")";
        return os;
    }
    friend std::istream& operator >> (std::istream& is, point_t& p) {
        is >> p.x >> p.y >> p.z;
        return is;
    }
};

现在您有了一个3d点类,可以计算点之间的距离。 剩下的就是为第一个点创建一个类的实例,并为您的文件创建另一个临时实例以从文件中读取其他点,从而使您可以计算两者之间的距离。 (如果要保存最近点的坐标,则第三个实例很方便)

数据文件的唯一警告是您需要丢弃包含xyz标题的第一行。 虽然您可以使用getline将行读入string并忽略它,但是C ++还提供了.ignore()流函数,该函数允许您最多忽略最多可读字符,直到达到定界符为止(此换行符案件)。 只需包含limits标头,然后就可以使用:

    std::ifstream f (argv[1]);  /* open file stream */
    ...
    /* discard 1st line in file */
    f.ignore(std::numeric_limits<std::streamsize>::max(), '\n');

(两种方法都可行)

无需将文件中的所有点都读取到容器中以供以后处理,而只需查找第一个点与其余点之间的最小距离即可。 您需要做的就是存储第一个点(下面的p1 ),然后计算它与其余点之间的距离,并保存为每个后续比较找到的最小距离(下面的distmin )。 (如果愿意,还可以保存最近点的坐标)

将其放在一起很短的main()可能看起来像:

int main (int argc, char **argv) {

    if (argc < 2) { /* validate argument available for filename */
        std::cerr << "error: insufficient input.\n";
        return 1;
    }

    std::ifstream f (argv[1]);  /* open file stream */
    point_t p1, min, tmp;       /* 1st, mininum & temporary points */
    /* initialize minimum distance to maximum allowable */
    double distmin = std::numeric_limits<double>::max();

    /* discard 1st line in file */
    f.ignore(std::numeric_limits<std::streamsize>::max(), '\n');

    if (!(f >> p1)) {   /* read 1st point */
        std::cerr << "error: failed read of p1\n";
        return 1;
    }
    while (f >> tmp) {  /* read remaining points */
        double dist = tmp.dist (p1);    /* get distance from p1 */
        if (dist < distmin) {           /* check less than distmin? */
            distmin = dist;             /* set new distmin */
            min = tmp;                  /* set new closest point */
        }
    }
    /* output results */
    std::cout << "\nclosest point to " << p1 << "\n\n" << min <<
                "\n\ndistance: " << distmin << '\n';
}

完整的示例如下:

#include <iostream>
#include <iomanip>
#include <fstream>
#include <cmath>
#include <limits>

/* 2D Cartesian Coordinate Point */
class point2_t {
  protected:        /* allows derived class access to x, y when inherited */
    double x, y;    /* private members would not be accessible */
  public:
    point2_t () { x = 0.0, y = 0.0; }   /* constructors */
    point2_t (const double a, const double b) : x{a}, y{b} { }
    const double& getx () const { return x; }   /* access functions */
    const double& gety () const { return y; }
    double dist (const point2_t& p) {           /* distance function */
        return sqrt ((x-p.getx()) * (x-p.getx()) +
                     (y-p.gety()) * (y-p.gety()));
    }
    /* overload output and input operators */
    friend std::ostream& operator << (std::ostream& os, const point2_t& p) {
        os << "(" << p.x << ", " << p.y << ")";
        return os;
    }
    friend std::istream& operator >> (std::istream& is, point2_t& p) {
        is >> p.x >> p.y;
        return is;
    }
};

/* 3D Cartesian Coordinate Point derived from 2D point class */
class point_t: public point2_t {
  protected:
    double z;   /* add z coordinate */
  public:
    point_t () { point2_t (0.0, 0.0); z = 0.0; };   /* default construct */
    /* construct with initializer list */
    point_t (const double a, const double b, const double c) :
                point2_t (a, b), z{c} {}
    const double& getz () const { return z; }       /* add getz accessor */
    double dist (const point_t& p) {                /* extend distance */
        return sqrt ((x-p.getx()) * (x-p.getx()) +
                     (y-p.gety()) * (y-p.gety()) +
                     (z-p.getz()) * (z-p.getz()));
    }
    /* extend operators */
    friend std::ostream& operator << (std::ostream& os, const point_t& p) {
        os << "(" << p.x << ", " << p.y << ", " << p.z << ")";
        return os;
    }
    friend std::istream& operator >> (std::istream& is, point_t& p) {
        is >> p.x >> p.y >> p.z;
        return is;
    }
};

int main (int argc, char **argv) {

    if (argc < 2) { /* validate argument available for filename */
        std::cerr << "error: insufficient input.\n";
        return 1;
    }

    std::ifstream f (argv[1]);  /* open file stream */
    point_t p1, min, tmp;       /* 1st, mininum & temporary points */
    /* initialize minimum distance to maximum allowable */
    double distmin = std::numeric_limits<double>::max();

    /* discard 1st line in file */
    f.ignore(std::numeric_limits<std::streamsize>::max(), '\n');

    if (!(f >> p1)) {   /* read 1st point */
        std::cerr << "error: failed read of p1\n";
        return 1;
    }
    while (f >> tmp) {  /* read remaining points */
        double dist = tmp.dist (p1);    /* get distance from p1 */
        if (dist < distmin) {           /* check less than distmin? */
            distmin = dist;             /* set new distmin */
            min = tmp;                  /* set new closest point */
        }
    }
    /* output results */
    std::cout << "\nclosest point to " << p1 << "\n\n" << min <<
                "\n\ndistance: " << distmin << '\n';
}

输入文件示例

在与您的值相同的范围内生成一些其他随机点将为您提供一个总点数为10的数据文件,用于验证程序,例如

$ cat dat/3dpoints-10.txt
x              y               z
0.068472     -0.024941       0.028884
-0.023238      0.028574      -0.021372
 0.015325     -0.086100       0.011980
-0.028137     -0.025350       0.021614
-0.013860      0.015710      -0.022659
 0.026026     -0.093600       0.019175
 0.010445     -0.098790       0.023332
-0.021594      0.017428      -0.025986
 0.021800     -0.027678       0.017078
-0.016704      0.017951       0.011059

使用/输出示例

然后,运行程序将找到最接近您的第一个点( p1 )的点,并提供以下答案:

$ ./bin/point_distmin dat/3dpoints-10.txt

closest point to (0.068472, -0.024941, 0.028884)

(0.0218, -0.027678, 0.017078)

distance: 0.0482198

仔细检查一下,如果您有任何问题,请告诉我。 cpprefernce.com是最佳参考文献之一(标准本身除外)。 请将该书签放在手边,并花一些时间来了解该语言和网站。

此答案很大程度上基于David C. Rankin的观点 main()几乎是复制粘贴的,带有两个额外的检查,显式流关闭和某些样式更改。 主要区别在于存储点和处理点的方式。 这里没有继承。 无论如何,这只是POD struct

数据,数据,数据。 您以点为单位考虑任务,因此您应该具有一种数据类型,以将坐标整齐地保持为一个点:

struct Point3d {
    double x, y, z;
};

为了与C ++ i / o流进行流畅的协作,让我们重载>><<运算符:

std::ostream& operator << (std::ostream& os, const Point3d& p) {
    os << "(" << p.x << ", " << p.y << ", " << p.z << ")";
    return os;
}

std::istream& operator >> (std::istream& is, Point3d& p) {
    is >> p.x >> p.y >> p.z;
    return is;
}

最后,我们需要计算两点之间的距离。 度量根据逻辑和定义都是对称的,因此让我们在代码中进行反映,并定义一个简单的函数来计算欧几里得距离:

double distance(const Point3d &a, const Point3d &b) {
    auto squared = std::pow(a.x-b.x, 2) +
                   std::pow(a.y-b.y, 2) +
                   std::pow(a.z-b.z, 2);
    return sqrt(squared);
}

那么整个程序是:

#include <iostream>
#include <iomanip>
#include <fstream>
#include <cmath>
#include <limits>

struct Point3d {
    double x, y, z;
};

std::ostream& operator << (std::ostream& os, const Point3d& p) {
    os << "(" << p.x << ", " << p.y << ", " << p.z << ")";
    return os;
}

std::istream& operator >> (std::istream& is, Point3d& p) {
    is >> p.x >> p.y >> p.z;
    return is;
}

double distance(const Point3d &a, const Point3d &b) {
    auto squared = std::pow(a.x-b.x, 2) +
                   std::pow(a.y-b.y, 2) +
                   std::pow(a.z-b.z, 2);
    return sqrt(squared);
}

int main(int argc, char **argv) {
    if (argc != 2) {
        std::cerr << "Exactly one argument expected, got " << argc << "\n";
        return 1;
    }

    std::ifstream f(argv[1]);
    if (!f.is_open()) {
        std::cerr << "error: failed to open '" << argv[1] << "'\n";
        return 1;
    }

    // discard the header line
    f.ignore(std::numeric_limits<std::streamsize>::max(), '\n');

    Point3d first_pt;
    if (!(f >> first_pt)) {  // read the first point
        std::cerr << "error: failed read of the first point\n";
        return 1;
    }

    bool other_points = false;
    double dist_min = std::numeric_limits<double>::max();
    Point3d closest, current;    
    while (f >> current) {  // loop through the other points
        other_points = true;
        double dist = distance(first_pt, current);
        if (dist < dist_min) {
            dist_min = dist;
            closest = current;
        }
    }
    f.close();

    if (other_points) {
        std::cout << "closest point to " << first_pt <<
                     " is " << closest << " [distance: " << dist_min << "]\n";
    } else {
        std::cout << "There was only one point in the file\n";
    }
}

您可以计算3维中两个点的欧几里得距离(点1与其他点),然后将它们进行比较以找到最接近的点。 该公式可在Wiki上找到: https//en.wikipedia.org/wiki/Euclidean_distance

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM