我的输入文件是一个csv文件,其数据结构如下;
5,3.5644,5.4556,3.5665
...
int_id,x_float,y_float,z_float
我有一个结构,其中包含一个点的ID及其三个坐标。我需要根据欧几里得距离找到4个最接近的结构。我已经通过幼稚的方法做到了,但是有没有有效的实现方法呢?我了解了knn算法,但它需要外部库。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <assert.h>
//struct for input csv data
struct oxygen_coordinates
{
unsigned int index; //index of an atom
//x,y and z coordinates of atom
float x;
float y;
float z;
};
//Given the data in a line in a an inputfile, process it to put it in oxygen_coordinates struct
struct oxygen_coordinates * line_splitter(struct oxygen_coordinates *data, const char *input)
{
return (sscanf(input, "%u,%f,%f,%f", &data->index, &data->x, &data->y, &data->z) != 7)
? NULL : data;
}
//Distance function for two pints in a struct
float getDistance(struct oxygen_coordinates a, struct oxygen_coordinates b)
{
float distance;
distance = sqrt((a.x - b.x) * (a.x - b.x) + (a.y-b.y) *(a.y-b.y) + (a.z - b.z) * (a.z - b.z));
return distance;
}
//struct for neighbour distance and their indices
struct nbrs_ind {
float value;
int index;
};
// comparision function for sorting the neighbours -> qsort library
int cmp(const void *pa, const void *pb)
{
struct nbrs_ind *pa1 = (struct nbrs_ind *)pa;
struct nbrs_ind *pa2 = (struct nbrs_ind *)pb;
if ((*pa1).value < (*pa2).value)
return -1;
else if ((*pa1).value > (*pa2).value)
return 1;
else
return 0;
}
//main program
int main(int argc, char *argv[])
{
FILE *stream; // file pointer
char *line = NULL; //line pointer
size_t len = 0;
ssize_t nread;
struct oxygen_coordinates * atom_data = NULL; //pointer to oxygen_coordinate struct
int numatoms = 0; // counter variable for number of atoms
int i,j,k,p ; // loop initilizers
//Check for correct number of arguments
if (argc !=2 ) {
fprintf(stderr, "Usage: %s <inputfile> <outputfile>\n", argv[0]);
exit(EXIT_FAILURE);
}
//Open the input csv file given in the first argument
stream = fopen(argv[1], "r");
if (stream == NULL) {
perror("fopen");
exit(EXIT_FAILURE);
}
while ((nread = getline(&line, &len, stream)) != -1) {
if ((atom_data = realloc(atom_data, (size_t) (numatoms + 1) * sizeof(struct oxygen_coordinates))) == NULL) {
fprintf(stderr, "error not enough memory");
exit(EXIT_FAILURE);
}
line_splitter(&atom_data[numatoms], line);
numatoms = numatoms + 1;
}
free(line);
fclose(stream);
// All the data is read in memory in atom_data
printf("-------------------------------------------\n");
printf("There are %d atoms in the input file. \n", numatoms);
printf("-------------------------------------------\n");
// declare a global array that will hold the 4 nearest atom_data...
float dist_mat[numatoms][numatoms] ;// create n by n matrix for distances
// Create a 2-D distnace matrix
for(j=0; j < numatoms; j++){
for(k=0; k < numatoms; k++) {
dist_mat[j][k] = getDistance(atom_data[j], atom_data[k]);
printf("%f\t", dist_mat[j][k]);
}
printf("\n");
}
//now I sort every row from dist_mat and get the closest 4
// I need something like as follows
////knn(atom_data[query],atom_data,4);//this should return closest 4 points based on Euclidean distances in atom_data
free(atom_data);
exit(EXIT_SUCCESS);
}
答案 0 :(得分:1)
提高性能的一种方法是认识到您不需要实际距离。比较距离的平方就足够了,因此可以跳过sqrt
函数调用。
另一个可能但不一定加快速度的事情是从仅计算x距离开始。利用距离始终为正的事实,因此,如果x距离大于第四最近点的总距离,则无需计算(a.y-b.y) *(a.y-b.y) + (a.z - b.z) * (a.z - b.z)
。
如果您选择仅从x值开始的方法,我还建议更改数据结构。代替每个点都有一个结构,请使用四个数组:int *x, *y, *z, *indexes;
这将使代码对缓存更友好。 (是的,指针和数组之间有区别,但是在这里并没有太大关系)
上述方法很容易调整。如果您想更高级,可以使用这个想法。
看这张图片:
例如,如果您在D4中有一个点,并且想要找到最近的四个邻居,并且在D4中找到了邻居,那么您知道,外面不可能有更近的邻居正方形C3:E5。同样,D4中在D3中具有邻居的点不能在区域B3:F6之外具有更近的邻居。
但是优化时首先要始终确定瓶颈。您确定这是问题所在吗?您说您从文件读取数据,并且从文件读取一行应该比计算距离慢。
答案 1 :(得分:0)
除了提到平方根效率低外,这些循环还计算每对两次的距离。
for(j=0; j < numatoms; j++){
for(k=0; k < numatoms; k++) {
dist_mat[j][k] = getDistance(atom_data[j], atom_data[k]);
printf("%f\t", dist_mat[j][k]);
}
printf("\n");
}
您可以通过计算每对一次的距离来将执行时间减半,例如
for(j=0; j < numatoms-1; j++){
for(k=j+1; k < numatoms; k++) {
dist_mat[j][k] = dist_mat[k][j] = getDistance(atom_data[j], atom_data[k]);
printf("%f\t", dist_mat[j][k]);
}
printf("\n");
}
当然,当j == k
时距离必须为0
。