Microsoft MVP성태의 닷넷 이야기
Math: 3. "유클리드 호제법"과 "Bezout's identity" 구현 코드(C#) [링크 복사], [링크+제목 복사],
조회: 29524
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일
(연관된 글이 2개 있습니다.)

"유클리드 호제법"과 "Bezout's identity" 구현 코드(C#)


유클리드 호제법 (Euclidean algorithm)
; http://ko.wikipedia.org/wiki/%EC%9C%A0%ED%81%B4%EB%A6%AC%EB%93%9C_%ED%98%B8%EC%A0%9C%EB%B2%95

위에도 소스 코드가 공개되어 있지만, 워낙에 호제법이 명쾌해서 C# 코드로도 쉽게 옮길 수가 있습니다.

static void Main(string[] args)
{
    Console.WriteLine(GetResult(247, 962));
    Console.WriteLine(GetResult(963, 247));
}

private static string GetResult(int num1, int num2)
{
    int gcd = GetGreatestCommonDivisor(num1, num2);
    string numFormatter = "{{{0}, {1}}} == ";

    if (gcd == 1)
    {
        return string.Format(numFormatter + "Relatively Prime", num1, num2);
    }

    int lcm = num1 * num2 / gcd;

    return string.Format(numFormatter + "Greatest Common Divisor = {2}, Least Common Multiple = {3}",
        num1, num2, gcd, lcm);
}

static int GetGreatestCommonDivisor(int num1, int num2)
{
    if (num1 > num2)
    {
        return GetGreatestCommonDivisor(num2, num1);
    }

    int remainder = 0;

    do
    {
        remainder = num2 % num1;

        num2 = num1;
        num1 = remainder;
    } while (remainder != 0); // 호제법 구현 do/while 코드

    return num2;
}

/* 재귀 호출을 이용한 호제법
static int GetGreatestCommonDivisor(int num1, int num2)
{
    if (num2 == 0)
    {
        return num1;
    }

    return GetGreatestCommonDivisor(num2, num1 % num2)
}
*/

사실, 여기까지 할 거면 ^^ 이 글을 쓰지도 않았겠지요.

위의 위키피디아 글에 보면 "호제법의 확장"에 대해서도 이야기하고 있는데, 여기에 그대로 내용을 실어보면 다음과 같습니다.

"
정수 m, n의 최대공약수(Greatest Common Divisor)를 gcd(m,n)와 나타낼 때, 확장된 유클리드 호제법을 이용하여, am + bn = gcd(m,n)의 해가 되는 정수 a, b 짝을 찾아낼 수 있다.(a, b 중 한개는 보통 음수가 된다.) 이 식은 Bezout's identity 라고 한다. 위에서 든 예의 경우,

    1071 = 1 * 1029 + 42
    1029 = 24 * 42 + 21 
    42 = 2 * 21 
 
이므로

    21 = 1029 - 24 * 42 = 1029 - 24 * (1071 - 1 * 1029) = -24 * 1071 + 25 * 1029 
 
가 된다.
"

즉, 2개의 양수 a, b의 최대 공약수를 d라고 했을 때, d는 적절한 정수 r, s에 의해 "d = ar + bs"로 정리될 수 있다는 것인데요. 약간의 코딩을 추가하면 위의 최종 식을 구할 수도 있겠다는 생각이 들더군요.

이를 위해, 호제법을 구하는 코드에서 "a = bq + r"의 형태를 "r = a - bq"의 형태로 기억하는 구조체를 넣어두었습니다.

do
{
    remainder = num2 % num1;

    RemainderFormula form = new RemainderFormula();
    form.Remainder = remainder;
    form.SubtractOperand = num2;
    form.MultiplyOperand1 = num1;
    form.MultiplyOperand2 = (int)Math.Floor((double)num2 / num1);
    forms.Add(form);

    num2 = num1;
    num1 = remainder;

} while (remainder != 0);

forms.Remove(forms.Last());
forms.Reverse();

그다음, 아래와 같이 "Bezout's identity"를 구하는 코드를 추가했습니다.

Dictionary<int, int> counter = new Dictionary<int, int>();

int multiplier = 0;
foreach (var item in forms)
{
    if (counter.ContainsKey(item.SubtractOperand) == false)
    {
        counter[item.SubtractOperand] = 1 * ((multiplier == 0) ? 1 : multiplier);
    }
    else
    {
        counter[item.SubtractOperand]++;
    }

    if (counter.ContainsKey(item.MultiplyOperand1) == false)
    {
        counter[item.MultiplyOperand1] = -item.MultiplyOperand2;
    }
    else
    {
        counter[item.MultiplyOperand1] += (-item.MultiplyOperand2 * multiplier);
    }

    multiplier = counter[item.MultiplyOperand1];
}

sb.AppendLine(string.Format("\t\t{0} = {1}r + {2}s, when r == {3}, s == {4}",
    gcd, num1, num2, counter[num1], counter[num2]));

몇 가지 수를 가지고 테스트 해보니 ^^ 아래와 같이 결과가 잘 나오는 군요.

{247, 962} == Greatest Common Divisor = 13, Least Common Multiple = 18278
                13 = 247r + 962s, when r == -35, s == 9


{45, 126} == Greatest Common Divisor = 9, Least Common Multiple = 630
                9 = 45r + 126s, when r == 3, s == -1


{255, 315} == Greatest Common Divisor = 15, Least Common Multiple = 5355
                15 = 255r + 315s, when r == 5, s == -4


{288, 639} == Greatest Common Divisor = 9, Least Common Multiple = 20448
                9 = 288r + 639s, when r == 20, s == -9


{963, 247} == Relatively Prime

참고로, 위키피디아에 "Extended Euclidean algorithm"라고 해서 알고리즘 설명이 나오기는 하는데... 음... 제가 한 방식과는 다르군요.

function extended_gcd(a, b)
    x := 0    lastx := 1
    y := 1    lasty := 0
    while b ≠ 0
        quotient := a div b
        (a, b) := (b, a mod b)
        (x, lastx) := (lastx - quotient*x, x)
        (y, lasty) := (lasty - quotient*y, y)       
    return (lastx, lasty)

이를 C# 코드로 옮겨 보면 다음과 같습니다.

var tuple = GetExtendedGcd(num1, num2);
sb.AppendLine(string.Format("Extended Euclidean algorithm: r == {0}, s == {1}",
    tuple.Item2, tuple.Item1));

private static Tuple<int, int> GetExtendedGcd(int num1, int num2)
{
    if (num2 > num1)
    {
        return GetExtendedGcd(num2, num1);
    }

    int x = 0;
    int lastx = 1;
    int y = 1;
    int lasty = 0;

    int quotient = 0;

    int tempNum2, tempx, tempy;

    while (num2 != 0)
    {
        quotient = (int)Math.Floor((double)num1 / num2);

        tempNum2 = num2;
        num2 = num1 % num2;
        num1 = tempNum2;

        tempx = lastx - quotient * x;
        lastx = x;
        x = tempx;

        tempy = lasty - quotient * y;
        lasty = y;
        y = tempy;
    }

    return new Tuple<int,int>(lastx, lasty);
}

역시 머리 좋은 사람들은 다르군요. 동일한 결과를 내면서도 ^^ 제 것보다 더 간결합니다.

{247, 962} == Greatest Common Divisor = 13, Least Common Multiple = 18278
                13 = 247r + 962s, Extended Euclidean algorithm: r == -35, s == 9


{45, 126} == Greatest Common Divisor = 9, Least Common Multiple = 630
                9 = 45r + 126s, Extended Euclidean algorithm: r == 3, s == -1


{255, 315} == Greatest Common Divisor = 15, Least Common Multiple = 5355
                15 = 255r + 315s, Extended Euclidean algorithm: r == 5, s == -4


{288, 639} == Greatest Common Divisor = 9, Least Common Multiple = 20448
                9 = 288r + 639s, Extended Euclidean algorithm: r == 20, s == -9

첨부된 파일은 위의 코드를 포함한 예제 프로젝트입니다.




[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]

[연관 글]






[최초 등록일: ]
[최종 수정일: 9/15/2021]

Creative Commons License
이 저작물은 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
by SeongTae Jeong, mailto:techsharer at outlook.com

비밀번호

댓글 작성자
 




... 46  47  48  49  50  [51]  52  53  54  55  56  57  58  59  60  ...
NoWriterDateCnt.TitleFile(s)
12696정성태7/6/202117376VC++: 146. 운영체제의 스레드 문맥 교환(Context Switch)을 유사하게 구현하는 방법파일 다운로드2
12695정성태7/3/202117568VC++: 145. C 언어의 setjmp/longjmp 기능을 Thread Context를 이용해 유사하게 구현하는 방법파일 다운로드1
12694정성태7/2/202119134Java: 24. Azure - Spring Boot 앱을 Java SE(Embedded Web Server)로 호스팅 시 로그 파일 남기는 방법 [1]
12693정성태6/30/202116146오류 유형: 731. Azure Web App Site Extension - Failed to install web app extension [...]. {1}
12692정성태6/30/202116805디버깅 기술: 180. Azure - Web App의 비정상 종료 시 남겨지는 로그 확인
12691정성태6/30/202116692개발 환경 구성: 573. 테스트 용도이지만 테스트에 적합하지 않은 Azure D1 공유(shared) 요금제
12690정성태6/28/202117588Java: 23. Azure - 자바(Java)로 만드는 Web App Service - Tomcat 호스팅
12689정성태6/25/202119938오류 유형: 730. Windows Forms 디자이너 - The class Form1 can be designed, but is not the first class in the file. [1]
12688정성태6/24/202119274.NET Framework: 1073. C# - JSON 역/직렬화 시 리플렉션 손실을 없애는 JsonSrcGen [2]파일 다운로드1
12687정성태6/22/202116389오류 유형: 729. Invalid data: Invalid artifact, java se app service only supports .jar artifact
12686정성태6/21/202117725Java: 22. Azure - 자바(Java)로 만드는 Web App Service - Java SE (Embedded Web Server) 호스팅
12685정성태6/21/202119766Java: 21. Azure Web App Service에 배포된 Java 프로세스의 메모리 및 힙(Heap) 덤프 뜨는 방법
12684정성태6/19/202118031오류 유형: 728. Visual Studio 2022부터 DTE.get_Properties 속성 접근 시 System.MissingMethodException 예외 발생
12683정성태6/18/202118735VS.NET IDE: 166. Visual Studio 2022 - Windows Forms 프로젝트의 x86 DLL 컨트롤이 Designer에서 오류가 발생하는 문제 [1]파일 다운로드1
12682정성태6/18/202115202VS.NET IDE: 165. Visual Studio 2022를 위한 Extension 마이그레이션
12681정성태6/18/202116072오류 유형: 727. .NET 2.0 ~ 3.5 + x64 환경에서 System.EnterpriseServices 참조 시 CS8012 경고
12680정성태6/18/202118090오류 유형: 726. python2.7.exe 실행 시 0xc000007b 오류
12679정성태6/18/202117801COM 개체 관련: 23. CoInitializeSecurity의 전역 설정을 재정의하는 CoSetProxyBlanket 함수 사용법파일 다운로드1
12678정성태6/17/202116069.NET Framework: 1072. C# - CoCreateInstance 관련 Inteop 오류 정리파일 다운로드1
12677정성태6/17/202119619VC++: 144. 역공학을 통한 lxssmanager.dll의 ILxssSession 사용법 분석파일 다운로드1
12676정성태6/16/202118414VC++: 143. ionescu007/lxss github repo에 공개된 lxssmanager.dll의 CLSID_LxssUserSession/IID_ILxssSession 사용법파일 다운로드1
12675정성태6/16/202116609Java: 20. maven package 명령어 결과물로 (war가 아닌) jar 생성 방법
12674정성태6/15/202117636VC++: 142. DEFINE_GUID 사용법
12673정성태6/15/202117827Java: 19. IntelliJ - 자바(Java)로 만드는 Web App을 Tomcat에서 실행하는 방법
12672정성태6/15/202120062오류 유형: 725. IntelliJ에서 Java webapp 실행 시 "Address localhost:1099 is already in use" 오류
12671정성태6/15/202128972오류 유형: 724. Tomcat 실행 시 Failed to initialize connector [Connector[HTTP/1.1-8080]] 오류
... 46  47  48  49  50  [51]  52  53  54  55  56  57  58  59  60  ...