【问题标题】:Fast algorithm for finding all people alive at a given time?在给定时间找到所有活着的人的快速算法?
【发布时间】:2019-10-15 13:12:43
【问题描述】:

假设你有类似的东西

class Person {
  LocalDate bornOn;
  LocalDate diedOn;
}

假设您有一堆“Person”实例,您可以以任何您喜欢的方式存储它们。

编写一个可以列出在给定时间所有还活着的人的高效函数的最佳方法是什么?

数据结构也应该是有效的可变的,特别是在添加新元素方面。

即概念上类似于

List<Person> alive(List<Person> people, LocalDate date) {
  return people.stream().filter(x -> x.bornOn.compareTo(date) <= 0 && x.diedOn.compareTo(date) > 0).collect(Collectors.toList())
}

只会更有效率。

我的第一个直觉是拥有两个 NavigableMaps

NavigableMap<LocalDate, Person> peopleSortedByBornOn;
NavigableMap<LocalDate, Person> peopleSortedByDiedOn;

每个都可以用给定日期的 headMap() / tailMap() 查询,这些查询的交集就是结果。

有没有更快或更方便的解决方案?甚至可能是某种广泛使用的 Java 集合/映射类型支持这样的操作?

【问题讨论】:

  • 如果你可以使用任何你喜欢的数据结构并且唯一的目标是快速查询,那么你可以使用从第一个出生日期到最后一个死亡日期的所有日期的(排序的)哈希图作为键和一个人的集合/列表作为值。这是最快的查询方式。
  • @TobiasOtto 谢谢,确实如此 - 我应该提到,结构也应该是有效的可变的。
  • 我能想到的每一个优化,你都必须对另一个约束施加压力。一些可能有帮助的问题……你关心强调记忆吗?当您说可变时,您是指更改出生/死亡值还是添加新人?该算法应该是线程安全的,还是您总是想使用单个线程对其进行过滤?您是否关心初始加载时间(创建结构可能很慢)?你关心删除/插入性能吗?
  • @IoannisDeligiannis : 1) 添加新人,假设 BornOn / deadOn 永远不会改变给定的人。 2) 单线程 3) 假设您在没有任何数据的情况下开始,并且您在移动中以快速但不可预测的信息流接收 Person 对象 4) 不需要删除;插入是根据上一点
  • @Bogey 您最多可以拥有多少人?

标签: java algorithm


【解决方案1】:

我想提一下几何数据结构,例如四叉树。出于理论目的。有(born, died)坐标:死>=出生。

    d         b=d
    |    | - /
    | +  |  /
    |    | /
  D |____|/
    |   /:
    |- / :
    | /  :
    |/___:_____ b
         D

这些点都在上面的三角形中,+ 是生活在日期 D 的人的矩形区域。矩形是左上角开放的。

拥有几何数据结构就可以了。还有一些数据库可以处理这样的几何查询。

我很想看到一个实现,尽管我不会打赌速度优势。也许有大量的数字。

【讨论】:

  • 基于old answer,即使是直接的实现也应该相当不错,并且使用PostGIS,您可以获得空间索引,我想性能会更高。
【解决方案2】:

考虑到限制说明,我会保持简单,并使用地图来保留给定日期的活着的人的参考,从而有效地创建索引。

Map<LocalDate,LinkedList<Person>> aliveMap;

地图的投入成本为 O(1),LinkedList 的投入成本为 O(1)。 另一方面,得到尽可能好; O(1)(假设一个好的散列算法)。

在内存方面,您会产生额外“引用”的成本,但这可能会很重要(x64 VM 约 80 年 x 365 x 8 字节或每人 233,600 字节)。

这种方法将在get 操作上产生最佳性能,在memoryput 操作上可能是最差的。

变化: 您可以创建buckets,而不是创建完整的index,例如每年,您首先让每个人在特定年份都活着,然后过滤掉死者。

Map<Integer,LinkedList<Person>> aliveMap;

注意:我假设您的数据已经超过 100 年,而不是覆盖整个人口(75 亿)。如果您只关注 50-100 年的窗口,那么可能会有更有效的专业化。

【讨论】:

  • 感谢您的建议!如果我理解你的方法是正确的,那么以下可能是一个问题:假设没有人在 2020 年 1 月 1 日出生或死亡。你可能仍然想知道这个日期谁还活着。但是,您不会有地图条目 - 除非您从现在到永恒(或接下来的 100 年)的每一天都添加条目,这将是高度内存密集型的。目前我正在使用类似的导航地图,仅存储整体状态发生变化的日期(任何人出生或死亡),并查找最接近的小于或等于日期
  • 实际上是后者,即每天存储。鉴于平均预期寿命约为 80 年,您将拥有大约 80 年的寿命。每人 80 个条目(因此上述计算中的 80 倍)。这个想法是存储相同的对象实例,因此只会产生指针的成本。正如我在上面的评论中提到的,我相信你可以优化它的唯一方法是强调另一个资源,在这种情况下就是内存。但是,与对象内的数据相比,指针占用空间应该可以忽略不计。
  • 啊。假设您查询几天(而不是几年)的生存状态,我认为我们需要 80 * 365 个条目;如果覆盖最大寿命而不是预期,可能更像.. 125 * 365 左右。如果包括过去出生的人,则可能会更多(但同意 - 这肯定是内存与 CPU 压力之间的权衡)
  • 你是对的......现在是清晨,所以大脑还没有正常启动。该算法和特性对平均 80-90 年敏感,而不是最大值。以后看看能不能给出更好的代码示例。
【解决方案3】:

我认为可以提高效率的唯一方法是创建自己的自定义数据结构。例如,在 Java 中创建您自己的 HashMap,您可以在其中重写“put”方法。这样,当您在地图中插入一个 Person 对象时,您将从插入的那一刻起就知道它是活的还是死的。

Here您有一个关于如何创建自定义 HashMap 的示例。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-02-25
    • 2018-09-21
    相关资源
    最近更新 更多