순환 복잡성 이해


11

최근에 Cyclomatic Complexity 를 발견했으며 더 잘 이해하려고합니다.

복잡성을 계산하는 여러 가지 요소에 대한 실제 코딩 예제는 무엇입니까? 특히의 Wikipedia 방정식 M = E − N + 2P에 대해 다음 각 용어의 의미를 더 잘 이해하고 싶습니다.

  • E = 그래프의 가장자리 수
  • N = 그래프의 노드 수
  • P = 연결된 구성 요소 수

나는 E 또는 N 이 코드 블록에서 의사 결정 지점의 수 (예, 다른 경우, foreach 등) 일 수 있다고 생각하지만 어느 것이 다른 것이 무엇인지 또는 무엇을 의미하는지는 확실하지 않습니다. 또한 P 는 함수 호출과 클래스 인스턴스화를 의미 한다고 생각 하지만 볼 수있는 명확한 정의는 없습니다. 누군가가 각각의 명확한 코드 예를 통해 조금 더 밝을 수 있다면 도움이 될 것입니다.

후속 조치로서, Cyclomatic Complexity는 100 % 경로 적용에 필요한 단위 테스트 수와 직접적으로 관련이 있습니까? 예를 들어, 복잡도가 4 인 방법은 해당 방법을 다루기 위해 4 개의 단위 테스트가 필요하다는 것을 나타 냅니까?

마지막으로 정규 표현식이 Cyclomatic Complexity에 영향을 줍니까? 그렇다면 어떻게됩니까?


나는 당신이 Wikipedia에서 McCabe에 의해 원본을 얻을 수 있고 Google Books는 McCabe가 그의 원본 논문에 사용한 책을 얻을 것임을 발견했습니다. 흥미롭게도 McCabe는 원래 정리를 잘못 사용했다는 것을 알게 될 것입니다 (그리고 무향 그래프로 시작해야하며 혼란스럽게 설명합니다. 처음에는 강력하게 연결할 필요가 없습니다). 올바른 공식은 M = E + 1-N + P이지만 P는 항상 1이므로 적합합니다 ...) 현대 "예외 처리"는 해당 메트릭의 작업에 스패너를 던지는 것으로 생각됩니다.
David Tonhofer

... 그리고 재귀 호출은 어떻습니까? 함수 그래프를 통합합니까? "&&"와 같은 부울 연산자 단락은 어떻습니까? ref가 null이면 null을 생성하는 "ref? .x"와 같은 보호 연산자? 아, 또 다른 지표 일뿐입니다. 그러나 여기 작은 대학 프로젝트를위한 몇 가지 작업이 있습니다.
David Tonhofer

답변:


8

공식과 관련하여 : 노드는 상태를 나타내고 가장자리는 상태 변경을 나타냅니다. 모든 프로그램에서 명령문은 프로그램 상태를 변경합니다. 각 연속 명령문은 에지로 표시되며 명령문 실행 후 (또는 이전에 ...) 프로그램의 상태는 노드입니다.

분기 문이있는 경우 ( if예 :) 상태가 두 가지 방식으로 변경 될 수 있기 때문에 두 개의 노드가 나옵니다.

CCN (Cyclomatic Complexity Number)을 계산하는 또 다른 방법은 실행 그래프에있는 "영역"수를 계산하는 것입니다 (여기서 "독립 영역"은 다른 원을 포함하지 않는 원입니다). 이 경우 CCN은 독립 영역의 수에 1을 더한 것입니다 (이전 공식에서 제공하는 것과 정확히 같은 숫자 임).

CCN은 분기 적용 범위 또는 경로 적용 범위에 동일하게 사용됩니다. CCN은 단일 스레드 응용 프로그램에서 이론적으로 가능한 여러 가지 분기 경로의 수와 같습니다 ( " if x < 2 and x > 5 then" 와 같은 분기를 포함 할 수 있지만 도달 할 수없는 코드로 우수한 컴파일러에 의해 포착되어야 함). 최소한 그 수의 다른 테스트 사례가 있어야합니다 (일부 테스트 사례는 이전 사례에서 다룬 경로를 반복 할 수 있지만 각 사례가 단일 경로를 포함한다고 가정 할 때 더 많을 수 있음). 가능한 테스트 사례로 경로를 처리 할 수없는 경우 도달 할 수없는 코드를 발견했습니다 (비록 도달 할 수없는 이유 , x < 2 and x > 5어딘가에 어딘가 중첩 된 이유 를 실제로 증명해야 함 ).

정규 표현식에 관해서는 물론 다른 코드와 마찬가지로 영향을 미칩니다. 그러나 정규식 구문의 CCN은 단일 단위 테스트에서 다루기에는 너무 높을 수 있으며 정규식 엔진이 테스트되었다고 가정 할 수 있으며 테스트 요구에 대한 표현식의 분기 가능성을 무시할 수 있습니다 ( 물론 정규식 엔진).


2
+1 : 실제로 정규식 엔진이 테스트되었다는 것을 신뢰 해야합니다 . 당신이 그것을 신뢰하지 않으면, 당신 신뢰 하는 것을 얻으십시오 .
S.Lott

"CCN은 단일 스레드 응용 프로그램에서 가능한 여러 실행 경로의 수와 같습니다." CCN이 의미가 아닌 코드 토폴로지 에만 기반하기 때문에 잘못되었습니다 . 이러한 경로의 좋은 비율은 설정할 수없는 입력 상태를 요구하기 때문에 실행이 불가능할 수 있습니다 (예 : 일부 x 는 5 이고 2보다 작음). 솔직히 CCN을 사용하여 테스트 사례를 결정하는 것은 잘못된 것이라고 생각합니다. CCN은 개발자에게 "여기 선외로 갔을 수 있습니다. 리팩토링을 고려하십시오"라고 알려주는 숫자입니다. 그럼에도 불구하고 높은 CCN에 대한 합당한 이유가있을 수 있습니다.
David Tonhofer

1
@David는이를 해결하기 위해 문장을 추가했습니다. CCN은 브랜치 커버리지이며 낮은 수준에서 높은 CCN에 대한 적절한 이유 는 없습니다 (일반적으로 개별 기능별로 시행 할 것을 제안합니다).
littleadv

지점 적용 범위와 경로 적용 범위는 동일하지 않습니다. 지점 범위는 모든 지점을 커버하는 것을 목표로하는 반면 경로 범위는 모든 지점 조합을 커버하는 것을 목표로합니다.
mouviciel

13

내가 쓴대로 이것에 대한 몇 가지 언급 ...

구체적으로, M = E-N + 2P의 Wikipedia 방정식에 대해

그 방정식은 매우 잘못되었습니다 .

어떤 이유로 McCabe는 실제로 자신의 논문 ( "복잡성 측정", IEEE 공학 소프트웨어 공학, Vo .. SE-2, No.4, 1976 년 12 월)에 그것을 사용하지만, 정당화하지 않고 실제로 올바른 인용을 한 후에 첫 번째 페이지의 수식

v (G) = e-v + p

(여기서 수식 요소의 레이블이 변경되었습니다)

특히 McCabe는 C.Berge, Graphs 및 Hypergraphs (아래에서 G & HG로 약칭) 라는 책을 참조합니다 . 그 책에서 직접 :

정의 (G & HG의 27 페이지 하단) :

(방향이 지정되지 않은) 그래프 G의 순환 수 v (G) (연결이 끊긴 구성 요소가 여러 개있을 수 있음)는 다음과 같이 정의됩니다.

v (G) = e-v + p

여기서 e = 모서리 수, v = 꼭지점 수, p = 연결된 구성 요소 수

정리 (29 페이지의 G & HG 상단) (McCave에서는 사용되지 않음) :

그래프 G의 순환 수 v (G)는 최대 독립 사이클 수와 같습니다.

사이클 시작 그래프에서 서로 인접한 시퀀스에서 각각 두 개의 연속 정점과 동일한 정점에서 종료 정점의 서열이다.

직관적으로, 보행을 중첩하여 다른 사이클에서 사이클을 구성 할 수없는 경우 사이클 세트독립적 입니다.

정리 (G & HG의 29 페이지 중간) (Mcabe에서 사용) :

강하게 연결된 그래프 G에서 사이클로 매틱 수는 선형 독립 회로의 최대 수와 같습니다.

회로는 허용 정점과 가장자리없이 반복과 순환이다.

지정된 방향으로 가장자리를 통과하여 다른 모든 정점에서 모든 정점에 도달 할 수 있으면 방향 그래프가 강력하게 연결 된다고합니다 .

여기서 우리는 무 방향 그래프 에서 강하게 연결된 그래프 로 전달했습니다 (이것은 ... Berge가 이것을 완전히 명확하게하지는 않습니다)

McCabe는 이제 위 정리를 적용하여 "McCabe Cyclomatic Complexity Number"(CCN)를 계산하는 간단한 방법을 도출합니다.

프로 시저의 "점프 토폴로지"를 나타내는 지시 그래프 (명령 흐름 그래프)가 주어지면, 고유 한 진입 점 을 나타내는 지정된 정점과 고유 한 종료점을 나타내는 지정된 정점이 있습니다 (출구 점 정점은 "구성되어야"할 수 있습니다. 여러 반환의 경우 추가)) 종료점 정점에서 시작점 정점으로 향한 가장자리를 추가하여 다른 정점에서 진입 점 정점에 도달 할 수 있도록하여 강하게 연결된 그래프를 만듭니다.

McCabe는 수정 된 명령 흐름 그래프의 순환적인 숫자가 "최소한의 경로 수"라는 직관적 인 개념에 부합한다는 점을 혼란스럽게 생각합니다. 따라서 우리는 그 숫자를 복잡성 척도로 사용해야합니다.

멋지다.

수정 된 명령 흐름 그래프의 순환 복잡도 수는 무 방향 그래프에서 "가장 작은"회로를 계산하여 확인할 수 있습니다. 이것은 사람이나 기계에 의해 특히 어렵지 않지만 위의 정리를 적용하면 더 쉽게 결정할 수 있습니다.

v (G) = e-v + p

가장자리의 방향성을 무시하는 경우

모든 경우에 우리는 단일 절차 만 고려하므로 전체 그래프에는 연결된 구성 요소가 하나뿐입니다.

v (G) = e-v + 1.

"exit-to-entry"edge가 추가되지 않은 원래 그래프 고려 하면 다음과 같이 간단하게 얻을 수 있습니다.

ṽ (G) = ẽ-v + 2

ẽ = e-1로

그의 논문에서 McCabe의 예를 사용하여 설명해 보겠습니다.

맥 카베의 예

여기에 우리가 있습니다 :

  • e = 10
  • v = 6
  • p = 1 (하나의 구성 요소)
  • v (G) = 5 (5 사이클을 명확하게 세고 있음)

순환 수에 대한 공식은 다음과 같습니다.

v (G) = e-v + p

이것은 5 = 10-6 + 1을 산출하므로 정확합니다!

그의 논문에 주어진 "McCabe 순환 복잡도 수"는

5 = 9-6 + 2 (어떻게 논문에 대한 자세한 설명은 없습니다)

이것은 정확하지만 (v (G)를 산출합니다) 잘못된 이유로 인해 사용됩니다.

ṽ (G) = ẽ-v + 2

따라서 ṽ (G) = v (G) ... 휴!

하지만이 방법이 좋은가요?

두 단어로 :별로

  • 특히 예외 처리 및 재귀가 그림에 들어간 경우 절차의 "명령 흐름 그래프"를 설정하는 방법이 완전히 명확하지 않습니다. McCabe는 그의 아이디어를 재귀, 예외 및 간단한 실행 구조가없는 언어 인 FORTRAN 66으로 작성된 코드에 적용했습니다 .
  • 결정이있는 절차와 루프가있는 절차가 동일한 CCN을 생성한다는 사실은 좋은 신호가 아닙니다.

여기에 이미지 설명을 입력하십시오


1
@JayElston 잘 잡았습니다. 참으로 그렇습니다. 결정된!
David Tonhofer 1

1
원본 용지에 연결하기위한 +1 그 당시에 작성된 많은 논문은 모든 중간 수준의 프로그래머가 읽을 수 있으며 읽어야합니다.
Daniel T.

1

후속 조치로서, Cyclomatic Complexity는 100 % 경로 적용에 필요한 단위 테스트 수와 직접적으로 관련이 있습니까?

예, 기본적으로 또한 리팩토링시기를 나타내는 지표로 순환 복잡도를 사용하는 것이 좋습니다. 내 경험상 CC가 낮을수록 테스트 가능성과 재사용 가능성이 크게 증가합니다 (실용적이어야하지만 과잉 리팩터링하지 않아야하지만 일부 방법은 특성상 CC가 높을 수 있습니다). 보다 낮은).

마지막으로 정규 표현식이 Cyclomatic Complexity에 영향을 줍니까? 그렇다면 어떻게됩니까?

그렇습니다. 대부분의 코드 분석 도구는 이러한 방식으로 고려하지 않지만 정확하고 싶다면 정규식은 유한 상태 기계 일 뿐이므로 CC는 FSM 그래프에서 계산할 수 있지만 상당히 큰 숫자라고 생각합니다.


+1-RegExes에 대한 CC를 계산하는 것은 재미있는 일이 아니라고 생각합니다.
VirtuosiMedia
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.