|
|
PUZZLE 64 BOXES
This is an interesting puzzle from Mikito Harakiri. Imagine that you have
a Cartesian space, and you are filling it with n-dimensional boxes. The
boxes are modeled as shown below:
CREATE TABLE Boxes
(box_id INTEGER NOT NULL PRIMARY KEY,
dimension_nbr INTEGER NOT NULL,
low INTEGER NOT NULL,
high INTEGER NOT NULL);
The problem is to find all the pairs of boxes that intersect (e.g., the
3D cubes):
A = {(x,0,2),(y,0,2),(z,0,2)}
B = {(x,1,3),(y,1,3),(z,1,3)}
C = {(x,10,12),(y,0,4),(z,0,100)}
Boxes A and B intersect, but box C intersects neither A nor B. Bonus
point: is there anything special about this kind of query?
Answer #1
This is from Bob Badour:
SELECT B1.box_id AS box1, B2.box_id AS box2
FROM Boxes AS B1, Boxes AS B2
WHERE B1.box_id < B2.box_id
AND NOT EXISTS (SELECT *
FROM Boxes AS B3, Boxes AS B4
WHERE B3.box_id = B1.box_id
AND B4.box_id = B2.box_id
AND B4.dimension_nbr = B3.dimension_nbr
AND (B4.high < B3.low OR B4.low > B3.high)
)
GROUP BY B1.box_id, B2.box_id;
Mikito Harakiri thought that this was interesting because it is a
generalization of relational division. Now, relational division expressed
in SQL is either a query with two levels of nested subqueries, or one level
of nested subqueries with EXCEPT operators, or the query with counting
subquery in the HAVING clause. Bob’s query is none of those.
SELECT B1.box_id AS box1, B2.box_id AS box2, B1.dim
FROM Boxes AS B1, Boxes AS B2
WHERE B1.low BETWEEN B2.low AND B2.high
AND B1.dim = B2.dim
GROUP BY B1.box_id, B2.box_id
HAVING COUNT(*) = (SELECT COUNT(*)
FROM Boxes AS B3
WHERE B3.box_id = B1.box_id
AND B3.dim = B1.dim);
Answer #2
Try a slightly different approach. Begin with one dimension and
stronger DDL:
CREATE TABLE Boxes
(box_id CHAR (1) NOT NULL,
dim CHAR(1) NOT NULL,
PRIMARY KEY (box_id, dim),
low INTEGER NOT NULL,
high INTEGER NOT NULL,
CHECK (low < high));
INSERT INTO Boxes VALUES ('A', 'x', 0, 2);
INSERT INTO Boxes VALUES ('B', 'x', 1, 3);
INSERT INTO Boxes VALUES ('C', 'x', 10, 12);
--in 1 dimension
SELECT B1.box_id, B2.box_id
FROM Boxes AS B1, Boxes AS B2
WHERE B1.box_id < B2.box_id
AND (B1.high - B1.low) + (B2.high - B2.low)
> ABS(B1.high - B2.low);
This says that two lines segments overlap when their combined
lengths are less than their span in the dimension. I am using math rather
than BETWEEN-ness.
Cubes A={(x,0,2),(y,0,2),(z,0,2)} and B={(x,1,3),(y,1,3),(z,1,3)}
intersect, while box C={(x,10,12),(y,0,4),(z,0,100)}. Let's go to two
dimensions:
INSERT INTO Boxes VALUES ('A', 'y', 0, 2);
INSERT INTO Boxes VALUES ('B', 'y', 1, 3);
INSERT INTO Boxes VALUES ('C', 'y', 0, 4);
--in 2 dimension: first shot:
SELECT B1.box_id, B2.box_id, B1.dim
FROM Boxes AS B1, Boxes AS B2
WHERE B1.box_id < B2.box_id
AND B1.dim = B2.dim
AND (B1.high - B1.low) + (B2.high - B2.low)
ABS(B1.high - B2.low);
Now look for a common area in (x,y) by having overlaps in both
dimensions:
SELECT B1.box_id, B2.box_id
FROM Boxes AS B1, Boxes AS B2
WHERE B1.box_id < B2.box_id
AND B1.dim = B2.dim
AND (B1.high - B1.low) + (B2.high - B2.low) > ABS(B1.high - B2.low)
GROUP BY B1.box_id, B2.box_id
HAVING COUNT(B1.dim) = 2;
--3 dimensions:
INSERT INTO Boxes VALUES ('A', 'z', 0, 2);
INSERT INTO Boxes VALUES ('B', 'z', 1, 3);
INSERT INTO Boxes VALUES ('C', 'z', 0, 100);
Now change the HAVING clause to
COUNT(B1.dim) = 3
or
(SELECT COUNT (DISTINCT dim) FROM Boxes)
if you do not know the dimension of the space you are using.
题目大意:BOXES表描述每个箱子占据的空间(维度,起点坐标,终点坐标),要求列出在空间上有交叠的每一对箱子。
CREATE TABLE Boxes
(box_id CHAR (1) NOT NULL,
dim CHAR(1) NOT NULL,
PRIMARY KEY (box_id, dim),
low INTEGER NOT NULL,
high INTEGER NOT NULL,
CHECK (low < high));
INSERT INTO Boxes VALUES ('A', 'x', 0, 2);
INSERT INTO Boxes VALUES ('B', 'x', 1, 3);
INSERT INTO Boxes VALUES ('C', 'x', 10, 12);
INSERT INTO Boxes VALUES ('A', 'y', 0, 2);
INSERT INTO Boxes VALUES ('B', 'y', 1, 3);
INSERT INTO Boxes VALUES ('C', 'y', 0, 4);
INSERT INTO Boxes VALUES ('A', 'z', 0, 2);
INSERT INTO Boxes VALUES ('B', 'z', 1, 3);
INSERT INTO Boxes VALUES ('C', 'z', 0, 100);
我写完这个答案才发现和作者的#2很像:
SELECT b1.box_id,b2.box_id
FROM Boxes b1, Boxes b2
WHERE b1.box_id < b2.box_id
AND b1.dim = b2.dim
AND b1.high > b2.low
AND b2.high > b1.low
GROUP BY b1.box_id,b2.box_id
HAVING COUNT(*)=3;
如果仅仅是三维也可以这么写:
WITH b AS (
SELECT box_id
,MAX(CASE WHEN dim='x' THEN low END) AS x1
,MAX(CASE WHEN dim='x' THEN high END) AS x2
,MAX(CASE WHEN dim='y' THEN low END) AS y1
,MAX(CASE WHEN dim='y' THEN high END) AS y2
,MAX(CASE WHEN dim='z' THEN low END) AS z1
,MAX(CASE WHEN dim='z' THEN high END) AS z2
FROM boxes
GROUP BY box_id
)
SELECT b1.box_id,b2.box_id
FROM b b1,b b2
WHERE b1.box_id<b2.box_id
AND b1.x2>b2.x1 AND b2.x2>b1.x1
AND b1.y2>b2.y1 AND b2.y2>b1.y1
AND b1.z2>b2.z1 AND b2.z2>b1.z1; |
|