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

时间:2019-03-16 01:33:51

标签: 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;
}

3 个答案:

答案 0 :(得分:1)

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

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

那么最小的二维点基类是什么样子?那么,您将需要x, y坐标,并且需要一个默认的构造函数来将xy设置为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 {在距离函数中使用{1}} y个值。

最低2d点等级可能是:

and

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

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

>>

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

z

从2d点类中简单推导3d点类(包括重载class derived : public base { /* additions */ }; <<运算符)可能是:

>>

现在您有了一个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; } }; 标题的第一行。虽然您可以使用x y z将行读入string并简单地忽略它,但是C ++还提供了流函数getline,使您可以忽略最多可读字符数,直到到达定界符(在这种情况下为换行符)。只需添加.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';
}

示例输入文件

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

#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';
}

使用/输出示例

然后,运行程序将找到最接近您的第一个点($ 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

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

答案 1 :(得分:1)

此答案很大程度上基于David C. Rankin'smain()几乎是复制粘贴的,带有两个额外的检查,显式关闭流和某些样式更改。主要区别在于存储点和处理点的方式。这里没有继承。而且还是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;
}

最后,我们需要计算两点之间的距离。指标是逻辑对称的,by definition也是对称的,因此让我们在代码中反映出来,并定义一个简单的函数来计算欧几里得距离:

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";
    }
}

答案 2 :(得分:0)

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