모름

https://www.acmicpc.net/problem/2438

 

2438번: 별 찍기 - 1

첫째 줄에는 별 1개, 둘째 줄에는 별 2개, N번째 줄에는 별 N개를 찍는 문제

www.acmicpc.net

문제

첫째 줄에는 별 1개, 둘째 줄에는 별 2개, N번째 줄에는 별 N개를 찍는 문제

입력

첫째 줄에 N(1 ≤ N ≤ 100)이 주어진다.

출력

첫째 줄부터 N번째 줄까지 차례대로 별을 출력한다.

 

코드

using System;
using System.Collections.Generic;
using System.Text;
using System.IO;

class Program {
    static void Main()
    {
        //입력 첫째 줄 : 자연수 N을 입력받는다
        int n = int.Parse(Console.ReadLine());
        
       
        //출력 첫째 줄 : 첫째 줄부터 n번째 줄까지 차례대로 늘어나는 별을 출력한다.
        StringBuilder Output = new StringBuilder();
        string growingStar = "";
        for (int i = 0; i < n; i++)
        {
            string star = "*";
            growingStar += star;
            Output.Append(growingStar).Append("\n");
        }
        
        Console.Write(Output.ToString());
    }
}

일단 몇개까지 늘릴지의 기준이 되는 자연수 N을 입력받습니다. 그리고 출력될 별을 저장할 StringBuilder Output을 생성합니다. 그리고 늘어나는 별을 닮을 문자열을 생성합니다.

 

재료는 준비됐습니다. for문을 통해 n번째 줄까지 별을 하나씩 늘려가며 받아줍니다.

 

출력

정상출력됩니다.

코드(2)

using System;
using System.Text;

class Program {
    static void Main()
    {
        int n = int.Parse(Console.ReadLine());
        StringBuilder Output = new StringBuilder();
        string star = "*";
        for (int i = 0; i < n; i++)
        {
            Output.Append(star);
            Console.WriteLine(Output.ToString());
        }
    }
}

그런데 코드(1)에서의 반복문에서 growingStar값이 자꾸 조합되면서 더 느려지지 않을까 생각해서 위와같이 코드를 바꾸어봤습니다. 이 경우엔 Console.WriteLine()을 매번마다 실행하게됩니다.

 

코드(3)

using System;
using System.Text;

class Program {
    static void Main()
    {
        int n = int.Parse(Console.ReadLine());
        
        StringBuilder Output = new StringBuilder();
        StringBuilder Output2 = new StringBuilder();

        string star = "*";
        for (int i = 0; i < n; i++)
        {
            Output.Append(star);
            Output2.Append(Output.ToString() + "\n");
        }
        Console.WriteLine(Output2.ToString());
	}
}

이번 경우엔 Console.WriteLine을 매번 실행하지 않고, 스트링빌더를 하나 더 생성해서 두개의 문자열값에 값을 저장해놨다가 마지막에 출력하는 코드입니다.

 

코드비교

위 세 개의 코드 중 뭐가 가장 빠를까요. 갑자기 궁금증이 돋아서 테스트를 해봤습니다. 각각 자연수 10, 30, 50, 70, 100이 주어졌을때 속도의 차이가 어떤지 살펴보겠습니다.

 

10번의 경우

30번의 경우

50번의 경우

70번의 경우

 

100번의 경우

 

정리

갑자기 테스트를 하게됐습니다만, 어쨋든 흥미로운 결과입니다. 다시 한 번 코드별 차이를 정리해보겠습니다.

 

1. Code1의 경우

문자열 조합이 발생합니다. growingStar라는 string변수에서 for문이 반복될때마다 문자열 조합이 발생합니다. string은 참조변수 타입으로 문자열 조합이 이루어질때마다 Heap메모리상에 값이 새로 저장된다고 알고있습니다...(?) 정확하진 않습니다. 어쨋든 메모리상에 문자열이 조합될때마다 조합된 값에 따라 참조와 값이 새로 갱신되면서 이전의 값이 쓰레기로 남는다고만 이해하고 있습니다.

 

즉, Code1의 경우는 문자열 조합이 계속 발생하는 경우입니다.

 

2. Code2의 경우

이 경우는  Console.WriteLine()함수만 for문 반복에 따라 발생합니다.

 

3. Code3의 경우

이 경우 하나의 문자열에 계속되서 반복문이 진행될때마다 값이 추가됩니다. 하지만 문자열 조합이 아니라 value만 바뀌는 경우입니다.

 

 

 결론

테스트 횟수가 적을때는 1,3번의 경우가 별로 차이를 보지 않았습니다. 하지만 N값이 높아질수록 문자열 조합이 발생하는 경우 큰폭으로 속도가 늦어짐을 확인했습니다. 2번의 경우 처음엔 속도가 안나지만 값이 높아져도 안정적으로 비슷한 속도를 유지하는 느낌입니다. 3번의 경우 제일 빠른 속도를 보여줍니다. 문자열 조합도 없고, Console.WriteLine()도 없습니다.

 

즉, 문자열 조합이 많아지면 많아질수록 컴퓨터에 부하가 많이 온다는걸 알수있습니다. Console.WriteLine도 많이 불러와지면 느린 속도에 한 몫 합니다. 그래도 문자열조합으로 인해 쓰레기값이 쌓이는 것보다, Console.WriteLine을 반복하는게 더 낫습니다.

 

내부적으로 어떻게 돌아가는지는 미천한 제 지식으론 알지 못하겠습니다... 이상입니다.

 

아래는 속도 테스트에 사용한 코드입니다.

...더보기
using System;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;
using System.IO;

class Program {

    static void Code1(int n, string star)
    {
        StringBuilder Output = new StringBuilder();
        string growingStar = "";
        for (int i = 0; i < n; i++)
        {
            growingStar += star;
            Output.Append(growingStar).Append("\n");
        }
        Console.Write(Output.ToString());
    }

    static void Code2(int n, string star)
    {
        StringBuilder Output = new StringBuilder();
        for (int i = 0; i < n; i++)
        {
            Output.Append(star);
            Console.WriteLine(Output.ToString());
        }
    }

    static void Code3(int n, string star)
    {
        StringBuilder Output = new StringBuilder();
        StringBuilder Output2 = new StringBuilder();
        for (int i = 0; i < n; i++)
        {
            Output.Append(star);
            Output2.Append(Output.ToString() + "\n");
        }
        Console.WriteLine(Output2.ToString());
    }

    static void Main()
    {
        int n = int.Parse(Console.ReadLine());
        string star = "*";

        const int max = 100;

        var s1 = Stopwatch.StartNew();
        for (int i = 0; i < max; i++)
        {
            Code1(n, star);
        }
        s1.Stop();

        var s2 = Stopwatch.StartNew();
        for (int i = 0; i < max; i++)
        {
            Code2(n, star);
        }
        s2.Stop();

        var s3 = Stopwatch.StartNew();
        for (int i = 0; i < max; i++)
        {
            Code3(n, star);
        }
        s2.Stop();

        Console.WriteLine("자연수 N이 " + n + "인 경우");
        Console.WriteLine("Code 1: " + ((double)(s1.Elapsed.TotalMilliseconds * 100) / max).ToString("0.000 ns"));
        Console.WriteLine("Code 2: " + ((double)(s2.Elapsed.TotalMilliseconds * 100) / max).ToString("0.000 ns"));
        Console.WriteLine("Code 3: " + ((double)(s3.Elapsed.TotalMilliseconds * 100) / max).ToString("0.000 ns"));
        Console.Read();
    }
}

야메 실험이지만 나름 재밌는 시간이었습니다.