database.sarang.net
UserID
Passwd
Database
DBMS
MySQL
PostgreSQL
Firebird
ㆍOracle
Informix
Sybase
MS-SQL
DB2
Cache
CUBRID
LDAP
ALTIBASE
Tibero
DB 문서들
스터디
Community
공지사항
자유게시판
구인|구직
DSN 갤러리
도움주신분들
Admin
운영게시판
최근게시물
Oracle Q&A 40701 게시물 읽기
No. 40701
oracle로 1차 연립방정식 풀기
작성자
김흥수(protokhs)
작성일
2015-01-30 10:37ⓒ
2015-01-31 01:44ⓜ
조회수
8,677

저번에  recursive with으로 피보나치 수열 문제가 풀리는 것을 알게 되고 연립방정식도 되겠다고 생각을 했습니다.

그런데 연립방정식은 직전행만을 참조하지 않다는 것을 생각하고 안되겠다... 생각했었는데요...

갑자기 꽁수가 생각나서 해보니 풀렸습니다.

 

다음의 sql은 :rowcount에 미지수 갯수를 넣으면 가우스 조르단 법으로 연립방정식을 풉니다.

단 대각행열에 0이 없어야 합니다.

 

 with 기초자료 as

(

                SELECT 1 R, 1 C , 1 V FROM DUAL

    UNION ALL   SELECT 1 R, 2 C , 2 V FROM DUAL

    UNION ALL   SELECT 1 R, 3 C , 3 V FROM DUAL

    UNION ALL   SELECT 1 R, 4 C , 1 V FROM DUAL

    UNION ALL   SELECT 2 R, 1 C , 1 V FROM DUAL

    UNION ALL   SELECT 2 R, 2 C , 3 V FROM DUAL

    UNION ALL   SELECT 2 R, 3 C , 6 V FROM DUAL

    UNION ALL   SELECT 2 R, 4 C , 1 V FROM DUAL

    UNION ALL   SELECT 3 R, 1 C , 2 V FROM DUAL

    UNION ALL   SELECT 3 R, 2 C , 6 V FROM DUAL

    UNION ALL   SELECT 3 R, 3 C , 13 V FROM DUAL

    UNION ALL   SELECT 3 R, 4 C , 5 V FROM DUAL

)

, 기초자료_열병합_재귀 ( R , C, V , RN) AS

(

    SELECT

        A.R

        , A.C

        , TO_CHAR(A.V) V

        , 0 RN

    FROM    기초자료 A

    UNION ALL

    SELECT

        A.R

        , A.C

        , TO_CHAR(A.V)||','||B.V  V

        , B.RN + 1

    FROM    기초자료 A

            , 기초자료_열병합_재귀 B

    WHERE   B.R = A.R

    AND     B.C = A.C + 1

)

, 기초자료_열병합 as

(

    SELECT

        a.r

        ,a.c

        ,','||a.v||',' V

    FROM    기초자료_열병합_재귀 a

    where   a.rN = :rowcount

)

, 기초자료_행병합_재귀 (R,V,RN) AS

(

    SELECT

        R,V,0 RN

    FROM    기초자료_열병합 A

    UNION ALL

    SELECT

        A.R

        ,B.V || '|'||A.V

        ,B.RN + 1

    FROM    기초자료_열병합 A

            , 기초자료_행병합_재귀 B

    WHERE   A.R = B.R + 1

)

, 기초자료_행열병합 AS

(

    SELECT

        '|'||V V

    FROM    기초자료_행병합_재귀 A

    WHERE   A.RN = :rowcount - 1

)

, 대각행열_재귀(itr,r,c,v,rn,vv) as

(

    select

        0 itr

        ,0 r

        ,0 c

        ,a.v

        ,0 rn

        , to_number(SUBSTR(A.V,INSTR(A.V,',',INSTR(A.V,'|',1,2) + 1,1) + 1 , INSTR(A.V,',',INSTR(A.V,'|',1,2) + 1,1+1) - INSTR(A.V,',',INSTR(A.V,'|',1,2) + 1,1) - 1 )) / to_number(SUBSTR(A.V,INSTR(A.V,',',INSTR(A.V,'|',1,1) + 1,1) + 1 , INSTR(A.V,',',INSTR(A.V,'|',1,1) + 1,1+1) - INSTR(A.V,',',INSTR(A.V,'|',1,1) + 1,1) - 1 ))

    from    기초자료_행열병합 a

    union all

    select

        (floor((a.rn ) / ((:rowcount+1) * :rowcount)) + 1) itr

        ,(mod((floor((a.rn ) / (:rowcount+1)) + 1) - 1 ,3) + 1) r

        ,(mod((a.rn ),(:rowcount+1))  + 1) c

        , case when (floor((a.rn ) / ((:rowcount+1) * :rowcount)) + 1) = (mod((floor((a.rn ) / (:rowcount+1)) + 1) - 1 ,3) + 1) then

            a.v

        else

            SUBSTR(A.V,1,INSTR(A.V,',',INSTR(A.V,'|',1,(mod((floor((a.rn ) / (:rowcount+1)) + 1) - 1 ,3) + 1)) + 1,(mod((a.rn ),(:rowcount+1))  + 1)))||

            to_char(to_number(SUBSTR(A.V,INSTR(A.V,',',INSTR(A.V,'|',1,(mod((floor((a.rn ) / (:rowcount+1)) + 1) - 1 ,3) + 1)) + 1,(mod((a.rn ),(:rowcount+1))  + 1)) + 1 , INSTR(A.V,',',INSTR(A.V,'|',1,(mod((floor((a.rn ) / (:rowcount+1)) + 1) - 1 ,3) + 1)) + 1,(mod((a.rn ),(:rowcount+1))  + 1)+1) - INSTR(A.V,',',INSTR(A.V,'|',1,(mod((floor((a.rn ) / (:rowcount+1)) + 1) - 1 ,3) + 1)) + 1,(mod((a.rn ),(:rowcount+1))  + 1)) - 1 ))

            - to_number(SUBSTR(A.V,INSTR(A.V,',',INSTR(A.V,'|',1,(floor((a.rn ) / ((:rowcount+1) * :rowcount)) + 1)) + 1,(mod((a.rn ),(:rowcount+1))  + 1)) + 1 , INSTR(A.V,',',INSTR(A.V,'|',1,(floor((a.rn ) / ((:rowcount+1) * :rowcount)) + 1)) + 1,(mod((a.rn ),(:rowcount+1))  + 1)+1) - INSTR(A.V,',',INSTR(A.V,'|',1,(floor((a.rn ) / ((:rowcount+1) * :rowcount)) + 1)) + 1,(mod((a.rn ),(:rowcount+1))  + 1)) - 1 )) * a.vv) ||

            SUBSTR(A.V,INSTR(A.V,',',INSTR(A.V,'|',1,(mod((floor((a.rn ) / (:rowcount+1)) + 1) - 1 ,3) + 1)) + 1,(mod((a.rn ),(:rowcount+1))  + 1)+1))

        end v

        ,a.rn + 1 rn

        , case when (mod((a.rn ),(:rowcount+1))  + 1) = :rowcount + 1 then

            nvl(to_number(SUBSTR(A.V,INSTR(A.V,',',INSTR(A.V,'|',1,(mod((floor(((a.rn+1) ) / (:rowcount+1)) + 1) - 1 ,3) + 1)) + 1,(floor(((a.rn+1) ) / ((:rowcount+1) * :rowcount)) + 1)) + 1 , INSTR(A.V,',',INSTR(A.V,'|',1,(mod((floor(((a.rn+1) ) / (:rowcount+1)) + 1) - 1 ,3) + 1)) + 1,(floor(((a.rn+1) ) / ((:rowcount+1) * :rowcount)) + 1)+1) - INSTR(A.V,',',INSTR(A.V,'|',1,(mod((floor(((a.rn+1) ) / (:rowcount+1)) + 1) - 1 ,3) + 1)) + 1,(floor(((a.rn+1) ) / ((:rowcount+1) * :rowcount)) + 1)) - 1 )) / 

            nullif(to_number(SUBSTR(A.V,INSTR(A.V,',',INSTR(A.V,'|',1,(floor(((a.rn+1) ) / ((:rowcount+1) * :rowcount)) + 1)) + 1,(floor(((a.rn+1) ) / ((:rowcount+1) * :rowcount)) + 1)) + 1 , INSTR(A.V,',',INSTR(A.V,'|',1,(floor(((a.rn+1) ) / ((:rowcount+1) * :rowcount)) + 1)) + 1,(floor(((a.rn+1) ) / ((:rowcount+1) * :rowcount)) + 1)+1) - INSTR(A.V,',',INSTR(A.V,'|',1,(floor(((a.rn+1) ) / ((:rowcount+1) * :rowcount)) + 1)) + 1,(floor(((a.rn+1) ) / ((:rowcount+1) * :rowcount)) + 1)) - 1 )),0),0)

        else

            a.vv

        end vv

    from    대각행열_재귀 a

    where   a.rn < (:rowcount + 1 ) * :rowcount * :rowcount

)

, 대각행열 as

(

    select

        a.itr

        ,a.r

        ,a.c

        ,a.v

        ,a.rn

        ,a.vv

    from    대각행열_재귀 a

    where   a.rn = (:rowcount + 1 ) * :rowcount * :rowcount

)

, 단위행열_재귀 (r,c,v,rn) as

(

    select

        0 r

        ,0 c

        , a.v

        ,0 rn

    from    대각행열 a

    union all

    select

        (floor((a.rn ) / (:rowcount+1)) + 1) r

        ,(mod((a.rn ),(:rowcount+1))  + 1) c

        ,SUBSTR(A.V,1,INSTR(A.V,',',INSTR(A.V,'|',1,(floor((a.rn ) / (:rowcount+1)) + 1)) + 1,(mod((a.rn ),(:rowcount+1))  + 1))) ||

        to_char(to_number(SUBSTR(A.V,INSTR(A.V,',',INSTR(A.V,'|',1,(floor((a.rn ) / (:rowcount+1)) + 1)) + 1,(mod((a.rn ),(:rowcount+1))  + 1)) + 1 , INSTR(A.V,',',INSTR(A.V,'|',1,(floor((a.rn ) / (:rowcount+1)) + 1)) + 1,(mod((a.rn ),(:rowcount+1))  + 1)+1) - INSTR(A.V,',',INSTR(A.V,'|',1,(floor((a.rn ) / (:rowcount+1)) + 1)) + 1,(mod((a.rn ),(:rowcount+1))  + 1)) - 1 )) / 

        to_number(SUBSTR(A.V,INSTR(A.V,',',INSTR(A.V,'|',1,(floor((a.rn ) / (:rowcount+1)) + 1)) + 1,(floor((a.rn ) / (:rowcount+1)) + 1)) + 1 , INSTR(A.V,',',INSTR(A.V,'|',1,(floor((a.rn ) / (:rowcount+1)) + 1)) + 1,(floor((a.rn ) / (:rowcount+1)) + 1)+1) - INSTR(A.V,',',INSTR(A.V,'|',1,(floor((a.rn ) / (:rowcount+1)) + 1)) + 1,(floor((a.rn ) / (:rowcount+1)) + 1)) - 1 )))

        ||SUBSTR(A.V,INSTR(A.V,',',INSTR(A.V,'|',1,(floor((a.rn ) / (:rowcount+1)) + 1)) + 1,(mod((a.rn ),(:rowcount+1))  + 1)+1)) v

        ,a.rn + 1

    from    단위행열_재귀 a

    where   a.rn < (:rowcount + 1 ) * :rowcount

)

, 단위행열 as

(

    select

        a.v

    from    단위행열_재귀 a

    where   a.rn = (:rowcount + 1 ) * :rowcount

)

select

    b.rn 미지수번호

    , to_number(SUBSTR(A.V,INSTR(A.V,',',INSTR(A.V,'|',1,b.rn) + 1,(:rowcount + 1)) + 1 , INSTR(A.V,',',INSTR(A.V,'|',1,b.rn) + 1,(:rowcount + 1)+1) - INSTR(A.V,',',INSTR(A.V,'|',1,b.rn) + 1,(:rowcount + 1)) - 1 )) 미지수값

from    단위행열 a

        , ( select level rn from dual connect by level < :rowcount + 1 ) b

            

/

 

 

재미삼아 보시는 것도 좋으시리라...

감사합니다^^

이 글에 대한 댓글이 총 2건 있습니다.

 설명이 전혀 없어서 추가합니다.

위의 쿼리는 아래의 방정식에 대한 해법입니다.

 

1x + 2y + 3z = 1
1x + 3y + 6z = 1
2x + 6y + 13z = 5
 
결과는 x=10 , y = -9 , z = 3 입니다.
구하는 방법은 행렬 표현식을 문자열로 병합하고
 
recursive with을 써서 
가우스 조르단 알고리즘을
점화식 형태로 바꾸어 구현한 것입니다.
 
문자열을 메모리 스택으로 사용하는 것이 꽁수입니다.
 
구현 순서는
1) 문자열로 병합
2) 대각행렬 구하기
3) 단위행열로 변환
4) 근 추출
의 순서로 되어 있습니다.
 
recursive with가 본질적으로 점화식 형태를 모두 구할 수 있습니다.
그리고 순차 프로그램은 점화식으로 모형화할 수 있으므로 모든 순차프로그램을
이론적으로 문자열만 무한하면 복잡해서 그렇지 recursive with를 사용하면 구할 수 있다는 뜻이 됩니다.
 
음... 참 재미있네요....
 
 
김흥수(protokhs)님이 2015-01-30 16:58에 작성한 댓글입니다.

 불필요하게 복잡한 문자열 행열 연산 부분을 함수로 바꾸어 로직이 드러나도록 하였습니다.

 

Create Or Replace Function get_From_Mtrx
(
    a_Mtrx          Varchar2
    ,a_Row          Number
    ,a_Col          Number
)
Return Number
Is
/*
    a_Mtrx는 행열을 문자열로 표현한 것이다.
    행 구분구분자 |
    열 구분자는 ,
    편의성을 위해 행과 열 구분자는 맨 앞과 맨 뒤에도 추가되어 있다.
    예를 들어
    1,2,3,5
    3,2,1,6
    1,2,1,7
    과 같은 행열이 있다면 다음과 같이 표현된다.
    |,1,2,3,5,|,3,2,1,6,|,1,2,1,7,|
    이렇게 표현된 행열 표현 문자열에서 원하는 행/열의 값을 추출하는 함수이다.
*/
Begin
    Return to_number(SUBSTR(a_Mtrx,INSTR(a_Mtrx,',',INSTR(a_Mtrx,'|',1,a_Row) + 1,a_Col) + 1 ,
        INSTR(a_Mtrx,',',INSTR(a_Mtrx,'|',1,a_Row) + 1,a_Col+1) - INSTR(a_Mtrx,',',INSTR(a_Mtrx,'|',1,a_Row) + 1,a_Col) - 1 ));
End;
/
Create Or Replace Function set_To_Mtrx
(
    a_Mtrx          Varchar2
    ,a_Row          Number
    ,a_Col          Number
    ,a_Val          Number
)
Return Varchar2
Is
/*
    a_Mtrx는 행열을 문자열로 표현한 것이다.
    행 구분구분자 |
    열 구분자는 ,
    편의성을 위해 행과 열 구분자는 맨 앞과 맨 뒤에도 추가되어 있다.
    예를 들어
    1,2,3,5
    3,2,1,6
    1,2,1,7
    과 같은 행열이 있다면 다음과 같이 표현된다.
    |,1,2,3,5,|,3,2,1,6,|,1,2,1,7,|
    이렇게 표현된 행열 표현 문자열에서 원하는 행/열에 값을 설정한 새로운 문자열을 구하는 함수이다.
        select
            set_To_Mtrx('|,1,2,3,5,|,3,2,1,6,|,1,2,1,7,|',1,4,100)
        from    dual
    의 결과 값은
    |,1,2,3,100,|,3,2,1,6,|,1,2,1,7,|
    이 된다.
*/
Begin
    Return  SUBSTR(a_Mtrx,1,INSTR(a_Mtrx,',',INSTR(a_Mtrx,'|',1,a_Row) + 1,a_Col))||
            to_char(a_Val) ||
            SUBSTR(a_Mtrx,INSTR(a_Mtrx,',',INSTR(a_Mtrx,'|',1,a_Row) + 1,a_Col+1));
End;
/
 
 
이 함수들로 행열 연산을 하고 약간의 노력을 들이면 다음과 같이 가독성이 개선된다.
 
with 기초자료 as
(
                SELECT 1 R, 1 C , 1 V FROM DUAL
    UNION ALL   SELECT 1 R, 2 C , 2 V FROM DUAL
    UNION ALL   SELECT 1 R, 3 C , 3 V FROM DUAL
    UNION ALL   SELECT 1 R, 4 C , 1 V FROM DUAL
    UNION ALL   SELECT 2 R, 1 C , 1 V FROM DUAL
    UNION ALL   SELECT 2 R, 2 C , 3 V FROM DUAL
    UNION ALL   SELECT 2 R, 3 C , 6 V FROM DUAL
    UNION ALL   SELECT 2 R, 4 C , 1 V FROM DUAL
    UNION ALL   SELECT 3 R, 1 C , 2 V FROM DUAL
    UNION ALL   SELECT 3 R, 2 C , 6 V FROM DUAL
    UNION ALL   SELECT 3 R, 3 C , 13 V FROM DUAL
    UNION ALL   SELECT 3 R, 4 C , 5 V FROM DUAL
)
, 기초자료_열병합_재귀 ( R , C, V , RN) AS
(
    SELECT
        A.R
        , A.C
        , TO_CHAR(A.V) V
        , 0 RN
    FROM    기초자료 A
    UNION ALL
    SELECT
        A.R
        , A.C
        , TO_CHAR(A.V)||','||B.V  V
        , B.RN + 1
    FROM    기초자료 A
            , 기초자료_열병합_재귀 B
    WHERE   B.R = A.R
    AND     B.C = A.C + 1
)
, 기초자료_열병합 as
(
    SELECT
        a.r
        ,a.c
        ,','||a.v||',' V
    FROM    기초자료_열병합_재귀 a
    where   a.rN = :rowcount
)
, 기초자료_행병합_재귀 (R,V,RN) AS
(
    SELECT
        R,V,0 RN
    FROM    기초자료_열병합 A
    UNION ALL
    SELECT
        A.R
        ,B.V || '|'||A.V
        ,B.RN + 1
    FROM    기초자료_열병합 A
            , 기초자료_행병합_재귀 B
    WHERE   A.R = B.R + 1
)
, 기초자료_행열병합 AS
(
    SELECT
        '|'||V ||'|' v
    FROM    기초자료_행병합_재귀 A
    WHERE   A.RN = :rowcount - 1
)
, 대각행열_재귀(v,대각행열나눔수,순번,다음순번,반복번호,다음반복번호, 행번호,열번호,다음행번호) as
(
    select
        a.v
        , get_From_Mtrx(a.v,1,1) 대각행열나눔수
        ,1 순번
        , 2 다음순번
        , 1 반복번호
        , 1 다음반복번호
        , 1 행번호
        , 1 열번호
        , 1 다음행번호
    from    기초자료_행열병합 a
    union all
    select
        case when a.반복번호  = a.행번호 then
            a.v
        else
            set_To_Mtrx(a.v,a.행번호,a.열번호,
                get_From_Mtrx(a.v, a.행번호, a.열번호)
                - get_From_Mtrx(a.v, a.반복번호, a.열번호) * a.대각행열나눔수 )
        end v
        , case when a.열번호 = :rowcount + 1 then
            nvl(get_From_Mtrx(a.v, a.다음행번호, a.다음반복번호) /
            nullif(get_From_Mtrx(a.v, a.다음반복번호, a.다음반복번호),0),0)
        else
            a.대각행열나눔수
        end 대각행열나눔수
        ,a.순번 + 1 순번
        ,a.다음순번 + 1 다음순번
        ,(floor((a.순번 ) / ((:rowcount+1) * :rowcount)) + 1) 반복번호
        ,(floor((a.다음순번 ) / ((:rowcount+1) * :rowcount)) + 1) 다음반복번호
        ,(mod((floor((a.순번 ) / (:rowcount+1)) + 1) - 1 ,3) + 1) r
        ,(mod((a.순번 ),(:rowcount+1))  + 1) 열번호
        ,(mod((floor((a.다음순번 ) / (:rowcount+1)) + 1) - 1 ,3) + 1) 다음행번호
    from    대각행열_재귀 a
    where   a.순번 <= (:rowcount + 1 ) * :rowcount * :rowcount
)
, 대각행열 as
(
    select
        a.행번호
        ,a.열번호
        ,a.v
        ,a.순번
        ,a.대각행열나눔수
    from    대각행열_재귀 a
    where   a.순번 = (:rowcount + 1 ) * :rowcount * :rowcount + 1
)
, 단위행열_재귀 (v,순번,행번호,열번호) as
(
    select
         a.v
        ,0 순번
        ,1 r
        ,1 c
    from    대각행열 a
    union all
    select
        set_To_Mtrx(a.v,a.행번호,a.열번호,
            get_From_Mtrx(a.v, a.행번호, a.열번호) /
            get_From_Mtrx(a.v, a.행번호, a.행번호))
        ,a.순번 + 1
        ,(floor((a.순번 + 1  ) / (:rowcount+1)) + 1) 행번호
        ,(mod((a.순번 + 1 ),(:rowcount+1))  + 1) 열번호
    from    단위행열_재귀 a
    where   a.순번 < (:rowcount + 1 ) * :rowcount
)
, 단위행열 as
(
    select
        a.v
    from    단위행열_재귀 a
    where   a.순번 = (:rowcount + 1 ) * :rowcount
)
select
    b.rn 미지수번호
    ,get_From_Mtrx(a.v,b.rn,:rowcount + 1) 미지수값
from    단위행열 a
        , ( select level rn from dual connect by level < :rowcount + 1 ) b
 
/
 
김흥수(protokhs)님이 2015-02-06 16:22에 작성한 댓글입니다.
이 댓글은 2015-02-06 16:23에 마지막으로 수정되었습니다.
[Top]
No.
제목
작성자
작성일
조회
407047일 연속 로그인한 고객데이터 뽑는 쿼리 부탁 드립니다. [2]
사바직자
2015-02-02
8105
40703DB 날짜 등록하기 [1]
조성찬
2015-02-02
7246
40702DB로 날짜 검색 [3]
조성찬
2015-01-30
8303
40701oracle로 1차 연립방정식 풀기 [2]
김흥수
2015-01-30
8677
40700ID중복검사..를 하고싶은데요. 지적부탁드립니다. [2]
newbie
2015-01-30
7828
40699조건부 count 쿼리 도와주세요 [2]
박용대
2015-01-29
7022
40698쿼리를 자주 안쓰면 느리게 되는 걸 어떻게 막죠? [1]
김삼
2015-01-29
8130
Valid XHTML 1.0!
All about the DATABASE... Copyleft 1999-2024 DSN, All rights reserved.
작업시간: 0.022초, 이곳 서비스는
	PostgreSQL v16.2로 자료를 관리합니다