필요할 때 솔루션에 도달하는 방법에 대한 설명을 출력하는 알고리즘 패턴


14

다음 시나리오는 여러 번 발생했습니다.

특정 문제를 해결하는 알고리즘을 프로그래밍했습니다. 제대로 작동하고 올바른 솔루션을 찾습니다. 이제 알고리즘에 "솔루션을 얻는 방법에 대한 자세한 설명을 작성하십시오"라고 알리는 옵션이 필요합니다. 내 목표는 온라인 데모, 자습서 클래스 등에서 알고리즘을 사용할 수 있도록하는 것입니다. 설명 없이도 실시간으로 알고리즘을 실행할 수있는 옵션이 필요합니다. 좋은 디자인 패턴은 무엇입니까?

예 : 최대 공약수를 구하기 위해이 방법을 구현한다고 가정 합니다. 현재 구현 된 메소드는 정답을 리턴하지만 설명은 없습니다. 메소드가 다음과 같이 조치를 설명하는 옵션을 갖고 싶습니다.

Initially, a=6 and b=4. The number of 2-factors, d, is initialized to 0.
a and b are both even, so we divide them by 2 and increment d by 1.
Now, a=3 and b=2.
a is odd but b is even, so we divide b by 2.
Now, a=3 and b=1.
a and b are both odd, so we replace a by (a-b)/2 = 1.
Now, a=1 and b=1.
a=b, so the GCD is a*2^d = 2.

콘솔과 웹 기반 응용 프로그램 모두에 쉽게 표시 될 수 있도록 출력을 반환해야합니다.

설명이 필요하지 않을 때 알고리즘의 실시간 성능을 손상시키지 않으면 서 필요한 경우 설명을 제공하는 좋은 패턴은 무엇입니까?

답변:


50

찾고있는 "패턴"을 "로깅"이라고합니다. 로깅 문장을 필요한만큼 자세하게 작성하면됩니다. 적절한 로깅 프레임 워크를 사용하면 런타임에이를 켜거나 끄고, 다양한 상세 레벨을 제공하거나, 다른 목적 (예 : 웹과 콘솔)에 맞게 출력을 조정할 수 있어야합니다.

이것이 현저한 성능 영향을 미치는 경우 (로깅이 해제 된 경우에도) 언어, 프레임 워크 및 특정 경우에 필요한 로깅 문의 수에 따라 달라질 수 있습니다. 컴파일 된 언어에서 이것이 실제로 문제가되는 경우 코드의 "로깅 변형"및 "비 로깅 변형"을 빌드하기 위해 컴파일러 스위치를 제공 할 수 있습니다. 그러나 먼저 측정하지 않고 "경우에 따라"최적화하지 않는 것이 좋습니다.


2
그것들은 로깅처럼 켜고 끄는 것이 아니지만 주석자체 문서화 코드 는 적어도 "자체를 설명하는 알고리즘"에 대한 질문에서 명예로운 언급을해야한다고 생각합니다.
candied_orange

9
@CandiedOrange 질문은 구체적으로 실제 런타임 값이 포함 된 "설명"을 요구합니다. 이 경우 의견은별로 도움이되지 않습니다.
메타 큐브

@metacubed 아 어서. 나는 그것이 로깅의 대안이라고 말하지 않았다. 질문 제목을보고 여기를 통과하는 트래픽에 대해 생각하십시오.
candied_orange

4
@ CandiedOrange : 질문 제목이 잘못되었다고 생각합니다. 그렇게 해석 될 수는 있지만 OP가 요구하는 것은 아닙니다. 그러나 제목을 수정하겠습니다.
Doc Brown

1
treelog 와 같은 것은 함수 호출의 전체 레코드를 생성하여 복잡한 계산을 설명하는 출력을 생성하도록 특별히 설계되었습니다.
거미 보리스

7

좋은 패턴은 관찰자입니다. https://ko.wikipedia.org/wiki/Observer_pattern

알고리즘에서 무언가를 출력하려는 ​​각 지점에서 관찰자에게 알립니다. 그런 다음 콘솔에서 텍스트를 출력하거나 HTML 엔진 / 아파치 등에 전송할 작업을 결정합니다.

프로그래밍 언어에 따라 빠르게 만드는 다른 방법이있을 수 있습니다. 예를 들어, Java에서는 (간결성을 위해 의사 코드로 처리하십시오. getter, setter와 함께 "올바르게"만드는 것은 독자에게 맡겨져 있습니다) :

interface AlgoLogObserver {
   public void observe(String message);
}

class AlgorithmXyz {   
   AlgoLogObserver observer = null;
   void runCalculation() {   
       if (observer!=null) { oberserver.observe("Hello"); }
       ...
   }   
}

...
algo = new AlgorithmXyz();
algo.observer = new ConsoleLoggingObserver();  // yes, yes make a 
                                               // setter instead, or use Ruby :-)
algo.runCalculation();

이것은 약간 장황하지만 확인하는 ==null것이 가능한 한 빠릅니다.

(주 일반적인 경우에, 것을 observer아마 될 것 Vector observers대신에 하나 이상의 관찰자 수 있도록, 이것은뿐만 아니라 물론 가능하며 더 많은 오버 헤드로 이어질하지 않습니다, 당신은 아직도 당신이 설정하는 것이 최적화에 넣을 수 있습니다 observers=null을하는 대신 비어 Vector있습니다.)

물론 달성하려는 목표에 따라 다양한 종류의 관찰자를 구현해야합니다. 거기에 타이밍 통계 등을 넣거나 다른 멋진 일을 할 수도 있습니다.


5

스트레이트 로깅의 약간의 개선으로, 한 번의 알고리즘 실행을 모델링하는 일종의 오브젝트를 작성하십시오. 코드가 흥미로운 일을 할 때마다이 컨테이너 객체에 "단계"를 추가하십시오. 알고리즘이 끝나면 컨테이너에서 누적 된 단계를 기록하십시오.

몇 가지 장점이 있습니다.

  1. 전체 실행을 하나의 로그 항목 으로 기록 할 수 있으며 , 다른 스레드가 알고리즘 단계 사이에 물건을 기록 할 가능성이있을 때 유용합니다.
  2. 이 클래스의 Java 버전 (단순히 "Debug"라고 함)에서 문자열 을 로그 항목으로 추가하지 않고 문자열 을 생성하는 람다 를 추가 합니다 . 이러한 람다는 실제 로깅이 발생하는 경우, 즉 디버그 개체가 로그 수준이 현재 활성화되어있는 것으로 확인 된 경우에만 평가됩니다. 이렇게하면 불필요하게 로그 문자열을 구성 할 때 성능 오버 헤드가 발생하지 않습니다.

편집 : 다른 사람들이 언급했듯이 람다는 오버 헤드가 있으므로이 오버 헤드가 로그 문자열을 구성하는 데 필요한 코드의 불필요한 평가보다 적은지 벤치 마크해야합니다 (로그 항목은 종종 단순한 리터럴이 아니지만 컨텍스트 정보를 얻는 것과 관련이 있습니다) 참여 대상).


2
물론 람다 를 만드는 오버 헤드가 있습니다 .
Sergio Tulentsev

1
세르지오는 당신의 논리의 어리 석음을 밝히지 만 완전히 설명하지는 않습니다. 로그 문자열을 구성의 성능 오버 헤드가있다 크기의 순서는 람다 구축의 성능 오버 헤드보다. 당신은 여기에서 매우 가난한 균형을 만든
Kyeotic

2
@Tyrsius : 신뢰할 수있는 벤치 마크가 있습니까? (당신이 연결하는 벤치 마크는 cf stackoverflow.com/questions/504103/…에 깊이 결함이 있습니다. )
meriton

1
@Tyrsius는 모두 특정 상황에 따라 다릅니다. 또한 더 관련성이 높은 반례를 제시 할 수도 있습니다 . String 버전이 Runnable보다 10 배 느리다는 것을 알 수 있습니다. 이 질문의 맥락에서 항상 문자열을 동적으로 구성하려고하기 때문에이 경우는 더 현실적입니다. 이를 위해서는 항상 Stringbuilder 객체를 생성해야하지만 Lambda를 사용하면 필요할 때만 (즉, 로깅이 켜져있을 때) 생성됩니다.
jhyot

1
람다는 오버 헤드가 있습니다. 그러나 게시 된 벤치 마크는 이와 관련하여 완전히 관련이 없습니다. 알고리즘 로깅에는 로깅을 건너 뛰었을 때 평가되지 않은 다른 코드의 평가가 포함되는 경우가 있습니다 (참여 객체에서 컨텍스트 정보 검색 등). 람다는 피하는 것이이 평가입니다. 그러나 당신이 옳습니다. 위의 대답은 람다 오버 헤드 가이 오버 헤드 보다 작다고 가정합니다 . 일관되게 테스트하지 않은 것입니다.
Cornel Masson

0

나는 보통 분기를 찾습니다. 즉, if 문을 찾습니다. 이것들은 내가 값을 평가한다는 것을 나타내므로 알고리즘의 흐름을 제어합니다. 이러한 각 발생 (각 조건)에서 선택한 경로와 선택한 이유를 기록 할 수 있습니다.

따라서 기본적으로 항목 값 (초기 상태), 선택한 모든 분기 (조건부) 및 선택한 분기 (임시 상태)를 입력 할 때의 값을 기록합니다.


1
이것은 설명이 필요 하지 않을 때 알고리즘의 실시간 성능을 해치지 않는 것에 대한 질문을 해결하려는 시도조차하지 않습니다
gnat

나는 그보다 더 일반적인 질문을하고 디자인 수준에서 대답했습니다. 그러나 이것이 우려되는 경우 로그에 인쇄할지 여부를 설정하려면 조건부 플래그를 조건부에 추가하십시오. 시작할 때이 플래그를 매개 변수로 설정하십시오.
Richard Tyregrim
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.