我有一组由y1和y2坐标定义的垂直区域,其中y1是起点,y2是每个区域的终点。我的坐标系的原点是左上角,所以y2总是大于y1。
这是一个例子:
var regions = [
[10, 100],
[50, 120],
[60, 180],
[140, 220]
];
我想找出大于一定大小的所有脱节交叉点,比方说,例如,20个单位。
到目前为止,我只能获得所有交叉点:[50, 100],[60, 100],[60, 120],[140, 180]
但是我期待这个结果:[60, 100],[140, 180]
。
有没有算法来获得这个结果?
答案 0 :(得分:1)
我相信问题很清楚。它在给定的垂直边缘范围数组中请求脱节的交叉点。这意味着
[[10, 100], [50, 120], [60, 180], [140, 220]]
数组为我们提供了两个脱节的交集1,来自[10, 100], [50, 120], [60, 180]
,结果为[60,100]
,另一个来自[60, 180], [140, 220]]
,结果为[140,180]
。因此,当您注意到结果交叉[60,100]
和[140,180]
时,从这组给定的垂直边缘获得的交叉点是脱节的。
在JS中实现此功能的一种方法如下:
function getDisjointedIntersections(a){
var di;
return a.reduce((p,c,i,a) => c.used ? p
: (p.push(a.map((_,j) => a[(i+j)%a.length])
.reduce((s,e) => (di = [Math.max(s[0],e[0]), Math.min(s[1],e[1])],
di[0] < di[1] ? (e.used = true, di) : s))),p),[]);
}
var regions = [[10, 100],[50, 120],[60, 180],[140, 220],[150, 330]];
console.log(getDisjointedIntersections(regions));
说明:首先,很容易将这项工作视为一项简单的减少工作,但我猜不是。在某些情况下,您应该考虑以前的垂直边缘,因此可能需要多次传递。
因此,我们将从给定的数组开始,并尽可能地将垂直边缘减少到它们的交点。在执行此操作时,将为每个导致成功交叉的垂直边缘分配名为used
且值为true
的属性。一旦完成此传递,我们将向上旋转输入数组,直到未使用的垂直边缘到达索引位置0并再次开始通过交叉减少垂直边缘,但这次是从之前未使用的项目开始。
所以代码的核心,交叉减速器是
.reduce((s,e) => (di = [Math.max(s[0],e[0]), Math.min(s[1],e[1])],
di[0] < di[1] ? (e.used = true, di) : s))
这是一个没有初始化的简单缩减。它首先将交集分配给di
。
di = [Math.max(s[0],e[0]), Math.min(s[1],e[1])]
然后如果交集成功则将当前元素标记为已使用并返回di
,这将是下一个缩减周期的前一个元素。如果交叉点不成功,那么它将返回上一个交点到下一个减少周期。
di[0] < di[1] ? (e.used = true, di) : s))
好的,我们已经完成了一个周期,只有[10, 100], [50, 120], [60, 180]
已经交叉并产生[60,100]
。因此我们将此结果推送到外部减少初始数组。
(p.push(a.map((_,j) => a[(i+j)%a.length])
.reduce((s,e) => ... // the code that we already know
但是我们链接到的地图函数是什么减少到的。每个外部reduce的迭代,地图将我们的输入数组旋转一个索引。好看看它,你会明白它是如何做的
a.reduce((p,c,i,a) => c.used ? p
: (p.push(a.map((_,j) => a[(i+j)%a.length])
.redu...
但是你也会注意到我们并没有浪费轮换。我们先等待,直到我们的外部reduce的当前元素(c
)遇到未使用的项目。只有这样我们才能旋转输入数组以形成新的输入数组。例如,我们必须处理这个例子的两个输入数组是。
[ [ 10, 100 ],[ 50, 120 ],[ 60, 180 ],[ 140, 220 ],[ 150, 330 ] ]
和
[ [ 140, 220 ],[ 150, 330 ],[ 10, 100, used: true ],[ 50, 120, used: true ],[ 60, 180, used: true ] ]
因此,在这种特殊情况下,只有两次通过标记所有垂直边缘,并为我们提供所有两个断开的交叉点......无论您将输入数组洗牌多少次。
计算15~16msecs的1000项输入数据和400-500msecs的10000项数据。您可以检查代码并使用参数here
进行播放确定以下版本被修改为仅显示允许的大小脱节交叉点。您可能希望here尝试不同数量的垂直边,轴大小和脱节交叉点的最小尺寸。
function getDisjointedIntersections(a,n){
var di, // disjointed intersections
ri; // resulting intersections
return a.reduce(function(p,c,i,a){
if (c.used) return p;
ri = a.map((_,j) => a[(i+j)%a.length])
.reduce(function(s,e){
di = [Math.max(s[0],e[0]), Math.min(s[1],e[1])];
return di[0] < di[1] ? (e.used = true, di) : s;
});
ri[1] - ri[0] > n && p.push(ri);
return p;
},[]);
}
var regions = [[10, 100],[50, 120],[60, 180],[140, 220],[150, 330]];
console.log(getDisjointedIntersections(regions,20));
答案 1 :(得分:0)
正如@MBo所说,选择交叉点[60, 100],[140, 180]
似乎不是一个明确的原因,因为所有交叉点([50, 100],[60, 100],[60, 120],[140, 180]
)显然都超过20.但如果由于某种原因你和#39;仍然希望在这里获得大于特定大小的交叉点&#39;过程:
sort regions by the lower bound of each element
for (i = 0; i < len; i++)
for (j = i+1; j < len; j++)
if the ith and jth region overlap and resultingSize > theSizeYouWant
add to the list of valid intersections // or whatever you want
else break
其中len
是regions
的长度(因此在您给出len = 4
的示例中)。这里有一些python执行上述操作(这是我知道的最简单的方法,可以按一组列表的索引进行排序)
from operator import itemgetter
regions = [
[10, 100],
[50, 120],
[60, 180],
[140, 220]
]
# sort regions by the lower bound of each element
# (already done in your test case)
regions = sorted(regions, key=itemgetter(0))
# the size to compare to
size = 20
# list of valid intersections
l = []
# for (i = 0; i < len(regions)-1; i++)
for i in range(len(regions)-1):
# for (j = i; j < len(regions)-1; j++)
for j in range(i+1,len(regions)):
# make sure regions overlap and if they do that they are at least size
if (regions[i][1]>regions[j][0] and regions[i][1]-regions[j][0]>size):
# add intersection to l
l.append(str(regions[j][0])+"-"+str(regions[i][1]))
# if not then there's no need to look at the others
else:
break
# print list of valid intersections
print(l)
输出:
['50-100', '60-100', '60-120', '140-180']
对于
的不同输入regions = [
[70, 100],
[50, 120],
[80, 180],
[150, 220]
]
输出:
['70-120', '80-120', '150-180']
如果这是你已经做过的那件小事我很抱歉,但也许有些事情有用......
答案 2 :(得分:0)
您可以使用简化算法并使用叉积来搜索重叠项目。然后通过迭代和过滤未知匹配来获得公共部分。
var regions = [[10, 100], [50, 120], [60, 180], [140, 220]],
hash = Object.create(null),
result = regions.reduce(function (r, a) {
var temp = regions.reduce(function (s, b) {
var min = Math.max(s[0], b[0]), max = Math.min(s[1], b[1]);
return min < max ? [min, max] : s;
}, a),
key = temp.join('|');
if (key && !hash[key]) {
hash[key] = true;
r.push(temp);
}
return r;
}, []);
console.log(result);