股票统计计算,时间和空间复杂度均为O(1)。

huangapple go评论66阅读模式
英文:

Stock statistics calculation with O(1) time and space complexity

问题

我必须在Java中设计一个REST API,其功能如下:

1)接受以下json的POST请求:

{
  "instrument": "ABC",
  "price": "200.90",
  "timestamp": "2018-09-25T12:00:00"
}

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

2)将有一个GET API,用于返回最近60秒内收到的特定仪器记录的统计信息。GET请求格式如下:/statistics/{instrumentName} 例如:/statistics/ABC。响应如下所示:

{
  "count": "3",
  "min": "100.00",
  "max": "200.00",
  "sum": "450.00",
  "avg": "150.00"
}

3)还将有另一个GET请求/statistics,用于返回最近60秒内收到的所有仪器的统计信息(不特定于特定的仪器,类似于#2)。

使得这个算法复杂的是GET调用应该在O(1)的时间和空间复杂度内执行。

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

但我无法理解如何解决问题2#,在O(1)的时间和空间复杂度内获取特定仪器/statistics/ABC在最近60秒的统计信息。

对于清理比60秒旧的记录,最好的策略是什么?

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

英文:

I have to design a rest API in Java which :-

  1. Accepts a POST request with the below json :-

    {
    "instrument": "ABC",
    "price": "200.90",
    "timestamp" : "2018-09-25T12:00:00"
    }

these records would be saved in an in memory collection and not any kind of database.

  1. There would be a GET API which returns the statistics of the specific instrument records received in the last 60 seconds. The GET request would be :- /statistics/{instrumentName} Ex :- /statistics/ABC . The response looks as mentioned below :-

    {
    "count" : "3"
    "min" : "100.00"
    "max" : "200.00"
    "sum" : "450.00"
    "avg" : "150.00"
    }

  2. There would be another GET request /statistics which returns the statistics of all the instruments that was received in the last 60 seconds ( Not specific to particular instrument like #2 )

What makes this algorithm complex to implement is that the GET call should be executed - O(1) time and space complexity.

The approach which I have thought for 3# is to have a collection which will have 60 buckets ( since we have to calculate for past 60 secs so sampling per 1 sec). Every time the transaction comes in it will go to specific bucket depending on the key i.e. hour-min-sec ( it would be a map with this key and the statistics for that sec ) .

But what I am not able to understand is how to address the problem 2# where we have to get the statistics of specific instrument /statistics/ABC for last 60 sec in O(1) time and space complexity.

What could be the best strategy to clean up records which are older than 60 secs?

Any help with the algorithm will be appreciated.

答案1

得分: 1

将数据存储在一个 Map<String, Instrument> 中,并使类的结构如下:

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

    // 内部辅助方法
    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(); // 从 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();
    }

    // 其他代码
}

Instrument 的所有公共方法都必须是 synchronized,并且必须以调用 cleanup() 开头,因为与上一次调用相比,时间已经过去了。当然,addPrice(LocalDateTime, BigDecimal) 方法必须更新这3个统计字段。

为了确保统计数据同步,最好有一个名为 Statistics 的类,可以将其用作返回值,以便所有4个主要统计值(包括从 this.prices.size() 获得的 count 值)表示相同的价格集合。

英文:

Store the data in a Map&lt;String, Instrument&gt;, and have the class look like this:

class Instrument {
    private String name;
    private SortedMap&lt;LocalDateTime, BigDecimal&gt; prices;
    private BigDecimal minPrice;
    private BigDecimal maxPrice;
    private BigDecimal sumPrice;

    // Internal helper method
    private void cleanup() {
        LocalDateTime expireTime = LocalDateTime.now().minusSeconds(60);
        Map&lt;LocalDateTime, BigDecimal&gt; 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 &amp;&amp; ! this.prices.isEmpty())
            this.minPrice = this.prices.values().stream().min(Comparator.naturalOrder()).get();
        if (this.maxPrice == null &amp;&amp; ! this.prices.isEmpty())
            this.maxPrice = this.prices.values().stream().max(Comparator.naturalOrder()).get();
    }

    // other code
}

All the public methods of Instrument must be synchronized and must start with a call to cleanup(), since time has elapsed since any previous call. The addPrice(LocalDateTime, BigDecimal) method must of course update the 3 statistics fields.

To ensure statistics are in sync, it would be appropriate to have a Statistics class that can be used as return value, so all 4 main statistics values (incl. count obtained from this.prices.size()) represent the same set of prices.

huangapple
  • 本文由 发表于 2020年9月12日 20:36:16
  • 转载请务必保留本文链接:https://go.coder-hub.com/63860391.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定