나선형 모양의 수열 문제(재도전) 0 10 4,544

by 손님 수열 나선형 [2012.07.20 14:44:01]


예전에 출제 했던 퀴즈 문제를 다시 내 봅니다.
(당시에도 쿼리를 만든 사람이 1명 밖에 없었죠.)
-----------------------------------------------------------------------



아래와 같이 나선형 모양(right helix)의 숫자집합이 있습니다.

(여기서는 N=5 일 때를 보여줍니다.)

1 2 3 4 5 -- line 1

16 17 18 19 6 -- line 2

15 24 25 20 7 -- line 3

14 23 22 21 8 -- line 4

13 12 11 10 9 -- line 5

위 숫자들 오른쪽에 해당 숫자가 몇 번째 라인에 속하는지 표시해 놓았습니다.

여기서 문제는 숫자집합이 위의 형태일때,

숫자가 1,2,3, .... 순차적으로 증가할 때, 각 숫자가 순서대로 몇번째 라인에

속하는지를 나타내는 쿼리를 만드는 것입니다.

위의 경우에 이 라인번호를 순서대로 출력해 보면,

(당연히, N*N 개 행이 출력되어야 합니다.)

1 -- 번호 1 은 1 라인에..

1 -- 번호 2 은 1 라인에..

1 -- 번호 3 은 1 라인에..

1 -- 번호 4 은 1 라인에..

1 -- 번호 5 은 1 라인에..

2 -- 번호 6 은 2 라인에..

3 -- 번호 7 은 3 라인에..

4 -- 번호 8 은 4 라인에..

5

5

5

5

5

4

3

2

2

2

2

3

...

...

이런식이 되겠죠.

일반적으로 변수 N(N*N 개 행이 출력)이 주어졌을 때, 라인 넘버를 찍어주는 쿼리를 찾는 것입니다.

도전해 보시길...

by 장비 [2012.07.24 13:18:20]

나름 해봤어요 ,n 이 홀수일때만 가정하고 만들었는데 짝수도 되네요

SELECT MAX(DECODE( J ,1, NUM)) AS C1
  ,  MAX(DECODE( J ,2, NUM)) AS C2
   ,  MAX(DECODE( J ,3, NUM)) AS C3
    ,  MAX(DECODE( J ,4, NUM)) AS C4
,  MAX(DECODE( J ,5, NUM)) AS C5
  ,  MAX(DECODE( J ,6, NUM)) AS C6
   ,  MAX(DECODE( J ,7, NUM)) AS C7
    ,  MAX(DECODE( J ,8, NUM)) AS C8
,  MAX(DECODE( J ,9, NUM)) AS C9
  FROM (  
SELECT A.*
  ,  DECODE( MOD( LLV,4) , 1 , LV ,2, Lv+RK , 3, :N-LV+1, 0, :N-RK-LV+1) I 
  ,  DECODE( MOD( LLV,4) , 1 , RK+LV-1 ,2, :N-LV+1 , 3, :N-RK-LV+1, 0,LV) J
FROM (
    SELECT MIN(LV) LV
   ,  MIN(LLV) LLV
   , MIN(S) S
   , NUM
   , RANK() OVER ( PARTITION BY MIN(S) ORDER BY NUM ASC)  RK
FROM    (
    SELECT ceil( LV/4) LV
,  LV  LLV
, SUM(VAL) OVER ( PARTITION BY T ORDER BY LV ASC)   as s
   FROM (  
    SELECT LEVEL AS LV
, :N- MOD(LEVEL-1,2) - FLOOR((LEVEL-1)/2) AS VAL  
, 1 AS T
  FROM DUAL CONNECT  BY LEVEL < 2 * :N
)
  ) A
  ,(  SELECT LEVEL NUM FROM DUAL CONNECT BY LEVEL <=  :N*:N ) B
  WHERE     A.S >= B.NUM
    GROUP BY NUM
    ORDER BY NUM
) A
   )
   GROUP BY  I
   ORDER BY I

by 손님 [2012.07.24 23:10:59]

잘 하셨습니다.

그런데, 인라인뷰 사용 횟수도 줄이고 쿼리도 더 줄여 보세요.
가능합니다.


by 손님 [2012.07.26 16:07:50]
SELECT
MAX(DECODE( J ,1 , NUM)) AS C1
,MAX(DECODE( J ,2 , NUM)) AS C2
,MAX(DECODE( J ,3 , NUM)) AS C3
,MAX(DECODE( J ,4 , NUM)) AS C4
,MAX(DECODE( J ,5 , NUM)) AS C5
,MAX(DECODE( J ,6 , NUM)) AS C6
,MAX(DECODE( J ,7 , NUM)) AS C7
,MAX(DECODE( J ,8 , NUM)) AS C8
,MAX(DECODE( J ,9 , NUM)) AS C9
  FROM (  
SELECT A.*
  ,  DECODE( MOD( LLV,4) , 1 , LV ,2, Lv+RK , 3, :N-LV+1, 0, :N-RK-LV+1) I 
  ,  DECODE( MOD( LLV,4) , 1 , RK+LV-1 ,2, :N-LV+1 , 3, :N-RK-LV+1, 0,LV) J
FROM (
    SELECT A.LV, A.LLV, ROWNUM AS NUM, B.LV AS RK
FROM  
    (  SELECT CEIL(LEVEL/4)  AS LV
    ,  LEVEL  AS LLV
    , CEIL(:N - LEVEL/2) AS CNT
  FROM DUAL
CONNECT BY LEVEL < 2* :N ) A
   , ( SELECT LEVEL AS LV FROM DUAL CONNECT BY LEVEL <= :N*:N)  B
WHERE  A.CNT >= B.LV
) A
   )
   GROUP BY  I
   ORDER BY I

by 장비 [2012.07.26 16:12:10]
처음에는 생각한 알고리즘을 순서대로 데이터를 만들생각이였고

두번째는 처음과 결과를 같게 만들 수 있게 생각했어요

by 장비 [2012.07.26 16:26:44]
추가 할게요 

 ( SELECT LEVEL AS LV FROM DUAL CONNECT BY LEVEL <= :N*:N)  B

이거 대신에
 ( SELECT LEVEL AS LV FROM DUAL CONNECT BY LEVEL <= :N)  B


by v상이v [2012.08.20 11:46:06]
1~5 => 가로 1row (5개)
6~9 => 세로 5row(4개)
10~13 => 가로 5row(4개)
14~16 => 세로 1row(3개)
17~19 => 가로 2row(3개)
20~21 => 세로 4row(2개)
22~23 => 가로 4row(2개)
24  => 세로 2row(1개)
25  => 가로 3row(1개)

숫자의 묶음은 5,4,4,3,3,2,2,1,1   즉 변수 N개를 시작으로 -1의 2묶음씩으로 구성되고

가로의 row는 1,5,2,4,3 순서로 홀수는 처음row부터 짝수는 마지막 row부터,

세로의 row는 5,1,4,2  순서로 홀수의 반대로 그려집니다.

가로 라인을 구하는거라 세로 row의 계산이 필요합니다.

이걸 토대로 짜 봤구요... 일단 답은 나오는듯 합니다.........;;

WITH TMP AS (SELECT 5 NUM FROM DUAL)
SELECT  LV2 AS "번호",
    CASE WHEN TMP IS NOT NULL THEN TMP
WHEN B = 1 THEN ROW_NUMBER() OVER(PARTITION BY LV ORDER BY LV2) + TMP1
WHEN B = 0 THEN NUM - ROW_NUMBER() OVER(PARTITION BY LV ORDER BY LV2)
    END AS "라인"
  FROM (    
    SELECT  LV,NUM,B,NVL2(LAG(NU2) OVER(ORDER BY LV),LAG(NU2) OVER(ORDER BY LV)+1,1) NU1,NU2,
    CASE WHEN A = 1 THEN SUM(DECODE(A,1,1,0)) OVER(ORDER BY LV)
WHEN A = 0 THEN NUM - (SUM(DECODE(A,0,1,0)) OVER(ORDER BY LV) - 1)
    END TMP,
    NVL2(B,SUM(B) OVER(ORDER BY LV), NULL) TMP1
  FROM (
    SELECT  LEVEL LV,NUM,
    SUM(NUM - CEIL((LEVEL-1)/2)) OVER(ORDER BY LEVEL) NU2,
    MOD(ROUND(DECODE(MOD(LEVEL,2),1,SUM(1) OVER(ORDER BY LEVEL))/2),2) AS A,
    MOD(ROUND(DECODE(MOD(LEVEL,2),0,SUM(1) OVER(ORDER BY LEVEL))/2),2) AS B
  FROM  (SELECT NUM FROM TMP)
    CONNECT BY LEVEL <= (NUM*2)-1
    )
   ) A,
   (SELECT  LEVEL LV2 FROM  (SELECT NUM FROM TMP)
   CONNECT BY LEVEL <= (NUM*NUM)
   ) B
 WHERE  B.LV2 BETWEEN A.NU1 AND A.NU2
 ORDER  BY LV2

by 손님 [2012.08.21 12:05:54]

출제자 입니다.

저는 with 문을 제외하고 인라인뷰를 2번 써서(즉 select를 3번 써서) 쿼리를 만들었습니다.

(v상이v 님은 상옥님이신가요? ^^)


by v상이v [2012.08.22 13:22:46]

저는 상옥님이 아닙니다....^^

좀 더 줄여볼수 있는 방법을 찾아봐야 겠네요.....

접근을 다르게도 해보고 싶은데... 자꾸 꼼수만 찾아볼라 해서 문제네요...;; (그러다 더 꼬여버린....)

by 신이만든짝퉁 [2012.09.08 00:36:33]
with tmp as (
select 1 seq, 1 num1, 2 num2, 3 num3, 4 num4, 5 num5 from dual
union all
select 2 seq, 16 num1, 17 num2, 18 num3, 19 num4, 6 num5 from dual
union all
select 3 seq, 15 num1, 24 num2, 25 num3, 20 num4, 7 num5 from dual
union all
select 4 seq, 14 num1, 23 num2, 22 num3, 21 num4, 8 num5 from dual
union all
select 5 seq, 13 num1, 12 num2, 11 num3, 10 num4, 9 num5 from dual
) select decode(lvl, 1, num1, 2, num2, 3, num3, 4, num4, 5, num5) as num
       , seq
  from tmp a
     ,  (select level lvl from dual connect by level <= 5) b
  order by num;

by 손님 [2012.09.21 13:32:00]

  SELECT NUM, (SUM(ADD_LINE) OVER(ORDER BY NUM))+1 AS LINE
  FROM
  (
SELECT CASE WHEN GB = 'X' THEN 0
ELSE CASE WHEN MOD(RK,2) = 0 THEN -1 ELSE 1 END
END ADD_LINE, STA_NUM, END_NUM
FROM  (
  SELECT GB, (LAG(SUM_CNT, 1, 0) OVER (ORDER BY SUM_CNT) )+1 AS STA_NUM, SUM_CNT AS END_NUM,
RANK() OVER (PARTITION BY GB ORDER BY SUM_CNT) RK
  FROM  (
   SELECT GB, CNT, SUM(CNT) OVER (ORDER BY CNT DESC, GB DESC) SUM_CNT
   FROM   (
   SELECT 'X' GB, LEVEL CNT FROM DUAL CONNECT BY ROWNUM <= :set_num
   UNION ALL SELECT 'Y', LEVEL FROM DUAL CONNECT BY ROWNUM < :set_num
  )
)
    )
  ) T1,
  (
SELECT LEVEL NUM FROM  DUAL
CONNECT BY ROWNUM <= :set_num*:set_num
  )
  WHERE NUM BETWEEN STA_NUM AND END_NUM
  ORDER BY NUM
댓글등록
SQL문을 포맷에 맞게(깔끔하게) 등록하려면 code() 버튼을 클릭하여 작성 하시면 됩니다.
로그인 사용자만 댓글을 작성 할 수 있습니다. 로그인, 회원가입