【问题标题】:Stock statistics calculation with O(1) time and space complexity具有 O(1) 时间和空间复杂度的股票统计计算
【发布时间】:2020-09-12 12:36:16
【问题描述】:

我必须用 Java 设计一个 rest API:-

  1. 接受带有以下json 的POST 请求:-

    { “仪器”:“ABC”, “价格”:“200.90”, “时间戳”:“2018-09-25T12:00:00” }

这些记录将保存在内存集合中,而不是任何类型的数据库中。

  1. 会有一个 GET API 返回过去 60 秒内收到的特定仪器记录的统计信息。 GET 请求将是:- /statistics/{instrumentName} 例如:- /statistics/ABC。响应如下所述:-

    { “计数”:“3” “分钟”:“100.00” “最大”:“200.00” “总和”:“450.00” “平均”:“150.00” }

  2. 会有另一个 GET 请求 /statistics 返回过去 60 秒内收到的所有工具的统计信息(不特定于特定工具,如 #2)

这个算法实现起来复杂的原因是应该执行 GET 调用 - O(1) 时间和空间复杂度。

我为 3# 考虑的方法是拥有一个包含 60 个桶的集合(因为我们必须计算过去 60 秒的时间,所以每 1 秒采样一次)。每次事务进入时,它都会根据键(即小时-分钟-秒)进入特定的存储桶(这将是带有此键的映射和该秒的统计信息)。

但我无法理解的是如何解决问题 2#,我们必须在 O(1) 时间和空间复杂度中获取过去 60 秒内特定仪器 /statistics/ABC 的统计数据。

清理超过 60 秒的记录的最佳策略是什么?

对算法的任何帮助将不胜感激。

【问题讨论】:

  • 您是否要求 get 始终为 O(1) 或摊销 O(1) 可接受?
  • @rici get 调用 3# 需要严格 O(1) 时间和空间复杂度。并且 get 调用 2# 的时间复杂度应该严格为 O(1)。这一要求实际上推动了解决方案的整个设计。
  • 是的,这绝对让它变得更棘手。摊销 O(1) 更容易(在实践中通常可以接受。)
  • 这里说的 O(1) 是指“在实践中需要很快”还是“这是一个家庭作业,具体要求是达到 O(1) 的性能?”
  • @templatetypedef : GET 调用的 O(1) 时间复杂度和空间复杂度,即 /statistics/ 意味着计算上述统计信息的迭代次数,即 average 、 count 、 min 、 max 等应该保持不变,不依赖于我们收到的记录/交易的数量。

标签: java multithreading algorithm data-structures time-complexity


【解决方案1】:

将数据存储在Map<String, Instrument> 中,并让类看起来像这样:

class Instrument {
    private String name;
    private SortedMap<LocalDateTime, BigDecimal> prices;
    private BigDecimal minPrice;
    private BigDecimal maxPrice;
    private BigDecimal sumPrice;

    // Internal helper method
    private void cleanup() {
        LocalDateTime expireTime = LocalDateTime.now().minusSeconds(60);
        Map<LocalDateTime, BigDecimal> expiredPrices = this.prices.headMap(expireTime);
        for (BigDecimal expiredPrice : expiredPrices.values()) {
            if (this.minPrice.compareTo(expiredPrice) == 0)
                this.minPrice = null;
            if (this.maxPrice.compareTo(expiredPrice) == 0)
                this.maxPrice = null;
            this.sumPrice = this.sumPrice.subtract(expiredPrice);
        }
        expiredPrices.clear(); // Removes expired prices from this.prices
        if (this.minPrice == null && ! this.prices.isEmpty())
            this.minPrice = this.prices.values().stream().min(Comparator.naturalOrder()).get();
        if (this.maxPrice == null && ! this.prices.isEmpty())
            this.maxPrice = this.prices.values().stream().max(Comparator.naturalOrder()).get();
    }

    // other code
}

Instrument 的所有公共方法都必须是 synchronized,并且必须以调用 cleanup() 开始,因为距离之前的任何调用都已过。 addPrice(LocalDateTime, BigDecimal) 方法当然必须更新 3 个统计字段。

为确保统计信息同步,有一个Statistics 类可用作返回值是合适的,因此所有4 个主要统计值(包括从this.prices.size() 获得的count)代表相同一组价格。

【讨论】:

  • 感谢您的解决方案。但是 GET 调用的时间和空间复杂度会是 o(1) 吗?另外我们如何实现#2,因为我们必须维护仪器的名称或一些标识符才能根据仪器名称获取统计信息。提前致谢。
  • 如果我们在 GET 调用时调用 cleanup 方法,那么时间复杂度不会是 O(n),其中 n 是 expiredPrices 的记录数?
  • @CodeMaster 在询问有关复杂性的问题之前,您需要确定n 是什么。对于您的问题,n 将是 total 价格数量。由于清理方法与总价格无关,因此摊销复杂度为 O(1),类似于 ArrayList.add() 的摊销复杂度为 O(1) 即使它有时需要扩展后备数组。
  • @CodeMaster 至于第一条评论,关于#2,请阅读答案的第一行。你认为那张地图的关键是什么?
猜你喜欢
  • 2015-05-25
  • 1970-01-01
  • 1970-01-01
  • 2015-09-21
  • 2020-12-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-03-03
相关资源
最近更新 更多