这个问题很简单,但是要想出一个不是数百行的斯法加蒂else-ifs解决方案就很难了。
有2个范围可能有边界,也可能没有边界。如果有边界,它们可能是包含边界的。
伪代码:
struct bound
{
float val
bool inclusive
}
struct range
{
optional<bound> lower_bound
optional<bound> upper_bound
}
例如,<3, 5]
(数字> 3和<= 5)将是range: { lower_bound: {3, false}, upper_bound: {5, true} }
,而[8,
(数字> = 8)将是range: { lower_bound: { 8, true }, upper_bound: null }
给出2个范围,决定第一个范围与第二个范围的关系(相同,子集,超集,相交,不交集)(上面的示例范围是不交集的,因为任何值都不能都是<= 5和> = 8)
enum relation { identical, subset, superset, intersect, disjoint }
relation_first_to_second(range first, range second): relation
{
// ???
}
问题在于,在编写函数时必须小心处理所有可能的极端情况,我制作了一个展示这些情况的示例图表(-
表示没有限制)。我们可以假设范围始终是有效的,换句话说,我们可以假设(如果存在)A始终小于或等于B,X始终小于或等于Y(但我们不能假定以下两者之间的关系: XA / YB / XB / Y)。
lower upper lower upper relation first to second
- - - - identical
- - - Y superset
- - X - superset
- - X Y superset
- B - - subset
- B - Y B=Y: identical, B<Y: subset, B>Y superset
- B X - B<X: disjoint, B>=X: intersect
- B X Y B<X: disjoint, X<=B<=Y: intersect, B>Y: superset
A - - - subset
A - - Y A<=Y: intersect, A>Y: disjoint
A - X - A=X: identical, A<X: superset, A>X: subset
A - X Y A>Y: disjoint, X<=A<=Y: intersect, A<X: superset
A B - - subset
A B - Y Y<A: disjoint, A<=Y<=B: intersect, Y>B: subset
A B X - X<A: subset, A<=X<=B: intersect, X>B: disjoint
A B X Y this will be complex to check...
请注意,此图表未考虑inclusive
。
如果您有更好的实现范围的想法-不用客气,只是不要假设<=3
与<4
是相同的,并且不要依赖未绑定范围可以使用整数的最小值/最大值(实际问题域更为通用)。我想不出任何更好的表示形式,而该类型由可能存在或不存在的2个边界(每个边界存储值以及是否包含)组成。
我正在寻找实现relation_first_to_second
函数的代码(任何语言或伪代码)越短越好。这不是一个代码问题,但是我认为“最短的代码解决方案是最好的答案”与这个问题非常相关。
答案 0 :(得分:2)
您对范围的表示是不完整的,因为它不允许人们区分正无穷大和负无穷大,因此将空范围表示为(+∞,-∞)。将+∞和-∞都添加到允许值列表中后,所有这些值突然都就位。
您现在可以比较端点并找到最小和最大的端点。如果两个端点具有相同的有限坐标并且在包含性上有所不同,则将其最小值和最大值都视为排他性会很方便。
现在,两个范围r1和r2的交集将是从[{'id': 1,
'indicator': [{'id': 4, 'name': 'Ind1_1'},
{'id': 5, 'name': 'Ind1_2'},
{'id': 6, 'name': 'Ind1_3'}],
'name': 'Res1',
'type': 'resource'},
{'id': 2,
'indicator': [{'id': 7, 'name': 'Ind2_1'},
{'id': 8, 'name': 'Ind2_2'},
{'id': 9, 'name': 'Ind2_3'},
{'id': 10, 'name': 'Ind2_4'}],
'name': 'Res2',
'type': 'service'},
{'id': 3,
'indicator': [{'id': 11, 'name': 'Ind3_1'},
{'id': 12, 'name': 'Ind3_2'},
{'id': 13, 'name': 'Ind3_3'},
{'id': 14, 'name': 'Ind3_4'}],
'name': 'Res3',
'type': 'service'}]
到max(r1.left, r2.left)
的范围。此结果可以为空,等于min(r1.right, r2.right)
,等于r1
,等于或两者都不是。这对应于您的不相交,子集,超集,相同和相交关系。
注意:两个空范围始终是相同的和不相交,但是它们的表示可能不等于等于(空范围有很多表示)。如果希望“相同”谓词在空范围内正常工作,则需要单独检查。
这里有些草率地拼凑了Haskell:
r2
添加data Point = MinusInfinity |
Finite Double Bool |
PlusInfinity deriving (Eq, Ord, Show)
data Range = Range { left :: Point , right :: Point } deriving (Eq, Show)
min', max' :: Point -> Point -> Point
min' (Finite p1 i1) (Finite p2 i2) | p1 == p2 = Finite p1 (i1 && i2)
min' z1 z2 = min z1 z2
max' (Finite p1 i1) (Finite p2 i2) | p1 == p2 = Finite p1 (i1 && i2)
max' z1 z2 = max z1 z2
intersection r1 r2 = Range (max' (left r1) (left r2)) (min' (right r1) (right r2))
等是不重要的练习。
答案 1 :(得分:1)
让我们解决两个范围都对某种假设的命令式/面向对象语言具有边界的情况。
如果您愿意,假设我们已实现绑定比较以返回-1,0,1(使用飞船运算符<=>
或由compare()
函数代替)。
bound self <=> bound y
return (self.val == y.val)
? (self.inclusive <=> y.inclusive) // assume that false < true
: (self.val <=> y.val);
现在,如果我们进行测试:
ax = a <=> x;
by = b <=> y;
If ax < by then ab is superset of xy;
If ax > by then ab is subset of xy;
If ax + by == 0 then ab equals xy;
然后还有交集和分离的情况,如果没有其他测试我们就无法决定
bx = b <=> x;
ay = a <=> y;
If bx == ay then ab and xy are disjoint;
Else ab and xy intersect;
我想我们对于有限范围不能做太多事情。
现在,如果您引入+/-无限val
或+/-无限边界,问题就解决了。
当然,对于无限边界,这意味着您必须处理所有比较,这可以通过<=>
运算符重载或对于非静态类型语言的双重分派来实现,但这是一项容易的任务。
如果您坚持使用不能区分-infinity和+ infinity的表示形式,那么您将无法轻松进行比较b <=> x
和a <=> y
,而不会引入其他if ...