将三维空间的点按照座标排序(兼谈为std::sort写compare function的注意事项)

最近碰到这样一个问题:我们从文件里读入了一组三维空间的点,其中有些点的X,Y,Z座标只存在微小的差别,远小于我们后续数据处理的精度,可以认为它们是重复的。所以我们要把这些重复的点去掉。因为数据量不大,这里不准备使用划分包围盒或者建立k-d tree这样的重型武器,我们简单的把点按照其三维坐标进行排序就好。

我们准备使用STL的std::sort来做这个排序。它要求提供一个符合如下签名的比较函数:

bool cmp(const Type1 &a, const Type2 &b)

怎么样写这个比较函数呢?基本的思路是首先按照点的X座标排序,X座标在误差范围内相等就比较Y,再相等就比较Z。

于是我很快写出第一版:

 1 bool compare(const Point& p1, const Point& p2)
 2 {
 3     if (fabs(p1.x - p2.x) < numeric_tol)
 4     {
 5         if (fabs(p1.y - p2.y) < numeric_tol)
 6         {
 7             if (fabs(p1.z - p2.z) < numeric_tol)
 8                 return true;
 9             else if (p1.z < p2.z)
10                 return true;
11             else
12                 return false;
13         }
14         else if (p1.y < p2.y)
15             return true;
16         else
17             return false;
18     }
19     else if (p1.x < p2.x)
20         return true;
21     else
22         return false;
23 }

运行时居然发现有弹出一个"invalid operator <"的对话框。

于是Google了一下,找到了std::sort的比较函数要满足的三个条件:

  • For all a, comp(a,a)==false
  • If comp(a,b)==true then comp(b,a)==false
  • if comp(a,b)==true and comp(b,c)==true then comp(a,c)==true

详细信息见这里: http://en.cppreference.com/w/cpp/concept/Compare

明显我们的比较函数不符合第一个条件。于是把第8行改成return false就可以工作了。

后来又想,可不可以在比较函数里面不使用tolerance,而只在对排序好的数组进行扫描时使用呢?又尝试了一些思路,发现这样是不行的。完整的测试代码如下:

  1 // consle_app_sort_points.cpp : Defines the entry point for the console application.
  2 //
  3
  4 #include "stdafx.h"
  5 #include <vector>
  6 #include <algorithm>
  7 #include "stdio.h"
  8
  9 using namespace std;
 10
 11 struct Point {
 12     double x;
 13     double y;
 14     double z;
 15 };
 16
 17 double numeric_tol = 1e-6;
 18
 19 // this compare function results in run time error "invalid operator <" because for below cases it will produce both p1 < p2 and p2 < p1
 20 // p1 = (1.1, 1.2, 0)
 21 // p2 = (1.2, 1.1, 0)
 22 bool compare1(const Point& p1, const Point& p2)
 23 {
 24     if (p1.x < p2.x)
 25         return true;
 26     else {
 27         if (p1.y < p2.y)
 28             return true;
 29         else
 30         {
 31             if(p1.z < p2.z)
 32                 return true;
 33             else
 34                 return false;
 35         }
 36     }
 37 }
 38
 39 // compare function with tolerance
 40 //
 41 bool compare2(const Point& p1, const Point& p2)
 42 {
 43     if (fabs(p1.x - p2.x) < numeric_tol)
 44     {
 45         if (fabs(p1.y - p2.y) < numeric_tol)
 46         {
 47             if (fabs(p1.z - p2.z) < numeric_tol)
 48                 return false;
 49             else if (p1.z < p2.z)
 50                 return true;
 51             else
 52                 return false;
 53         }
 54         else if (p1.y < p2.y)
 55             return true;
 56         else
 57             return false;
 58     }
 59     else if (p1.x < p2.x)
 60         return true;
 61     else
 62         return false;
 63 }
 64
 65 // compare function without tolerance
 66 //
 67 bool compare3(const Point& p1, const Point& p2)
 68 {
 69     if (p1.x == p2.x)
 70     {
 71         if (p1.y == p2.y)
 72         {
 73             if (p1.z == p2.z)
 74                 return false;
 75             else if (p1.z < p2.z)
 76                 return true;
 77             else
 78                 return false;
 79         }
 80         else if (p1.y < p2.y)
 81             return true;
 82         else
 83             return false;
 84     }
 85     else if (p1.x < p2.x)
 86         return true;
 87     else
 88         return false;
 89 }
 90
 91 // compare function without tolerance
 92 // essentially the same with compare3
 93 bool compare4(const Point& p1, const Point& p2)
 94 {
 95     if (p1.x < p2.x)
 96         return true;
 97     else if (p1.x > p2.x)
 98         return false;
 99     else {
100         if (p1.y < p2.y)
101             return true;
102         else if (p1.y > p2.y)
103             return false;
104         else {
105             if (p1.z < p2.z)
106                 return true;
107             else if (p1.z > p2.z)
108                 return false;
109             else
110                 return false;
111         }
112     }
113 }
114
115
116 void print(const std::vector<Point>& v)
117 {
118     std::vector<Point>::const_iterator it = v.begin();
119     for (; it != v.end(); ++it)
120         printf("{%1.7f, %1.7f, %1.7f}\n", it->x, it->y, it->z);
121 }
122
123 int _tmain(int argc, _TCHAR* argv[])
124 {
125     Point p1 = {1.0000001, 1.2000003, 0.0};
126     Point p2 = {1.0000002, 1.0,       0.0};
127     Point p3 = {1.0000002, 1.1,       0.0};
128     Point p4 = {1.0000002, 1.2000003, 0.0};
129
130     std::vector<Point> v1;
131     v1.push_back(p1);
132     v1.push_back(p2);
133     v1.push_back(p3);
134     v1.push_back(p4);
135
136     std::vector<Point> v2(v1);
137
138     printf("vector before sort:\n");
139     print(v1);
140
141     std::sort(v1.begin(), v1.end(), compare2);
142
143     printf("sort using compare function with tolerance:\n");
144     print(v1);
145
146     printf("\n\n");
147
148     printf("vector before sort:\n");
149     print(v2);
150
151     std::sort(v2.begin(), v2.end(), compare4);
152
153     printf("sort using compare function without tolerance:\n");
154     print(v2);
155
156     return 0;
157 }

运行结果如下。可以发现在比较函数里不使用tolerance确实是不行的。

points vector before sort:
{1.0000001, 1.2000003, 0.0000000}
{1.0000002, 1.0000000, 0.0000000}
{1.0000002, 1.1000000, 0.0000000}
{1.0000002, 1.2000003, 0.0000000}

sort using compare function with tolerance:
{1.0000002, 1.0000000, 0.0000000}
{1.0000002, 1.1000000, 0.0000000}
{1.0000001, 1.2000003, 0.0000000}                          // 这两个点被放在一起,是对的
{1.0000002, 1.2000003, 0.0000000}

sort using compare function without tolerance:
{1.0000001, 1.2000003, 0.0000000}                          // 第一个点和第四个点不在一起,不行
{1.0000002, 1.0000000, 0.0000000}
{1.0000002, 1.1000000, 0.0000000}
{1.0000002, 1.2000003, 0.0000000}                          // 第一个点和第四个点不在一起,不行
时间: 2024-08-28 13:30:29

将三维空间的点按照座标排序(兼谈为std::sort写compare function的注意事项)的相关文章

[C/C++标准库]_[初级]_[使用std::sort排序各种类型数据]

std::sort 场景: 1. 在使用sort排序时,有时候需要对对象的某个值进行排序,比如对类对象的某个id的int类型值或者bool类型值,其实bool类型值排序研究了半天.. test_sort.cpp #include <stdlib.h> #include <string.h> #include <string> #include <vector> #include <algorithm> #include <iostream&

如何匿名上网?-兼谈安全上网

身在中国大陆,可能大家比较关心的是科学上网:今天我谈的不是科学上网,而是匿名上网与安全上网. 什么叫匿名上网?在互联网上,匿名是互联网独特的一种特性.1993年<纽约客>杂志上彼得·斯坦纳的一幅漫画被网民们所信奉.这幅漫画的标题是“在网上,没有人知道你是一条狗”. 虽然在网络上匿名给予了人们更大的自由(阅读与发布信息的自由),但是其代价则是丧失了信息来源的确定性,而且也会引发许多不良的和违法的行为.例如网络色情.诽谤性的言论,欺诈行为,侵犯版权等等. 匿名上网的重要性大家访问网站,然后绝大多数

排序中的qsort和sort

原文:http://www.cnblogs.com/ForeverJoker/archive/2013/05/25/qsort-sort.html 先说明一下qsort和sort,只能对连续内存的数据进行排序,像链表这样的结构是无法排序的. 首先说一下, qsort qsort(基本快速排序的方法,每次把数组分成两部分和中间的一个划分值,而对于有多个重复值的数组来说,基本快速排序的效率较低,且不稳定).集成在C语言库函数里面的的qsort函数,使用 三 路划分的方法解决排序这个问题.所谓三路划分

对创业者总担心大公司抄袭的八点建议——兼谈腾讯微博

腾讯微博最近发布了调整新闻,不再赘述.正好把老生常谈的"大公司抄袭"话题再说一遍. 腾讯微博和新浪微博不是第一批twitter模式,第一批的twitter模式早就关站了,和腾讯微博.新浪微博没什么关系.而且第一批难道就不是"抄"么.所以结论一:创业者不必张口闭口担心大公司抄袭,本来你们也不是原创. 腾讯微博和新浪微博是同一批中最好的两家了,当然现在就只剩新浪微博一支独秀了.所以结论二:创业者不必张口闭口担心大公司抄袭,不是每家大公司都能抄袭成功的. 接下来抛开具体案

js 实现排序算法 -- 归并排序(Merge Sort)

原文: 十大经典排序算法(动图演示) 归并排序 归并排序是建立在归并操作上的一种有效的排序算法.该算法是采用分治法(Divide and Conquer)的一个非常典型的应用.将已有序的子序列合并,得到完全有序的序列:即先使每个子序列有序,再使子序列段间有序.若将两个有序表合并成一个有序表,称为2-路归并. 算法描述 把长度为n的输入序列分成两个长度为n/2的子序列: 对这两个子序列分别采用归并排序: 将两个排序好的子序列合并成一个最终的排序序列. 动图演示 代码实现 function merg

[排序] 快排 &amp;&amp; 冒泡(自己写)

#include <iostream> using namespace std; /* 快速排序 通过一趟排序,以轴点为界 分割为两部分:左部分 <= 轴点 <= 右部分 再分别对左右两部分继续递归排序 最终整个序列有序 */ void quickSort(int arr[], int low,int high){ //if (arr==NULL||length<=0||start<=0||end>=length) //break; int i,key,j; if

nyoj 8 一种排序(用vector,sort,不用set)

一种排序 时间限制:3000 ms  |  内存限制:65535 KB 难度:3 描述 现在有很多长方形,每一个长方形都有一个编号,这个编号可以重复:还知道这个长方形的宽和长,编号.长.宽都是整数:现在要求按照一下方式排序(默认排序规则都是从小到大): 1.按照编号从小到大排序 2.对于编号相等的长方形,按照长方形的长排序: 3.如果编号和长都相同,按照长方形的宽排序: 4.如果编号.长.宽都相同,就只保留一个长方形用于排序,删除多余的长方形:最后排好序按照指定格式显示所有的长方形: 输入 第一

EXCEL排序(杭电1862)(sort排序)

EXCEL排序 Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 13392    Accepted Submission(s): 5254 Problem Description Excel可以对一组纪录按任意指定列排序.现请你编写程序实现类似功能. Input 测试输入包含若干测试用例.每个测试用例的第1行包含两个整数 N (<=1

排序算法之冒泡排序(Bubble Sort)

基本思想 假如按照从小到大的顺序排序,对待排序数组进行遍历,如果当前值大于其后一个值则进行交换,不断的进行遍历,直到没有交换动作的发生.冒泡排序的最好时间复杂度为O(n),最坏的时间复杂度为O(n2),所以冒泡排序的平均时间复杂度为O(n2),另外冒泡排序不会改变相同元素的前后顺序,故其是一种稳定的排序算法. 实现代码 #include<iostream> using namespace std; int main() { int MyData[10] = { 7,3,12,46,32,64,