我正在使用Java编程。每100毫秒,我的程序会得到一个新数字。
它有一个缓存,包含过去n = 180
个数字的历史记录。
当我得到一个新数字x
时,我想计算在缓存中有多少个数字小于x
。
然后,我想删除缓存中最旧的数字。
每100毫秒,我希望重复计算有多少较小的数字并删除最旧的数字。
哪种算法应该使用?我想优化计算速度,因为这不是那100毫秒内唯一要计算的事情。
我正在使用Java编程。每100毫秒,我的程序会得到一个新数字。
它有一个缓存,包含过去n = 180
个数字的历史记录。
当我得到一个新数字x
时,我想计算在缓存中有多少个数字小于x
。
然后,我想删除缓存中最旧的数字。
每100毫秒,我希望重复计算有多少较小的数字并删除最旧的数字。
哪种算法应该使用?我想优化计算速度,因为这不是那100毫秒内唯一要计算的事情。
出于实际考虑和合理的 n
值,您最好使用原始的 int
类型的 环形缓冲区 (以跟踪最旧条目)和 线性扫描(以确定小于 x
的值的数量)。
为了使其达到 O(log n)
,您需要使用类似于 Guava TreeMultiset 这样的东西。以下是它的大致概述。
class Statistics {
private final static int N = 180;
Queue<Integer> queue = new LinkedList<Integer>();
SortedMap<Integer, Integer> counts = new TreeMap<Integer, Integer>();
public int insertAndGetSmallerCount(int x) {
queue.add(x); // O(1)
counts.put(x, getCount(x) + 1); // O(log N)
int lessCount = 0; // O(N), unfortunately
for (int i : counts.headMap(x).values()) // use Guavas TreeMultiset
lessCount += i; // for O(log n)
if (queue.size() > N) { // O(1)
int oldest = queue.remove(); // O(1)
int newCount = getCount(oldest) - 1; // O(log N)
if (newCount == 0)
counts.remove(oldest); // O(log N)
else
counts.put(oldest, newCount); // O(log N)
}
return lessCount;
}
private int getCount(int x) {
return counts.containsKey(x) ? counts.get(x) : 0;
}
}
在我的1.8 GHz笔记本电脑上,这个解决方案大约需要13秒完成100万次迭代(即每次迭代约需要0.013毫秒,远远低于100毫秒)。
将您的数字添加到列表中。如果大小>180,则删除第一个数字。 计数只是迭代180个元素,这可能足够快。从性能上来说很难超越。
您可以尝试使用自定义的链表数据结构,其中每个节点都维护next/prev以及排序后的next/prev引用。然后插入变成了一个两阶段的过程,首先总是将节点插入到尾部,然后进行插入排序,插入排序将返回小于x的数字计数。删除只需删除头部即可。
这里有一个例子,请注意:这是非常糟糕的Java示例代码,它仅仅是为了演示这个想法。你懂的!;) 另外,我只添加了一些项目,但它应该给你一个它如何工作的想法...这种情况下的最坏情况是完全迭代排序链表 - 这不比上面的例子更糟糕吧?
import java.util.*;
class SortedLinkedList {
public static class SortedLL<T>
{
public class SortedNode<T>
{
public SortedNode(T value)
{
_value = value;
}
T _value;
SortedNode<T> prev;
SortedNode<T> next;
SortedNode<T> sortedPrev;
SortedNode<T> sortedNext;
}
public SortedLL(Comparator comp)
{
_comp = comp;
_head = new SortedNode<T>(null);
_tail = new SortedNode<T>(null);
// Setup the pointers
_head.next = _tail;
_tail.prev = _head;
_head.sortedNext = _tail;
_tail.sortedPrev = _head;
_sortedHead = _head;
_sortedTail = _tail;
}
int insert(T value)
{
SortedNode<T> nn = new SortedNode<T>(value);
// always add node at end
nn.prev = _tail.prev;
nn.prev.next = nn;
nn.next = _tail;
_tail.prev = nn;
// now second insert sort through..
int count = 0;
SortedNode<T> ptr = _sortedHead.sortedNext;
while(ptr.sortedNext != null)
{
if (_comp.compare(ptr._value, nn._value) >= 0)
{
break;
}
++count;
ptr = ptr.sortedNext;
}
// update the sorted pointers..
nn.sortedNext = ptr;
nn.sortedPrev = ptr.sortedPrev;
if (nn.sortedPrev != null)
nn.sortedPrev.sortedNext = nn;
ptr.sortedPrev = nn;
return count;
}
void trim()
{
// Remove from the head...
if (_head.next != _tail)
{
// trim.
SortedNode<T> tmp = _head.next;
_head.next = tmp.next;
_head.next.prev = _head;
// Now updated the sorted list
if (tmp.sortedPrev != null)
{
tmp.sortedPrev.sortedNext = tmp.sortedNext;
}
if (tmp.sortedNext != null)
{
tmp.sortedNext.sortedPrev = tmp.sortedPrev;
}
}
}
void printList()
{
SortedNode<T> ptr = _head.next;
while (ptr != _tail)
{
System.out.println("node: v: " + ptr._value);
ptr = ptr.next;
}
}
void printSorted()
{
SortedNode<T> ptr = _sortedHead.sortedNext;
while (ptr != _sortedTail)
{
System.out.println("sorted: v: " + ptr._value);
ptr = ptr.sortedNext;
}
}
Comparator _comp;
SortedNode<T> _head;
SortedNode<T> _tail;
SortedNode<T> _sortedHead;
SortedNode<T> _sortedTail;
}
public static class IntComparator implements Comparator
{
public int compare(Object v1, Object v2){
Integer iv1 = (Integer)v1;
Integer iv2 = (Integer)v2;
return iv1.compareTo(iv2);
}
}
public static void main(String[] args){
SortedLL<Integer> ll = new SortedLL<Integer>(new IntComparator());
System.out.println("inserting: " + ll.insert(1));
System.out.println("inserting: " + ll.insert(3));
System.out.println("inserting: " + ll.insert(2));
System.out.println("inserting: " + ll.insert(5));
System.out.println("inserting: " + ll.insert(4));
ll.printList();
ll.printSorted();
System.out.println("inserting new value");
System.out.println("inserting: " + ll.insert(3));
ll.trim();
ll.printList();
ll.printSorted();
}
}
你可以使用LinkedList实现。
有了这个结构,你可以轻松地操作List的第一个和最后一个元素。 (addFirst,removeFirst,...) 对于算法(查找比某个数小/大的数字数量),只需在列表上简单循环即可,在180个元素的列表上可以在不到100毫秒内得出结果。
180个值不算多,一个简单的数组,采用暴力搜索和System.arraycopy()应该比1微秒(1/1000毫秒)更快,并且不会产生垃圾回收。这可能比使用更复杂的集合更快。
我建议你保持简单,先测量一下所需时间,再考虑是否需要优化。
n
进行重复测试 :-) - aioobe