关于三维空间的点的算法

大鸟的小天空 2003-01-06 06:58:55
在数据库中储存很多点的坐标(X,Y,Z)。这些点(10000个以上)中只要相互相邻(彼此的距离为1)则称为在一团,团的大小范围不限,比如一条直线,一个球体(充满的),一个折线,就是说只要能连在一起的点就是一团的(一个空心的圆形不是一团,因为组成他的没个点都不是彼此相邻,距离为1)。。当然,这个3维空间中,有不连续的点,那么就是另外的一团,要求将这个空间中的点以团的单位来区分之。
希望能得到大虾的帮忙。
...全文
281 28 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
28 条回复
切换为时间正序
请发表友善的回复…
发表回复
大鸟的小天空 2003-01-11
  • 打赏
  • 举报
回复
谢谢大家了。结账。
cuiyuting 2003-01-10
  • 打赏
  • 举报
回复
第三部分
//找到下一个没有团的点
inline void GetNextBeginPoint (Point* & pbegin, Point * pend)
{
for (;(pbegin != pend) && (pbegin->group != -1);pbegin++);
}

//设置每个点的团编号
int SetGroupNum (Point * Points, Point ** PointX, Point ** PointY, Point ** PointZ, int n)
{
int GroupNum = 0;
LineStack LineSX, LineSY, LineSZ;
Point * pBeginPoint = Points;
Point * pPoint;
Point * pPointsEnd = &Points[n];
Point ** Line;
while (pBeginPoint != pPointsEnd){
LineSX.clear(); LineSY.clear(); LineSZ.clear();
pPoint = pBeginPoint;
pPoint->group = GroupNum;
if(pPoint->xlinelen > 1){
LineSX.push_back(pPoint->xline);
(*(pPoint->xline))->xlinelen = 0; //每个线段起点的linelen如果是0,
//表示这个线段已经被处理过或正准备接收处理
}
if(pPoint->ylinelen > 1){
LineSY.push_back(pPoint->yline);
(*(pPoint->yline))->ylinelen = 0;
}
if(pPoint->zlinelen > 1){
LineSZ.push_back(pPoint->zline);
(*(pPoint->yline))->zlinelen = 0;
}
//找到所有与pPoint相连的点,把他们的group置成当前的GroupNum
while ((!LineSX.empty())||(!LineSY.empty())||(!LineSZ.empty())){ //还有没有待处理的线段
if (!LineSX.empty()){ //处理x线段
Line = LineSX.back();
LineSX.pop_back();
for (int i = 0; i < (Line[1])->xlinelen; i++){ //找到这个线段上所有点的y,z线段
if (Line[i]->group == -1){
if ((*(Line[i]->yline))->ylinelen > 1){ //判断这个线段是否被用过
LineSY.push_back(Line[i]->yline);
(*(Line[i]->yline))->ylinelen = 0;
}
if ((*(Line[i]->zline))->zlinelen > 1){
LineSZ.push_back(Line[i]->zline);
(*(Line[i]->zline))->zlinelen = 0;
}
Line[i]->group = GroupNum;
}
}
}
if (!LineSY.empty()){
Line = LineSY.back();
LineSY.pop_back();
for (int i = 0; i < (Line[1])->ylinelen; i++){
if (Line[i]->group == -1){
if ((*(Line[i]->zline))->zlinelen > 1){
LineSZ.push_back(Line[i]->zline);
(*(Line[i]->zline))->zlinelen = 0;
}
if ((*(Line[i]->xline))->xlinelen > 1){
LineSX.push_back(Line[i]->xline);
(*(Line[i]->xline))->xlinelen = 0;
}
Line[i]->group = GroupNum;
}
}
}
if (!LineSZ.empty()){
Line = LineSZ.back();
LineSZ.pop_back();
for (int i = 0; i < (Line[1])->zlinelen; i++){
if (Line[i]->group == -1){
if ((*(Line[i]->xline))->xlinelen > 1){
LineSX.push_back(Line[i]->xline);
(*(Line[i]->xline))->xlinelen = 0;
}
if ((*(Line[i]->yline))->ylinelen > 1){
LineSY.push_back(Line[i]->yline);
(*(Line[i]->yline))->ylinelen = 0;
}
Line[i]->group = GroupNum;
}
}
}
}
pBeginPoint++;
GetNextBeginPoint(pBeginPoint, pPointsEnd);
GroupNum++;
}
return GroupNum;
}

void OutPutPoint (Point * Points, int n)
{
for (int i = 0; i < n; i++){
cout << Points[i].x << '\t' << Points[i].y << '\t' << Points[i].z << '\t' << Points[i].group << endl;
}
}

void OutPutPoint (Point ** Points, int n)
{
for (int i = 0; i < n; i++){
cout << (*Points[i]).x << '\t' << (*Points[i]).y << '\t' << (*Points[i]).z << endl;
}
}

//输出的PointArray中每个点的group就是所在团的编号,返回值是团的总数。
int GetGroup (Point * PointArray, int n)
{
Point * * PointX = new (Point * [n]);
Point * * PointY = new (Point * [n]);
Point * * PointZ = new (Point * [n]);
for (int i = 0; i < n; i++){
PointX[i] = & PointArray[i];
PointY[i] = & PointArray[i];
PointZ[i] = & PointArray[i];
PointArray[i].group = -1; //-1表示没有团
}

//OutPutPoint(PointArray, n);
//clock_t x = clock();
SortX(PointX, n);
SortY(PointY, n);
SortZ(PointZ, n);
//cout << clock() - x << endl;
//x = clock();
SetXLine(PointX, n);
//OutPutPoint(PointX, n);
SetYLine(PointY, n);
//OutPutPoint(PointY, n);
SetZLine(PointZ, n);
//OutPutPoint(PointZ, n);
//cout << clock() - x << endl;
//x = clock();
int GNum = SetGroupNum (PointArray, PointX, PointY, PointZ, n);
//cout << clock() - x << endl;
delete [] PointX;
delete [] PointY;
delete [] PointZ;
return GNum;
}

int main(int argc, char * argv[])
{
Point * PointArray = new (Point [N]);
GenRandPoint (PointArray, N);

clock_t x = clock();
cout << GetGroup (PointArray, N) << endl;
cout << clock() - x << endl;
//OutPutPoint (PointArray, N);
delete [] PointArray;
return 0;
}
cuiyuting 2003-01-10
  • 打赏
  • 举报
回复
第二部分
//构造每个点的X线段信息xline指向所在x线段起点,xlinelen是所在x线段长度
void SetXLine(Point ** PointX, int n)
{
int CurrentLineBegin = 0;
int CurrentX = PointX[0]->x;
int CurrentY = PointX[0]->y;
int CurrentZ = PointX[0]->z;
PointX[0]->xline = PointX;
for (int i = 1; i < n; i++){
if ((PointX[i]->y != CurrentY)||(PointX[i]->z != CurrentZ)||(PointX[i]->x > CurrentX + 1)){
for (int j = CurrentLineBegin; j < i; j++){
PointX[j]->xlinelen = i - CurrentLineBegin;
}
CurrentLineBegin = i;
CurrentX = PointX[i]->x;
CurrentY = PointX[i]->y;
CurrentZ = PointX[i]->z;
PointX[i]->xline = &PointX[i];
}
else{
PointX[i]->xline = &PointX[CurrentLineBegin];
CurrentX = PointX[i]->x;
}
}
for (int j = CurrentLineBegin; j < n; j++){
PointX[j]->xlinelen = i - CurrentLineBegin;
}
}

void SetYLine(Point ** PointY, int n)
{
int CurrentLineBegin = 0;
int CurrentX = PointY[0]->x;
int CurrentY = PointY[0]->y;
int CurrentZ = PointY[0]->z;
PointY[0]->yline = PointY;
for (int i = 1; i < n; i++){
if ((PointY[i]->z != CurrentZ)||(PointY[i]->x != CurrentX)||(PointY[i]->y > CurrentY + 1)){
for (int j = CurrentLineBegin; j < i; j++){
PointY[j]->ylinelen = i - CurrentLineBegin;
}
CurrentLineBegin = i;
CurrentX = PointY[i]->x;
CurrentY = PointY[i]->y;
CurrentZ = PointY[i]->z;
PointY[i]->yline = &PointY[i];
}
else{
PointY[i]->yline = &PointY[CurrentLineBegin];
CurrentY = PointY[i]->y;
}
}
for (int j = CurrentLineBegin; j < n; j++){
PointY[j]->ylinelen = i - CurrentLineBegin;
}
}


void SetZLine(Point ** PointZ, int n)
{
int CurrentLineBegin = 0;
int CurrentX = PointZ[0]->x;
int CurrentY = PointZ[0]->y;
int CurrentZ = PointZ[0]->z;
PointZ[0]->zline = PointZ;
for (int i = 1; i < n; i++){
if ((PointZ[i]->x != CurrentX)||(PointZ[i]->y != CurrentY)||(PointZ[i]->z > CurrentZ + 1)){
for (int j = CurrentLineBegin; j < i; j++){
PointZ[j]->zlinelen = i - CurrentLineBegin;
}
CurrentLineBegin = i;
CurrentX = PointZ[i]->x;
CurrentY = PointZ[i]->y;
CurrentZ = PointZ[i]->z;
PointZ[i]->zline = &PointZ[i];
}
else{
PointZ[i]->zline = &PointZ[CurrentLineBegin];
CurrentZ = PointZ[i]->z;
}
}
for (int j = CurrentLineBegin; j < n; j++){
PointZ[j]->zlinelen = i - CurrentLineBegin;
}
}
cuiyuting 2003-01-10
  • 打赏
  • 举报
回复
分就免了吧,我是看到这个题比较有意思才做的,
而且程序写得也不怎么样
代码如下(超长了):
#include <iostream>
#include <algorithm>
#include <cstdlib>
#include <deque>
#include <ctime>
using namespace std;

//主要执行时间集中在Sort上面(5/6),考虑基数排序
//TODO:把线段信息和团编号从点的结构里分离出来,在GetGroup中单独建立数组,
//线段信息只在GetGroup函数中有用,结束后可以delete,节省空间。
struct Point {
//3D
int x;
int y;
int z;
//所在线段长度
int xlinelen;
int ylinelen;
int zlinelen;
//团编号
int group;
//所在线段起始位置
Point ** xline;
Point ** yline;
Point ** zline;
};

typedef deque <Point **> LineStack;

const int N = 1000000; //点个数

//生成随机点,测试用
void GenRandPoint (Point * pPoint, int n)
{
clock_t x = clock();
srand (x);
for (int i = 0; i < n; i++){
pPoint[i].x = rand()%100;
pPoint[i].y = rand()%100;
pPoint[i].z = rand()%100;
}
}

//比较两点的x值,用于sort
inline int CompareXPoint(Point * pPoint1, Point * pPoint2 )
{
return ((pPoint1->x) < (pPoint2->x));
}

inline int CompareYPoint(Point * pPoint1, Point * pPoint2 )
{
return ((pPoint1->y) < (pPoint2->y));
}

inline int CompareZPoint(Point * pPoint1, Point * pPoint2 )
{
return ((pPoint1->z) < (pPoint2->z));
}

//输出PointX中所有X轴上相邻的点组成的线段(包括单个点)
void SortX(Point ** PointX, int n)
{
sort(PointX, &(PointX[n]), CompareYPoint); //先按Y排序
Point ** begin = PointX, ** end = PointX + 1;
while (begin != &(PointX[n])){
int currenty = (*begin)->y;
while ((end != &(PointX[n])) && ((*end)->y == currenty)){
end++;
}
sort(begin, end, CompareZPoint); //Y相同的点按Z排序
begin = end;
end++;
}
begin = PointX, end = PointX + 1;
while (begin != &(PointX[n])){
int currenty = (*begin)->y;
int currentz = (*begin)->z;
while ((end != &(PointX[n])) && ((*end)->z == currentz) && ((*end)->y == currenty)){
end++;
}
sort(begin, end, CompareXPoint); //YZ都相同的点按X排序
begin = end;
end++;
}
}

void SortY(Point ** PointY, int n)
{
//memcpy (PointY, PointX, sizeof (Point *) * n);
sort(PointY, &(PointY[n]), CompareZPoint);
Point ** begin = PointY, ** end = PointY + 1;
while (begin != &(PointY[n])){
int currentz = (*begin)->z;
while ((end != &(PointY[n])) && ((*end)->z == currentz)){
end++;
}
sort(begin, end, CompareXPoint);
begin = end;
end++;
}
begin = PointY, end = PointY + 1;
while (begin != &(PointY[n])){
int currentz = (*begin)->z;
int currentx = (*begin)->x;
while ((end != &(PointY[n])) && ((*end)->x == currentx) && ((*end)->z == currentz)){
end++;
}
sort(begin, end, CompareYPoint);
begin = end;
end++;
}
}

void SortZ(Point ** PointZ, int n)
{
//memcpy (PointZ, PointY, sizeof (Point *) * n);
sort(PointZ, &(PointZ[n]), CompareXPoint);
Point ** begin = PointZ, ** end = PointZ + 1;
while (begin != &(PointZ[n])){
int currentx = (*begin)->x;
while ((end != &(PointZ[n])) && ((*end)->x == currentx)){
end++;
}
sort(begin, end, CompareYPoint);
begin = end;
end++;
}
begin = PointZ, end = PointZ + 1;
while (begin != &(PointZ[n])){
int currentx = (*begin)->x;
int currenty = (*begin)->y;
while ((end != &(PointZ[n])) && ((*end)->y == currenty) && ((*end)->x == currentx)){
end++;
}
sort(begin, end, CompareZPoint);
begin = end;
end++;
}
}
/*void SortX(Point ** PointX, int n)
{
stable_sort(PointX, &(PointX[n]), CompareXPoint);
stable_sort(PointX, &(PointX[n]), CompareYPoint);
stable_sort(PointX, &(PointX[n]), CompareZPoint);
}

void SortY(Point ** PointX, Point ** PointY, int n)
{
memcpy (PointY, PointX, sizeof (Point *) * n);
stable_sort(PointY, &(PointY[n]), CompareXPoint);
}

void SortZ(Point ** PointY, Point ** PointZ, int n)
{
memcpy (PointZ, PointY, sizeof (Point *) * n);
stable_sort(PointZ, &(PointZ[n]), CompareYPoint);
}*/
xqr 2003-01-10
  • 打赏
  • 举报
回复
提供另一算法---三维点阵6向追踪法(类似二维点阵“种子填充”中的4向搜索法)
--------------------------------------------------------------------
设空间点坐标值为整型
根据空间点坐标范围(DX,DY,DZ)定义三维数组 char Q[DX][DY][DZ] 并冲0
将各空间点(x,y,z)映射至Q,取值1(或记空间点号)
int n=0; //团号
顺序扫描Q
{
if(Q[i][j][k]!=0)
{
调用“6 向判别与追踪函数”
// 6向指:[i+1][j][k],[i-1][j][k],[i][k+1][j], [i][k-1][j], [i][k][j+1], [i][k][j-1]
将Q[i][j][k]加进编号为n的团
Q[i][j][k]=0;
}
n++;
}
最后,团的个数为n
注:“6 向判别与追踪函数”可做成是递归的,或栈结构的,或队结构的。
-------------------------------------------------------------
大鸟的小天空 2003-01-10
  • 打赏
  • 举报
回复
cuiyuting(阿崔)
能不能把你的代码贴出来,我看看,谢了。
给你500分酬谢。


不过,我想知道,没有更好的算法了吗?
xqr 2003-01-10
  • 打赏
  • 举报
回复
更正:应将 n++; 提至上一个{ }内。
更正后如下:
-------------------------------------------------------
设空间点坐标值均为整型
根据空间点坐标范围(DX,DY,DZ)定义三维数组 Q[][][]
DX=Xmax-Xmin+1; DY=Ymax-Ymin+1; DZ=Zmax-Zmin+1;
char Q[DX][DY][DZ];
Q冲0
将各空间点Ps(Xs,Ys,Zs)映射至Q
i=Xs-Xmin; j=Ys-Ymin; k=Zs-Zmin;
Q[i][j][k]=1; //或记空间点号s

int n=0; //团的编号
for(int i=0; i<DX; i++)
for(int j=0; j<DY; j++)
for(int k=0; k<DZ; k++)
{
if(Q[i][j][k]!=0)
{
trace6(i,j,k,n); //调用“6 向判别与追踪函数”
n++;
}
}

void trace6(int i, int j, int k, int n)
{
将Q[i][j][k]加进编号为n的团
Q[i][j][k]=0;
if(Q[i+1][j][k]!=0)
{
trace6(i+1,j,k,n); //递归调用
}
if(Q[i-1][j][k]!=0)
{
trace6(i-1,j,k,n); //递归调用
}
if(Q[i][j+1][k]!=0)
{
trace6(i,j+1,k,n); //递归调用
}
if(Q[i][j-1][k]!=0)
{
trace6(i,j-1,k,n); //递归调用
}
if(Q[i][j][k+1]!=0)
{
trace6(i,j,k+1,n); //递归调用
}
if(Q[i][j][k-1]!=0)
{
trace6(i,j,k-1,n); //递归调用
}
}
----------------------------------------------
cuiyuting 2003-01-10
  • 打赏
  • 举报
回复
呵呵,我做了一个,在P4 1.7G 256M内存,Win2000系统下,处理1M个随机点(数据放在内存中),用时在6秒左右(内存消耗大概是50M左右),不知道能不能满足你的需求
我的做法其实和 sliant() 的差不了多少,
基本想法如下,任两个点如果相邻,那么他们要么在与x轴平行的直线上,要么在与y(或者z)轴平行的直线上。
那么用排序算法很容易就可以把这些点组成的所有与x轴,y轴,z轴平行的线段都求出来(我用的c++自带的sort函数)
然后遍历这些线段,就可以完成团的计算
复杂度也是O(nlogn)
xqr 2003-01-10
  • 打赏
  • 举报
回复
也将“三维点阵6向追踪法”说得细一点。
6向是指:上、下、左、右、前、后。
--------------------------------------------------------------------
设空间点坐标值均为整型
根据空间点坐标范围(DX,DY,DZ)定义三维数组 Q[][][]
DX=Xmax-Xmin+1; DY=Ymax-Ymin+1; DZ=Zmax-Zmin+1;
char Q[DX][DY][DZ];
Q冲0
将各空间点Ps(Xs,Ys,Zs)映射至Q
i=Xs-Xmin; j=Ys-Ymin; k=Zs-Zmin;
Q[i][j][k]=1; //或记空间点号s

int n=0; //团的编号
for(int i=0; i<DX; i++)
for(int j=0; j<DY; j++)
for(int k=0; k<DZ; k++)
{
if(Q[i][j][k]!=0)
{
trace6(i,j,k,n); //调用“6 向判别与追踪函数”
}
n++;
}

void trace6(int i, int j, int k, int n)
{
将Q[i][j][k]加进编号为n的团
Q[i][j][k]=0;
if(Q[i+1][j][k]!=0)
{
trace6(i+1,j,k,n); //递归调用
}
if(Q[i-1][j][k]!=0)
{
trace6(i-1,j,k,n); //递归调用
}
if(Q[i][j+1][k]!=0)
{
trace6(i,j+1,k,n); //递归调用
}
if(Q[i][j-1][k]!=0)
{
trace6(i,j-1,k,n); //递归调用
}
if(Q[i][j][k+1]!=0)
{
trace6(i,j,k+1,n); //递归调用
}
if(Q[i][j][k-1]!=0)
{
trace6(i,j,k-1,n); //递归调用
}
}
----------------------------------------------
最后,团的个数为n
trace是做成递归的。
zzwu 2003-01-09
  • 打赏
  • 举报
回复
以下圖形:
。。。。。。
。。。。。。
。。。。。。
。。。。。。
。。。。。。
。。。。。。
。。。。。。
。。。。。。

是2團,還是1團?

以下圖形:


。。。。。。
。。。。。。
。。 。。
。。 。。 。。
。。 。。
。。。。。。
。。。。。。

又算幾團?

xqr 2003-01-09
  • 打赏
  • 举报
回复
按frman() 给出的定义,下图中各点都彼此“相邻”,但它是一个“团”吗?

。。。。。。
。。。。。。
。。 。。
。。 。。
。。 。。
。。。。。。
。。。。。。
大鸟的小天空 2003-01-09
  • 打赏
  • 举报
回复
xqr(星球人)

老大,郁闷,不明白你的算法。怎么实现检出,又怎么实现剔除。
大鸟的小天空 2003-01-09
  • 打赏
  • 举报
回复
正如frman()所说,团的定义就是那样。
哈哈,向你学习表达能力。
大鸟的小天空 2003-01-09
  • 打赏
  • 举报
回复
xqr(星球人)所说的是一团
以下圖形:
。。。。。。
。。。。。。
。。。。。。
。。。。。。
。。。。。。
。。。。。。
。。。。。。
。。。。。。
是一团
。。。。。。
。。。。。。
。。。。。。
。。。。。。
。。。。。。
。。。。。。
。。。。。。
。。。。。。
不是一团

。。。。。。
。。。。。。
。。 。。
。。 。。 。。
。。 。。
。。。。。。
。。。。。。

是3团,外面一个,里面一个。第一条横线是一个。
sliant 2003-01-08
  • 打赏
  • 举报
回复
简单的想法,供你参考:


数据结构:
n 个点存放在一个 list 中,这个 list 可以用坐标进行 hash 查找。
也可以将点存放在数据库表中,三个坐标联合索引。


算法:
对一个不完整的团(初始化时就只有一点),计算这个团所有相邻的坐标存入 arround_list,重复以下步骤:查找 arround_list 坐标的点是否存在,如果存在则将这些点加入团,同时这些点的相邻坐标添加到下一次的 arround_list,直到 arround_list 坐标的点都不存在,则得到一个完整的团。
时间复杂度应该不大于 O( n*log(n) )
大鸟的小天空 2003-01-08
  • 打赏
  • 举报
回复
这个数据库最少有2万条,如果算法不对要死记呀。我已做出来一个方法,不过要运行好长时间。所以请大家来帮助我。

不知道 xqr(星球人) 的追踪法,怎么个说法。
xqr 2003-01-08
  • 打赏
  • 举报
回复
请对“团”定义再说清楚些,否则难定算法。
xqr 2003-01-08
  • 打赏
  • 举报
回复
“团”可定义为:在空间连续(dx<=1 and dy<=1 and dz<=1)且内部无“空洞”的集合。
第一步:检出仅满足连续的集合Ai(i=0,1,2,...)。
第二步:剔除有“空洞”的A,剩下的便是“团”。

frman 2003-01-08
  • 打赏
  • 举报
回复
晕。。。。。

是不是可以这么定义,一个团中的点必须彼此“相邻”。
而“相邻”可以从两方面得到
1。 A点和B点中有至少两个坐标相同
2。 A点和B点虽没有至少两个坐标相同,但他们通过法则1都与C相邻,所以A,B相邻

不知是否是这样
大鸟的小天空 2003-01-08
  • 打赏
  • 举报
回复
这个数据库最少有2万条,如果算法不对要死记呀。我已做出来一个方法,不过要运行好长时间。所以请大家来帮助我。

不知道 xqr(星球人) 的追踪法,怎么个说法。
加载更多回复(8)

33,028

社区成员

发帖
与我相关
我的任务
社区描述
数据结构与算法相关内容讨论专区
社区管理员
  • 数据结构与算法社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧