맨위로가기

루프 불변성

"오늘의AI위키"는 AI 기술로 일관성 있고 체계적인 최신 지식을 제공하는 혁신 플랫폼입니다.
"오늘의AI위키"의 AI를 통해 더욱 풍부하고 폭넓은 지식 경험을 누리세요.

1. 개요

루프 불변성은 루프가 실행되는 동안 참으로 유지되는 프로그램 속성을 의미하며, 프로그램의 정확성을 보장하고 코드의 효율성을 높이는 데 중요한 개념이다. 루프 불변성은 코드를 문서화하고, 어서션을 통해 확인하며, 호어 논리를 기반으로 검증하는 데 사용될 수 있다. 에펠 및 Whiley와 같은 프로그래밍 언어는 루프 불변성을 언어 차원에서 지원하며, 컴파일러는 루프 불변 코드 이동과 같은 최적화를 통해 프로그램 성능을 향상시킨다.

더 읽어볼만한 페이지

  • 정형 기법 - 자동 정리 증명
    자동 정리 증명은 논리적 진술의 타당성을 자동으로 확인하는 기술로서, 형식 논리 발전과 컴퓨터 등장 이후 다양한 논리를 지원하며 여러 분야에 응용되고, 관련 연구와 시스템 개발이 활발히 진행되고 있다.
  • 정형 기법 - 튜링 기계
    튜링 기계는 앨런 튜링이 제시한 계산 모델로, 테이프 위에서 기계적으로 작동하며, 유한한 상태, 테이프, 헤드, 명령 표를 통해 작동하고, 계산 가능성과 알고리즘의 한계를 연구하는 데 사용된다.
  • 제어 흐름 - 프로그램 카운터
    프로그램 카운터는 CPU 내에서 다음에 실행될 명령어의 주소를 저장하는 레지스터로, 명령어 사이클의 fetch 단계에서 사용되어 명령어를 가져오고 실행 후 갱신되며, CPU 성능 향상 기술과 현대 프로그래밍 모델에 영향을 미친다.
  • 제어 흐름 - 예외 처리
    예외 처리는 프로그램 실행 중 예외 발생 시 정상적인 실행 흐름을 유지하거나 안전하게 종료하기 위한 메커니즘으로, 많은 프로그래밍 언어에서 제공하며 예외 안전성을 목표로 한다.
루프 불변성
개요
정의루프가 실행되기 전이나 실행 중, 그리고 실행이 끝난 후에도 항상 참인 조건
용도프로그램의 정확성을 증명하는 데 사용
관련 개념수학적 귀납법
상세 내용
설명루프의 각 반복에서 불변성이 유지되면 루프가 올바르게 작동함을 보장
예시배열 정렬 알고리즘에서 루프 불변성은 루프의 각 반복 후 배열의 일부가 정렬된 상태로 유지되는 것
필요성복잡한 루프의 정확성을 보장하고 디버깅을 용이하게 함
예시 (최대값 찾기)
불변성'`m`은 배열의 `0`부터 `i-1`번째 요소 중 최대값이다'
초기화`i = 1`, `m = a[0]`
유지`a[i] > m`이면 `m = a[i]`
종료`i = n`일 때 `m`은 배열 `a`의 최대값이 됨
활용
소프트웨어 개발코드의 정확성을 검증하고, 디버깅 과정을 단순화
알고리즘 설계알고리즘의 정확성을 보장하고, 성능을 최적화
주의사항
불변성 정의루프의 목적과 일치해야 함
불변성 유지각 반복에서 불변성이 깨지지 않도록 주의

2. 비공식 예제

C 언어로 작성된 `max()` 함수는 주어진 배열에서 최댓값을 찾는 과정을 보여준다. 이 함수는 루프 불변성을 활용하여 프로그램의 정확성을 보장한다.

함수 내에서 `m`은 현재까지 찾은 최댓값을, `i`는 배열의 인덱스를 나타낸다. 루프가 시작되기 전, `m`은 배열의 첫 번째 요소로 초기화되고, `i`는 1로 설정된다. 루프 불변 조건은 "m은 a[0...i-1]의 최댓값이다." 이다.

루프 내부에서는 `m`과 `a[i]`를 비교하여 `a[i]`가 더 크면 `m`을 업데이트한다. 그 후 `i`를 증가시켜 다음 요소를 가리키도록 한다. 이 과정을 통해 루프 불변성은 계속 유지된다.

루프가 종료되는 시점(`i == n`)에는, 루프 불변성에 의해 `m`은 배열 전체(`a[0...n-1]`)의 최댓값을 가지게 된다. 따라서 함수는 `m`을 반환하여 올바른 결과를 얻는다.

2. 1. C 코드 예제

다음은 C 서브루틴 `max()`로, 인수로 받은 배열 `a[]`의 최댓값을 반환하며, 배열의 길이 `n`은 1 이상이어야 한다.

```c

int max(int n, const int a[n]) {

int m = a[0];

// m은 a[0...0]의 최대값이다.

int i = 1;

while (i != n) {

// m은 a[0...i-1]의 최대값이다.

if (m < a[i])

m = a[i];

// m은 a[0...i]의 최대값이다.

++i;

// m은 a[0...i-1]의 최대값이다.

}

// m은 a[0...i-1]의 최대값이며, i == n이다.

return m;

}

```

주석은 3, 6, 9, 11, 13행에 제공된다. 각 주석은 함수 해당 단계에서 하나 이상의 변수 값에 대한 단언을 한다.

루프 본문 내에서, 루프의 시작과 끝에서 강조 표시된 단언(6행과 11행)은 정확히 동일하다. 따라서 루프의 불변 속성을 설명한다.

13행에 도달하면 이 불변 속성은 여전히 유지되며, 루프 조건 `i!=n`(5행)이 거짓이 되었다는 것을 알 수 있다. 이 두 속성이 함께 `m`이 `a[0...n-1]`의 최댓값과 같다는 것을 의미하며, 즉 14행에서 올바른 값이 반환된다.

방어적 프로그래밍 패러다임을 따르려면, 5행의 루프 조건 `i!=n`을 `i=n`만 알 수 있기 때문이다. 또한 `i<=n`이 유효하도록 하려면 해당 조건을 루프 불변 속성에 포함해야 한다. `i<=n` 역시 루프의 불변 속성임을 쉽게 알 수 있다. 6행에서 `i

2. 2. 방어적 프로그래밍

방어적 프로그래밍 관점에서, 루프 조건 `i != n`을 `i < n`으로 수정하면 `n`이 음수일 때 무한 루프에 빠지는 잠재적인 오류를 방지할 수 있다. 이러한 코드 변경은 직관적으로는 큰 차이가 없어 보이지만, 프로그램의 정확성을 분석하는 과정은 다소 복잡해진다. `i != n` 조건에서는 루프 종료 후 `i == n`임을 바로 알 수 있지만, `i < n` 조건에서는 루프 종료 후 `i >= n`이라는 것만 알 수 있기 때문이다.

따라서 `i <= n`이라는 조건을 루프 불변성에 추가해야 한다. `i <= n`이 루프 불변성이라는 것은 쉽게 확인할 수 있다. 루프 진입 시 `i`는 1이고 `n`은 1 이상이므로 `i <= n`이 성립한다. 또한, 루프 내부에서 `i`가 증가하기 전(6행)에 `i < n`이 참이므로, `i`가 증가한 후(11행)에도 `i <= n`이 유지된다.

이처럼 루프 불변성을 명시적으로 작성하면 프로그램의 정확성을 검증하는 데 도움이 된다. 특히, `i <= n`과 같이 너무 당연해 보이는 조건은 간과하기 쉽기 때문에, 루프 불변성을 수동으로 제공하는 과정에서 이러한 조건을 놓치지 않고 확인할 수 있다.

3. 호어 논리(Floyd-Hoare logic)

플로이드-호어 논리는 루프 불변성을 정형적으로 정의하고 추론 규칙을 설명하는 데 사용된다.[2][3]

3. 1. 추론 규칙

플로이드-호어 논리에서,[2][3] 부분적 정확성의 while 루프는 다음과 같은 추론 규칙에 의해 제어된다.

:\frac{\{C\land I\}\;\mathrm{body}\;\{I\}} {\{I\}\;\mathtt{while}\ (C)\ \mathrm{body}\;\{\lnot C\land I\}}

이는 다음을 의미한다.

  • 만약 어떤 속성 가 코드 \mathrm{body}에 의해 보존된다면 — 좀 더 정확하게는, 와 가 모두 먼저 성립했을 때 \mathrm{body}를 실행한 후에 가 성립한다면— ''(윗줄)''
  • 가 루프 전에 참이었다면 ''(아랫줄)'' 전체 루프 \mathtt{while}\ (C)\ \mathrm{body}를 실행한 후, 와 는 각각 거짓과 참이 될 것이 보장된다.


다시 말해, 위 규칙은 전제 \{C\land I\}\;\mathrm{body}\;\{I\}를 갖는 호어 트리플에 해당하는 연역적 단계이다. 이 트리플은 실제로는 머신 상태에 대한 관계이다. 이는 부울 표현식 C\land I가 참인 상태에서 시작하여 \mathrm{body}라고 불리는 코드를 성공적으로 실행하면 머신이 가 참인 상태로 끝나면 성립한다. 이 관계가 증명될 수 있다면, 이 규칙을 통해 프로그램 \mathtt{while}\ (C)\ \mathrm{body}의 성공적인 실행이 가 참인 상태에서 \lnot C\land I가 성립하는 상태로 이어진다고 결론 내릴 수 있다. 이 규칙에서 부울 공식 는 루프 불변량이라고 불린다.

사용되는 표기법에 약간의 변형이 있고 루프가 중단된다는 전제가 있는 이 규칙은 '''불변 관계 정리'''라고도 한다.[4][5] 1970년대의 한 교과서는 학생 프로그래머가 접근하기 쉽게 다음과 같이 제시하고 있다.[4]

P { seq } Q 표기법은 seq 일련의 명령문이 실행되기 전에 P가 참이면, 그 후에 Q가 참이 된다는 의미를 갖는다. 그러면 불변 관계 정리가 다음과 같이 성립한다.

:P & c { seq } P

::implies

:P { DO WHILE (c); seq END; } P & ¬c

3. 2. 불변 관계 정리

플로이드-호어 논리에서 사용되는 표기법에 약간의 변형을 가하고 루프가 중단된다는 전제를 추가한 추론 규칙을 '''불변 관계 정리'''라고도 한다.[4][5] 1970년대의 한 교과서에서는 학생 프로그래머가 이해하기 쉽도록 다음과 같이 제시하였다.[4]

: `P & c { seq } P`

: implies

: `P { DO WHILE (c); seq END; } P & ¬c`

여기서 `P { seq } Q` 표기법은 `seq`라는 일련의 명령문이 실행되기 전에 `P`가 참이면, 그 후에 `Q`가 참이 된다는 의미를 갖는다.

3. 3. 예제

다음은 호어 논리의 추론 규칙 작동 예시이다.[2][3] 다음 프로그램을 보자.

```

while (x < 10)

x := x+1;

```

다음 호어 트리플을 증명할 수 있다.

:\{x\leq10\}\; \mathtt{while}\ (x<10)\ x := x+1\;\{x=10\}

`while` 루프의 조건 ''C''는 x<10이다. 루프 불변량으로 x\leq10을 선택하면, 다음 호어 삼중항을 증명할 수 있다.

:\{x<10 \land x\leq10\}\; x := x+1 \;\{x\leq10\}

이는 할당 규칙에서 도출되거나, x<10인 상태에서 1을 더해도 여전히 x\leq10임을 직관적으로 이해할 수 있다.

`while` 루프 규칙에 따라 다음 결론을 얻는다.

:\{x\leq10\}\; \mathtt{while}\ (x<10)\ x := x+1 \;\{\lnot(x<10) \land x\leq10\}

결론의 \lnot(x<10)\land x\leq10x=10과 논리적으로 같다.

다른 루프 불변량으로 0 \leq x를 적용하면 \{0 \leq x\}\; \mathtt{while}\ (x<10)\ x := x+1\;\{10 \leq x\}를, \mathrm{true}를 적용하면 \{\mathrm{true}\}\; \mathtt{while}\ (x<10)\ x := x+1\;\{10 \leq x\}를 얻을 수 있다.

4. 프로그래밍 언어 지원

에펠과 Whiley는 루프 불변성을 직접 지원하는 프로그래밍 언어이다.[6][7] 에펠은 `invariant` 키워드를, Whiley는 `where` 절을 사용하여 루프 불변성을 표현한다.

4. 1. Eiffel

에펠 프로그래밍 언어는 루프 불변성에 대한 네이티브 지원을 제공한다.[6] 루프 불변성은 클래스 불변성에 사용되는 것과 동일한 구문으로 표현된다. 아래 예제에서 루프 불변식 x <= 10은 루프 초기화 이후와 루프 본문의 각 실행 이후에 참이어야 하며, 이는 런타임에 검사된다.

```eiffel

from

x := 0

invariant

x <= 10

until

x > 10

loop

x := x + 1

end

4. 2. Whiley

Whiley 프로그래밍 언어는 루프 불변성에 대한 일급 지원을 제공한다.[7] 루프 불변성은 하나 이상의 `where` 절을 사용하여 표현된다.

다음은 Whiley 코드 예시이다.



function max(int[] items) -> (int r)

// 최대값을 계산하려면 최소한 하나의 요소가 필요합니다.

requires |items| > 0

// (1) 결과는 어떤 요소보다 작지 않습니다.

ensures all { i in 0..|items| | items[i] <= r }

// (2) 결과는 최소한 하나의 요소와 일치합니다.

ensures some { i in 0..|items| | items[i] == r }:

//

nat i = 1

int m = items[0]

//

while i < |items|

// (1) 지금까지 본 어떤 항목도 m보다 크지 않습니다.

where all { k in 0..i | items[k] <= m }

// (2) 지금까지 본 하나 이상의 항목이 m과 일치합니다.

where some { k in 0..i | items[k] == m }:

if items[i] > m:

m = items[i]

i = i + 1

//

return m



`max()` 함수는 정수 배열에서 가장 큰 요소를 찾는다. 이 함수가 제대로 작동하려면 배열에 최소한 하나의 요소가 있어야 한다. `max()` 함수의 사후 조건은 반환된 값이 (1) 어떤 요소보다 작지 않고, (2) 최소한 하나의 요소와 같아야 한다고 명시한다. 루프 불변성은 두 개의 `where` 절을 통해 귀납적으로 정의되며, 각 절은 사후 조건의 절에 해당한다. 루프 불변성의 각 절은 현재 요소 `i`까지의 결과가 정확함을 나타내는 반면, 사후 조건은 모든 요소에 대해 결과가 정확함을 나타낸다는 차이점이 있다.

5. 루프 불변성의 활용

루프 불변성은 주로 코드의 정확성을 검증하고 이해하기 위한 도구로 활용된다. 플로이드-호어 논리에서 루프 불변성은 while 루프의 추론 규칙을 통해 부분적 정확성을 증명하는 데 사용된다.[2][3]

플로이드-호어 논리에서 while 루프의 추론 규칙은 다음과 같다.

:\frac{\{C\land I\}\;\mathrm{body}\;\{I\}} {\{I\}\;\mathtt{while}\ (C)\ \mathrm{body}\;\{\lnot C\land I\}}

여기서,


  • \{C\land I\}\;\mathrm{body}\;\{I\}는 조건 C와 불변성 I가 모두 참인 상태에서 코드 \mathrm{body}를 실행하면 불변성 I가 여전히 참임을 의미한다.
  • \{I\}\;\mathtt{while}\ (C)\ \mathrm{body}\;\{\lnot C\land I\}는 불변성 I가 참인 상태에서 while 루프를 실행하면, 루프 종료 후에는 조건 C가 거짓이 되고 불변성 I는 여전히 참이 됨을 의미한다.


즉, 루프 불변성 I는 루프가 실행되는 동안 항상 참으로 유지되는 속성을 나타낸다. 이를 통해 루프의 동작을 예측하고, 루프가 종료된 후의 상태를 추론할 수 있다.

1970년대의 한 교과서에서는 불변 관계 정리를 다음과 같이 제시하기도 했다.[4][5]

:P & c { seq } P

::implies

:P { DO WHILE (c); seq END; } P & ¬c

여기서 P { seq } Qseq라는 일련의 명령문이 실행되기 전 P가 참이면, 그 후에는 Q가 참이 된다는 것을 의미한다.

5. 1. 목적

루프 불변성은 다음 세 가지 목적으로 사용될 수 있다.

1. 순수하게 문서화

2. 코드 내에서 어서션 호출을 통해 확인

3. 플로이드-호어 접근법을 기반으로 검증

첫 번째 경우, 자연어 주석(예: 위 예시의 `// m은 a[0...i-1]의 최대값과 같다`)으로 충분하다.

두 번째 경우, C 라이브러리 assert.h 또는 위에 표시된 에펠의 `invariant` 절과 같은 프로그래밍 언어 지원이 필요하다. 런타임 검사는 컴파일러 또는 런타임 옵션을 통해 켜거나(디버깅 실행용) 끌 수 있다(프로덕션 실행용).

세 번째 경우, 주어진 루프 코드가 실제로 주어진 루프 불변성을 만족하는지(세트)에 대한 위에 표시된 플로이드-호어 규칙을 기반으로 하는 수학적 증명을 지원하는 도구가 있다.

추상 해석 기술을 사용하여 주어진 코드의 루프 불변성을 자동으로 감지할 수 있다. 그러나 이 접근 방식은 매우 간단한 불변성(예: `0<=i && i<=n && i%2==0`)으로 제한된다.

5. 2. 자동 감지

추상 해석 기술을 이용해 주어진 코드의 루프 불변성을 자동으로 감지할 수 있다. 그러나 이 접근 방식은 매우 간단한 불변성(예: 0<=i && i<=n && i%2==0)으로 제한된다.

6. 루프 불변 코드와의 비교

'''루프 불변 코드'''는 프로그램 실행 결과에 영향을 주지 않으면서 루프 바깥으로 옮길 수 있는 코드 구문이나 표현식을 말한다. 반면 '''루프 불변성'''은 루프가 실행되는 동안 항상 참으로 유지되는 조건을 가리킨다. 예를 들어 `0<=i && i<=n`과 같은 조건은 루프 불변성이지만, 코드의 일부가 아니기 때문에 루프 밖으로 옮긴다는 것은 의미가 없다.

루프 불변 코드는 루프 불변성을 유도할 수 있다.

6. 1. 루프 불변 코드

컴파일러 최적화에서 사용되는 '''루프 불변 코드'''는 프로그램의 의미에 영향을 주지 않으면서 루프 본체 외부로 이동할 수 있는 구문 또는 표현식을 말한다. 이러한 변환을 루프 불변 코드 이동이라고 하며, 일부 컴파일러에서 프로그램을 최적화하기 위해 수행한다.

루프 불변 코드의 예시(C 프로그래밍 언어)는 다음과 같다.

```c

for (int i=0; i
x = y+z;

a[i] = 6*i + x*x;

}

```

여기서 `x = y+z` 및 `x*x`의 계산은 루프 앞으로 이동하여 동일하지만 더 빠른 프로그램을 만들 수 있다.

```c

x = y+z;

t1 = x*x;

for (int i=0; i
a[i] = 6*i + t1;

}

6. 2. 예시

C 프로그래밍 언어를 사용한 루프 불변 코드의 예시는 다음과 같다.

```c

for (int i=0; i
x = y+z;

a[i] = 6*i + x*x;

}

```

여기서 `x = y+z` 및 `x*x`의 계산은 루프 앞으로 이동하여 동일하지만 더 빠른 프로그램을 만들 수 있다.

```c

x = y+z;

t1 = x*x;

for (int i=0; i
a[i] = 6*i + t1;

}

```

이러한 코드는 최적화를 통해 프로그램의 실행 속도를 향상시킬 수 있다.

6. 3. 루프 불변성과의 관계

루프 불변 코드는 프로그램의 의미에 영향을 주지 않고 루프 본체 외부로 이동할 수 있는 구문 또는 표현식으로 구성된다. 이러한 변환은 루프 불변 코드 이동이라고 하며, 일부 컴파일러에서 프로그램을 최적화하기 위해 수행된다.

루프 불변 코드는 해당 루프 불변 속성을 유도할 수 있다. 아래는 루프 불변 코드가 루프 안팎에서 모두 계산되는 C 언어 프로그램 예시이다.

```c

x1 = y+z;

t1 = x1*x1;

for (int i=0; i
x2 = y+z;

a[i] = 6*i + t1;

}

```

이 코드의 루프 불변 속성은 `(x1==x2 && t1==x2*x2) || i==0`이며, 이는 루프 전에 계산된 값이 (첫 번째 반복 전을 제외하고) 루프 내에서 계산된 값과 일치함을 나타낸다.

참조

[1] 논문 Loop invariants: analysis, classification, and examples http://se.ethz.ch/~m[...] 2014-02
[2] 서적 Proceedings of Symposia in Applied Mathematics American Mathematical Society
[3] 간행물 An axiomatic basis for computer programming http://www.spatial.m[...] 1969-10
[4] 서적 An Introduction to Programming: A Structured Approach using PL/1 and PL/C Winthrop
[5] 서적 Software Error Detection through Testing and Analysis John Wiley & Sons
[6] 문서 Eiffel: The Language Prentice Hall
[7] 간행물 Designing a Verifying Compiler: Lessons Learned from Developing Whiley



본 사이트는 AI가 위키백과와 뉴스 기사,정부 간행물,학술 논문등을 바탕으로 정보를 가공하여 제공하는 백과사전형 서비스입니다.
모든 문서는 AI에 의해 자동 생성되며, CC BY-SA 4.0 라이선스에 따라 이용할 수 있습니다.
하지만, 위키백과나 뉴스 기사 자체에 오류, 부정확한 정보, 또는 가짜 뉴스가 포함될 수 있으며, AI는 이러한 내용을 완벽하게 걸러내지 못할 수 있습니다.
따라서 제공되는 정보에 일부 오류나 편향이 있을 수 있으므로, 중요한 정보는 반드시 다른 출처를 통해 교차 검증하시기 바랍니다.

문의하기 : help@durumis.com