簡體   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