【问题标题】:Find relationship between gps data查找gps数据之间的关系
【发布时间】:2018-02-03 05:15:20
【问题描述】:

1000 万用户 gps 数据,结构如下:

userId
startGps
endGps

一个用户有两个gps,起点和终点。如果来自不同用户的两个点的距离大于 1 公里。我们将在那里定义用户可能是亲密关系。

userA startGpsA endGpsA
userB startGpsB endGpsB

function relation(userGpsA A, userGpsB B)
    if(distance(A.startGps , B.startGps) > 1km || distance(A.startGps , B.endGps) > 1km || distance(A.endGps , B.endGps) > 1km)
        return A,B
    return null

我怎样才能快速找到这些关系?

【问题讨论】:

  • 你能在你的算法开始之前维护/计算额外的数据结构(O(#user))吗?如果是这样,您可以对 n 维网格进行聚类(其中 n 是距离函数使用的坐标数)。
  • 我想到的是空间分区树。有了它,你就可以接近“免费”了。但也是一棵非常的大树。您必须告诉我们您希望做出什么样的权衡。生活中没有什么是免费的。 :P

标签: java multithreading algorithm


【解决方案1】:

一种可能的算法使用空间“桶”来减少计算时间。 它不会做特殊的线程技巧,但会减少很多用户比较(取决于桶的大小)。

我们的想法是为每个彼此相距不远的用户放入相同的“桶”,并在“桶”上创建一个索引,以便以低成本获取相邻的“桶”。

假设我们有

class User{
    long userId;
    GPS start;
    GPS stop;
}

class GPS{
    long x;
    long y;
}

首先我们为索引用户创建一个类:

class BucketEntity implements Comparable<BucketEntity>{
 User origin;
 long x;
 long y
}
class Bucket extends Set<BucketEntity {
}

我们将为每个用户创建两个 BucketEntity,一个用于“开始”,一个用于“结束”。我们会将这些 BucketEntity 存储到一个特殊的索引数据结构中,以便轻松检索最近的其他 BucketEntity。

class Index extends ConcurrentHashMap<BucketEntity,Bucket> {
      // Overload the 'put' implementation to correctly manage the Bucket (null initialy, etc...)
}

我们只需要在 BucketEntity 类中实现 'hash' (和 'equals' 方法。如果两个 BucketEntity 距离不远,'hash' 和 'equals' 的规范是相同的)其他。对于给定的 BucketEntity,我们还希望能够计算在空间上与另一个 Bucket 相邻的 Bucket 的“哈希”函数。

要获得“哈希”和“等于”的正确行为,一个不错/快速的解决方案是进行“精度降低”。简而言之,如果您有“x = 1248813”,则将其替换为“x=124”(除以 1000),就像将 GPS 米精度更改为 GPS 公里精度一样。

public static long scall = 1000;
boolean equals(BucketEntity that)
{
   if (this == that) return true;
   if (this.x / scall == that.x / scall &&
       this.y / scall == that.y / scall)
      return true;
   return false;
}

// Maybe an 'int' is not enough to correctly hash your data
// if so you have to create you own implementation of Map
// with a special "long hashCode()" support.
int hashCode()
{
     // We put the 'x' bits in the high level, and the 'y' bits in the low level.
     // So the 'x' and 'y' don't conflict.
     // Take extra-care of the value of 'scall' relatively to your data and the max value of 'int'. scall == 10000 should be a maximum.
     return (this.x / scall) * scall + (this.y / scall);
}

正如你在hashCode()方法中看到的那样,彼此靠近的Bucket确实接近hashCode(),如果我给你一个Bucket,你也可以计算空间相邻的Bucket hashCode()。

现在您可以获得与给定 BucketEntity 位于同一 Bucket 中的 BucketEntity。要获取相邻的存储桶,您需要在 BucketEntity 的存储桶周围创建 9 个虚拟 BucketEntity 到 'get()' Bucket/null。

   List<BucketEntity> shortListToCheck = // A List not a Set !
   shortListToCheck.addindex.get(new BucketEntity(user, (x / scall)+1  , (y/scall)+1 )));
   shortListToCheck.addindex.get(new BucketEntity(user, (x / scall)+1  , (y/scall) )));
   shortListToCheck.addindex.get(new BucketEntity(user, (x / scall)+1  , (y/scall)-1 )));
   shortListToCheck.addindex.get(new BucketEntity(user, (x / scall)+1  , (y/scall)+1 )));
   shortListToCheck.addindex.get(new BucketEntity(user, (x / scall)    , (y/scall) )));
   shortListToCheck.addindex.get(new BucketEntity(user, (x / scall)-1  , (y/scall)-1 )));
   shortListToCheck.addindex.get(new BucketEntity(user, (x / scall)-1  , (y/scall)+1 )));
   shortListToCheck.addindex.get(new BucketEntity(user, (x / scall)-1  , (y/scall) )));
   shortListToCheck.addindex.get(new BucketEntity(user, (x / scall)-1  , (y/scall)-1 )));

get() 匹配9个虚拟BucketEntry的所有Bucket(可以为null)。 对于给定 9 个存储桶的每个用户,按照您在问题中提供的方式计算距离。

然后玩'scall'。你能看到吗,这里对多线程没有真正的限制。也许下一个级别的算法优化是基于自适应缩放大小的自适应/递归桶大小。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-06-29
    • 2017-01-22
    • 2014-11-05
    • 2020-11-28
    • 1970-01-01
    相关资源
    最近更新 更多