问题描述
我需要一种快速的方法来在间隔重叠时找到最大值,这与找到重叠最多的点不同,那就是“顺序”。我将有int[][] data
中的int[]
中有2个值,其中第一个数字是中心,第二个数字是半径,越靠近中心,该点的值将越大。例如,如果我得到如下数据:
int[][] data = new int[][]{
{1,1},{3,3},{2,4}};
然后在数字行上,它是这样的:
x axis: -2 -1 0 1 2 3 4 5 6 7
1 1: 1 2 1
3 3: 1 2 3 4 3 2 1
2 4: 1 2 3 4 5 4 3 2 1
因此,要使我的点的值尽可能大,我需要选择点x = 2,这得出的总值为1 + 3 + 5 = 9,这是最大可能的值。有没有办法快速做到这一点?像O(n)或O(nlogn)的时间复杂度
解决方法
这可以通过简单的O(n log n)算法来完成。
考虑值函数v(x),然后考虑其离散导数dv(x)= v(x)-v(x-1)。假设您只有一个间隔,例如{3,3}
。 dv(x)从-infinity至-1为0,然后从0至3为1,然后从4至6为-1,然后从7至infinity为0。也就是说,导数在-1之后紧随其后变化1,在3之后紧随-2以及6之后紧随其后变化1。
对于n个间隔,存在3 * n个导数更改(其中一些可能在同一点发生)。因此,找到所有派生更改(x,change)
的列表,按它们的x对其进行排序,然后遍历集合。
看哪:
intervals = [(1,1),(3,3),(2,4)]
events = []
for mid,width in intervals:
before_start = mid - width - 1
at_end = mid + width
events += [(before_start,(mid,-2),(at_end,1)]
events.sort()
prev_x = -1000
v = 0
dv = 0
best_v = -1000
best_x = None
for x,change in events:
dx = x - prev_x
v += dv * dx
if v > best_v:
best_v = v
best_x = x
dv += change
prev_x = x
print best_x,best_v
还有Java代码:
TreeMap<Integer,Integer> ts = new TreeMap<Integer,Integer>();
for(int i = 0;i<cows.size();i++) {
int index = cows.get(i)[0] - cows.get(i)[1];
if(ts.containsKey(index)) {
ts.replace(index,ts.get(index) + 1);
}else {
ts.put(index,1);
}
index = cows.get(i)[0] + 1;
if(ts.containsKey(index)) {
ts.replace(index,ts.get(index) - 2);
}else {
ts.put(index,-2);
}
index = cows.get(i)[0] + cows.get(i)[1] + 2;
if(ts.containsKey(index)) {
ts.replace(index,ts.get(index) + 1);
}else {
ts.put(index,1);
}
}
int value = 0;
int best = 0;
int change = 0;
int indexBefore = -100000000;
while(ts.size() > 1) {
int index = ts.firstKey();
value += (ts.get(index) - indexBefore) * change;
best = Math.max(value,best);
change += ts.get(index);
ts.remove(index);
}
其中cows
是数据
嗯,一般的O(n log n)
或更棘手,可能可以通过线性编程解决,但这会变得相当复杂。
经过一些争论,我认为可以通过线相交和函数求和(以线段相交表示)来解决。基本上,将每个都视为直线顶部的三角形。如果输入为(C,R)
,则三角形以C
为中心,半径为R
。线上的点是C-R (value 0)
,C (value R)
和C+R (value 0)
。三角形的每个线段代表一个值。
考虑任何2个这样的“三角形”,最大值出现在2个位置之一:
- 其中一个三角形的峰值
- 三角形的交点或两个三角形整体的交点。多个三角形仅意味着更多可能的相交点,可悲的是可能的相交数呈平方增长,因此,除非有相交的数量为{{1 }}或更少。
要找到所有相交点,我们可以为此使用标准算法,但是我们需要以一种特定的方式进行修改。我们需要添加一条线,该线从每个峰延伸得足够高,因此它将比任何线都高,因此基本上是从(C,C)到O(N log N)
。然后,我们运行该算法,输出敏感的相交查找算法为O(N)
,其中(C,Max_R)
是相交的数量。可悲的是,它可能高达O(N log N + k)
(考虑大小写k
,依此类推,直到O(N^2)
。每条线都会与其他每条线相交。一旦有了(1,100),100)...
交集在每个交叉点,您都可以通过对队列中所有点的求和来计算该值。运行总和可以保留为缓存值,因此它只更改(50,100)
次,尽管可能不可行。改为O(N + K)
。将其设置为潜在的O(K)
(在O(N*K)
最坏的情况下)代替:(。尽管这似乎是合理的。对于每个交叉点,您需要总结为O(N^3)
行可以获取该点的价值,尽管在实践中,可能会获得更好的性能。
考虑到您的目标是最大化而不是仅仅找到交叉点,可以进行一些优化。可能会有一些不值得追求的交叉路口,但是,我也看到了这样的情况:距离太近了,无法将其削减。让我想起了凸包。在许多情况下,您可以轻松地减少90%的数据,但在某些情况下,您会看到最差的结果(每个点或几乎每个点都是船体点)。例如,在实践中,肯定有一些原因可以确保您的总和将小于当前的已知最大值。
另一种优化方法可能是构建间隔树。