C# 코딩테스트 강좌, 소수 구하기

문제 설명

주어진 자연수 N 이하의 모든 소수를 구하시오.

소수란, 1보다 큰 자연수 중에서 1과 자기 자신 외에는 약수가 없는 수를 말합니다. 예를 들어, 2, 3, 5, 7, 11, 13, 17, 19 등이 소수입니다.

입력

자연수 N (2 ≤ N ≤ 10,000,000)

출력

N 이하의 모든 소수를 한 줄에 하나씩 출력

예제

입력:
10

출력:
2
3
5
7

문제 해결 전략

소수를 구하는 데 가장 널리 알려진 알고리즘 중 하나는 에라토스테네스의 체(Sieve of Eratosthenes) 알고리즘입니다. 이 알고리즘은 주어진 범위 내의 모든 소수를 효율적으로 찾을 수 있도록 돕습니다.

에라토스테네스의 체 알고리즘의 작동 방식은 다음과 같습니다:

  1. 2부터 N까지의 모든 숫자를 포함하는 배열을 생성합니다.
  2. 2는 소수이므로, 2의 배수는 소수가 아닐 것이므로 이를 배열에서 제거합니다.
  3. 다음으로, 남은 수 중 첫 번째 소수인 3을 선택하고, 3의 배수를 제거합니다.
  4. 이 과정을 배열의 끝까지 반복합니다. 남은 수가 소수입니다.

C# 구현

위의 방법을 사용하여 C# 언어로 소수를 구하는 프로그램을 구현해 보겠습니다:

using System;

class Program
{
    static void Main(string[] args)
    {
        // 사용자로부터 N 입력 받기
        Console.Write("자연수 N을 입력하세요 (2 ≤ N ≤ 10,000,000): ");
        int N = int.Parse(Console.ReadLine());

        // 소수 판별을 위한 벡터 배열 초기화
        bool[] isPrime = new bool[N + 1];

        // 모든 수를 소수로 초기화
        for (int i = 2; i <= N; i++)
        {
            isPrime[i] = true;
        }

        // 에라토스테네스의 체 알고리즘
        for (int i = 2; i * i <= N; i++)
        {
            if (isPrime[i])
            {
                // i의 배수를 false로 설정
                for (int j = i * i; j <= N; j += i)
                {
                    isPrime[j] = false;
                }
            }
        }

        // 결과 출력
        Console.WriteLine($"다음은 {N} 이하의 소수입니다:");
        for (int i = 2; i <= N; i++)
        {
            if (isPrime[i])
            {
                Console.WriteLine(i);
            }
        }
    }
}

코드 설명

  • 입력 받기: 사용자에게 N을 입력하도록 요청합니다. 입력받은 값을 정수로 변환합니다.
  • 소수 배열 초기화: N까지의 모든 수를 소수로 가정하여 true로 초기화하는 boolean 배열을 생성합니다.
  • 에라토스테네스의 체 구현: 두 개의 중첩 루프를 사용하여 소수가 아닌 숫자를 false로 설정합니다. 외부 루프는 2부터 시작하고, 내부 루프는 i의 배수를 false로 설정합니다.
  • 결과 출력: 최종적으로, 배열에서 true인 인덱스를 찾아 소수를 출력합니다.

복잡도 분석

이 알고리즘의 시간 복잡도는 O(n log log n)이며, 공간 복잡도는 O(n)입니다. 이는 큰 범위의 소수를 찾는데 매우 효율적입니다.

결론

이번 강좌에서는 소수를 구하는 알고리즘을 소개하고, C#을 사용하여 이를 구현하는 방법을 설명했습니다. 에라토스테네스의 체는 매우 효율적인 소수 판별 알고리즘으로, 실제 코딩 테스트에서도 자주 출제되는 문제입니다. 소수의 개념을 이해하고 알고리즘을 구현해보는 것은 프로그래밍 실력을 향상시키는 데 큰 도움이 될 것입니다.

작성자: 조광형

날짜: [작성일자]

C# 코딩테스트 강좌, 최솟값을 만드는 괄호 배치 찾기

문제 설명

괄호의 배치에 따라 수식의 계산 결과가 달라질 수 있습니다. 예를 들어,
2 * 3 + 4(2 * 3) + 4는 같은 결과를 주지만,
2 * (3 + 4)는 다른 결과를 줍니다.

주어진 수식에서 괄호의 위치에 따라 가능한 최소 값을 찾는 문제를 해결하려고 합니다.
수식은 숫자와 연산자(+, -, *)로 구성되어 있습니다.

문제 정의

정수로 이루어진 배열과 연산자가 주어질 때, 괄호를 적절히 배치하여
가장 작은 결과값을 만들어내는 방법을 찾는 과제를 수행하십시오.
구체적으로 말하자면, 수식이 다음과 같은 형태일 때:

2 * 3 - 5 + 7

이 수식을 괄호를 이용해 최솟값으로 만드는 방법을 찾아야 합니다.

문제 접근 방법

이 문제를 해결하기 위한 전략으로는 재귀 탐색을 사용하여 모든 괄호의 배치를 고려해야 합니다.
괄호를 배치할 수 있는 모든 조합을 생성하고, 각 경우에 대한 결과를 계산하여 가장 작은 값을 찾는 방식입니다.

1. 문제를 단순화하기

먼저, 아래와 같은 수식을 배열로 표현해보겠습니다.
예를 들어, 2 * 3 - 5 + 7
다음과 같은 구조로 변환됩니다:

[2, '*', 3, '-', 5, '+', 7]

2. 재귀적 접근

각 가능한 위치에 긴 구문을 제어하는 괄호를 배치하기 위해서,
재귀적인 함수를 사용하여 모든 경우를 탐색할 것입니다.
주요 단계는 다음과 같습니다:

  • 수식을 재귀적으로 나누어 괄호를 추가합니다.
  • 각 부분 표현의 결과를 계산합니다.
  • 계산된 결과 중 최솟값을 갱신합니다.

3. 코드 구현

아래는 C#으로 구현한 예제 코드입니다.


using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
        string expression = "2*3-5+7";
        int result = MinValue(expression);
        Console.WriteLine("최솟값: " + result);
    }

    static int MinValue(string expression)
    {
        var numbers = new List();
        var operators = new List();

        // 입력 문자열을 숫자와 연산자로 나눈다.
        for (int i = 0; i < expression.Length; i++)
        {
            if (char.IsDigit(expression[i]))
            {
                int num = 0;
                while (i < expression.Length && char.IsDigit(expression[i]))
                {
                    num = num * 10 + (expression[i] - '0');
                    i++;
                }
                numbers.Add(num);
                i--; // i 값을 조정
            }
            else
            {
                operators.Add(expression[i]);
            }
        }

        return CalculateMin(numbers, operators);
    }

    static int CalculateMin(List numbers, List operators)
    {
        // 기본 경우: 숫자가 하나만 남아 있을 때
        if (numbers.Count == 1)
            return numbers[0];

        int minValue = int.MaxValue;

        for (int i = 0; i < operators.Count; i++)
        {
            char op = operators[i];
            List leftNumbers = numbers.ToList();
            List rightNumbers = numbers.ToList();
            List leftOperators = operators.GetRange(0, i);
            List rightOperators = operators.GetRange(i + 1, operators.Count - i - 1);

            // 연산자에 따라 왼쪽과 오른쪽 구문을 나눈다.
            int leftValue = CalculateMin(leftNumbers.GetRange(0, i + 1), leftOperators);
            int rightValue = CalculateMin(rightNumbers.GetRange(i + 1, rightNumbers.Count - i - 1), rightOperators);

            // 연산을 수행한다.
            int result = PerformOperation(leftValue, rightValue, op);

            // 최솟값 갱신
            if (result < minValue)
                minValue = result;
        }

        return minValue;
    }

    static int PerformOperation(int left, int right, char op)
    {
        switch (op)
        {
            case '+':
                return left + right;
            case '-':
                return left - right;
            case '*':
                return left * right;
            default:
                throw new InvalidOperationException("지원하지 않는 연산자입니다.");
        }
    }
}

4. 코드 설명

위 코드에서 사용된 함수들은 다음과 같은 기능을 수행합니다:

  • MinValue: 주어진 수식 문자열을 숫자와 연산자로 나누고, 최솟값 계산을 위한 초기 데이터를 준비합니다.
  • CalculateMin: 가능한 모든 서브 표현을 재귀적으로 계산하며, 최솟값을 찾습니다.
  • PerformOperation: 두 숫자와 연산자를 이용해 계산을 수행합니다.

이 구조를 통해 모든 괄호 배치 조합을 탐색하고, 계산된 결과 중 최솟값을 도출하게 됩니다.

마무리

본 예제 문제를 통해 괄호 배치와 알고리즘적 접근 방법을 이해하는 데 도움이 되었기를 바랍니다.
이 방식과 코드를 바탕으로 다양한 수식에 대한 최솟값을 찾아내는 문제들을 해결해볼 수 있습니다.
항상 문제를 단순화하고 재귀적 접근 방식을 활용하는 연습을 통해 알고리즘적 사고를 극대화할 수 있습니다.

추가 학습 자료

알고리즘을 깊이 있는 학습을 위해서는 다음의 자료를 참고해보시기 바랍니다:

C# 코딩테스트 강좌, 내림차순으로 자릿수 정렬하기

코딩테스트 준비를 위한 알고리즘 문제 해결 능력을 강화해보세요.

문제 설명

주어진 정수 N이 있을 때, N을 구성하는 각각의 자릿수를 내림차순으로 정렬하여 새로운 정수로 반환하는 함수를 작성하십시오.

입력 조건

  • 0 이상 10억 이하의 정수 N이 주어집니다.

출력 조건

  • 정수 N의 각 자릿수를 내림차순으로 정렬한 정수를 반환합니다.

예제

입력 예시

N = 118372

출력 예시

873211

문제 접근 방법

이 문제를 해결하기 위해서는 다음과 같은 절차가 필요합니다.

  1. 정수 N을 문자열로 변환합니다.
  2. 각 자릿수를 리스트에 저장합니다.
  3. 리스트를 내림차순으로 정렬합니다.
  4. 정렬된 리스트의 요소들을 다시 문자열로 결합한 후, 정수로 변환합니다.

C# 코드 구현

아래는 주어진 문제를 해결하기 위한 C# 코드입니다.

            
                using System;
                using System.Linq;

                public class Program
                {
                    public static void Main(string[] args)
                    {
                        int N = 118372;
                        Console.WriteLine(SortDigitsDescending(N));
                    }

                    public static int SortDigitsDescending(int n)
                    {
                        // 정수를 문자열로 변환
                        var digits = n.ToString().ToCharArray();

                        // 문자열을 내림차순으로 정렬
                        Array.Sort(digits);
                        Array.Reverse(digits);

                        // 정렬된 문자열을 다시 정수로 변환
                        return int.Parse(new string(digits));
                    }
                }
            
        

코드 설명

위 코드에서 사용된 각 단계에 대해 설명하겠습니다.

1. 정수를 문자열로 변환

n.ToString().ToCharArray()를 사용하여 정수를 문자열로 변환합니다. 이후 ToCharArray() 메소드를 통해 각 자릿수를 문자 배열로 변환합니다.

2. 자릿수 정렬

Array.Sort(digits)를 호출하여 자릿수를 오름차순으로 정렬합니다. 이후 Array.Reverse(digits)를 호출하여 내림차순으로 변경합니다.

3. 정수로 변환

최종적으로 new string(digits)를 통해 문자 배열을 문자열로 변환한 다음, int.Parse를 사용하여 정수로 변환합니다.

시간 복잡도

이 알고리즘의 시간 복잡도는 O(d log d)입니다. 여기서 d는 입력된 정수의 자릿수입니다. 자릿수를 정렬하는 데 걸리는 시간은 O(d log d)이고, 그 외의 연산들은 O(d)입니다.

결론

본 강좌에서는 주어진 정수를 내림차순으로 정렬하는 방법에 대해 알아보았습니다. C#의 기본적인 배열 조작과 문자열 변환을 통해 이 문제를 효과적으로 해결할 수 있었습니다. 기초적인 알고리즘 문제이지만, 다양한 응용 문제로 발전될 수 있는 만큼 연습을 통해 더 많은 문제를 풀어보는 것이 중요합니다.

© 2023 C# 코딩테스트 강좌. 모든 권리 보유.