|
|
40 PERMUTATIONS 排列组合
题目大意:求出 N 个数的全排列
CREATE TABLE Elements
(i INTEGER NOT NULL PRIMARY KEY);
INSERT INTO Elements VALUES (1);
INSERT INTO Elements VALUES (2);
INSERT INTO Elements VALUES (3);
INSERT INTO Elements VALUES (4);
INSERT INTO Elements VALUES (5);
INSERT INTO Elements VALUES (6);
INSERT INTO Elements VALUES (7);
Answer #1
The obvious and horrible answer is:
SELECT E1.i, E2.i, E3.i, E4.i, E5.i, E6.i, E7.i
FROM Elements AS E1, Elements AS E2, Elements AS E3,
Elements AS E4, Elements AS E5, Elements AS E6,
Elements AS E7
WHERE E1.i NOT IN (E2.i, E3.i, E4.i, E5.i, E6.i, E7.i)
AND E2.i NOT IN (E1.i, E3.i, E4.i, E5.i, E6.i, E7.i)
AND E3.i NOT IN (E1.i, E2.i, E4.i, E5.i, E6.i, E7.i)
AND E4.i NOT IN (E1.i, E2.i, E3.i, E5.i, E6.i, E7.i)
AND E5.i NOT IN (E1.i, E2.i, E3.i, E4.i, E6.i, E7.i)
AND E6.i NOT IN (E1.i, E2.i, E3.i, E4.i, E5.i, E7.i)
AND E7.i NOT IN (E1.i, E2.i, E3.i, E4.i, E5.i, E6.i);
Answer #2
An improvement on this query can be made by adding one more
predicate to the WHERE clause:
SELECT E1.i, E2.i, E3.i, E4.i, E5.i, E6.i, E7.i
FROM Elements AS E1, Elements AS E2, Elements AS E3,
Elements AS E4, Elements AS E5, Elements AS E6,
Elements AS E7
WHERE (E1.i + E2.i + E3.i + E4.i + E5.i + E6.i + E7.i)
= 28
AND E1.i NOT IN (E2.i, E3.i, E4.i, E5.i, E6.i, E7.i)
AND E2.i NOT IN (E1.i, E3.i, E4.i, E5.i, E6.i, E7.i)
AND E3.i NOT IN (E1.i, E2.i, E4.i, E5.i, E6.i, E7.i)
AND E4.i NOT IN (E1.i, E2.i, E3.i, E5.i, E6.i, E7.i)
AND E5.i NOT IN (E1.i, E2.i, E3.i, E4.i, E6.i, E7.i)
AND E6.i NOT IN (E1.i, E2.i, E3.i, E4.i, E5.i, E7.i)
AND E7.i NOT IN (E1.i, E2.i, E3.i, E4.i, E5.i, E6.i);
作者说加上第一个谓词判断可以快速过滤掉一些组合,但我在ORACLE中比较结果并无改善。
Answer #3
But let's carry the totals trick one step further. First, redefine the
Elements table to have a weight for each element in the set:
CREATE TABLE Elements
(i INTEGER NOT NULL
,wgt INTEGER NOT NULL);
INSERT INTO Elements VALUES (1, 1) ;
INSERT INTO elements VALUES (2, 2) ;
INSERT INTO elements VALUES (3, 4) ;
INSERT INTO elements VALUES (4, 8) ;
INSERT INTO elements VALUES (5, 16);
INSERT INTO elements VALUES (6, 32);
INSERT INTO elements VALUES (7, 64);
SELECT E1.i, E2.i, E3.i, E4.i, E5.i, E6.i, E7.i
FROM Elements AS E1, Elements AS E2, Elements AS E3,
Elements AS E4, Elements AS E5, Elements AS E6,
Elements AS E7
WHERE (E1.wgt + E2.wgt + E3.wgt + E4.wgt
+ E5.wgt + E6.wgt + E7.wgt) = 127;
加上了以2的N次幂作为权重,为了快速判断各元素仅出现一次。
听起来不错,不知为何在ORACLE中行不通,SQL计划的成本和逻辑读比上个答案增加了十几倍!
利用这个加权的思路,我重新写了一种解法,把7表连接改为两两连接,每次都尽量过滤掉一些数据:
WITH r2 AS (
SELECT e1.i i1,e2.i i2,e1.wgt + e2.wgt as wgt
FROM Elements e1, Elements e2
WHERE e1.i<>e2.i
)
,r3 AS (
SELECT r2.i1,r2.i2,e.i i3,r2.wgt+e.wgt as wgt
FROM r2,Elements e
WHERE e.i NOT IN (r2.i1,r2.i2)
)
,r4 AS (
SELECT r21.i1,r21.i2,r22.i1 i3,r22.i2 i4,r21.wgt+r22.wgt as wgt
FROM r2 r21,r2 r22
WHERE BITAND(r21.wgt,r22.wgt)=0
)
SELECT r3.i1,r3.i2,r3.i3,r4.i1 i4,r4.i2 i5,r4.i3 i6,r4.i4 i7
FROM r3,r4
WHERE BITAND(r3.wgt,r4.wgt)=0
;
Answer #4
为了在ORACLE中实现这个答案,我们必须先写一个自定义函数,它的功能是在一个字符串中的指定位置塞入另一个串:
CREATE OR REPLACE FUNCTION STUFF(
p_str IN VARCHAR2
,p_pos IN NUMBER
,p_len IN NUMBER
,p_new IN VARCHAR2
) RETURN VARCHAR2
AS
BEGIN
RETURN SUBSTR(p_str,1,p_pos-1)||p_new||SUBSTR(p_str,p_pos+p_len);
END STUFF;
/
在本例中,p_new总是为NULL, 实际上是在指定位置删除一个字符。
答案#4:
SELECT a || c
FROM (SELECT a || SUBSTR(c , i , 1) a,
STUFF(c, i, 1, '') c
FROM Elements,
(SELECT a || SUBSTR(c , i , 1) a,
STUFF(c, i, 1, '') c
FROM Elements,
(SELECT a || SUBSTR(c , i , 1) a,
STUFF(c, i, 1, '') c
FROM Elements,
(SELECT a || SUBSTR(c , i , 1) a,
STUFF(c, i, 1, '') c
FROM Elements,
(SELECT a || SUBSTR(c, i, 1) a,
STUFF(c,i, 1, '') c
FROM Elements,
(SELECT SUBSTR('1234567', i, 1) a,
STUFF('1234567',i, 1, '') c
FROM Elements
WHERE i <= 7) T1
WHERE i <= 6) T2
WHERE i <= 5) T3
WHERE i <= 4) T4
WHERE i <= 3) T5
WHERE i <= 2) T6;
它也是把7表连接改为两两连接,但是很巧妙,每次连接中总有一个集合很小。
Answer #5
其实和上一种一样,但是我觉得可读性更差。
SELECT SUBSTR('1234567', a, 1) ||
SUBSTR(STUFF('1234567', a, 1, ''), b, 1) ||
SUBSTR(STUFF(STUFF('1234567', a, 1, ''), b, 1,''), c,1) ||
SUBSTR(STUFF(STUFF(STUFF('1234567',a, 1, ''), b, 1, ''), c, 1, ''), d, 1) ||
SUBSTR(STUFF(STUFF(STUFF(STUFF('1234567',a, 1, ''), b, 1, ''), c, 1, ''), d, 1, ''), e, 1) ||
SUBSTR(STUFF(STUFF(STUFF(STUFF(STUFF('1234567',a, 1, ''), b, 1, ''), c, 1, ''), d, 1, ''), e, 1,''),f, 1) ||
STUFF(STUFF(STUFF(STUFF(STUFF(STUFF('1234567',a, 1, ''), b, 1, ''), c, 1, ''), d, 1, ''), e, 1,''),f, 1, '')
FROM (SELECT i a
FROM Elements
WHERE i <= 7) T1,
(SELECT i b
FROM Elements
WHERE i <= 6) T2,
(SELECT i c
FROM Elements
WHERE i <= 5) T3,
(SELECT i d
FROM Elements
WHERE i <= 4) T4,
(SELECT i e
FROM Elements
WHERE i <= 3) T5,
(SELECT i f
FROM Elements
WHERE i <= 2) T6;
最后是我自己的方法,得力于10G的NOCYCLE:
SELECT SYS_CONNECT_BY_PATH(i,',')
FROM Elements
WHERE LEVEL=7
CONNECT BY NOCYCLE i <> PRIOR i; |
|