본문 바로가기

C# WPF

C# WPF 그래프 그리기(시간복잡도 그래프)

그래프를 그려보겠습니다.

알고리즘 시간에 많이 나오는 시간 복잡도 그래프를 프로그램으로 그려보려고 합니다. 물론 어떤 함수도 그릴 수 있습니다.

최종 프로그램의 모습입니다. x 축은 800까지 표시하고, y축은 6000까지 표시하도록 했습니다. 실제 nlogn 이나 n^2 정도만 되어도 매우 큰 값이 되기 때문에 y 축은 크기를 1/10으로 축소해서 그렸습니다. 즉 픽셀 하나가 10의 값이 됩니다.

그래프가 그려지는 모습을 보기위해서 타이머를 사용했습니다. 0.01초에 한개의 점씩 그려줍니다. x 축이나 y축의 범위를 넘어서면 타이머를 Stop 시킵니다.

 

WPF 파일은 다음과 같습니다.

<Window x:Class="TimeComplexty.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="TimeComplexity" SizeToContent="WidthAndHeight" ResizeMode="CanMinimize">
    <Grid Margin="10" VerticalAlignment="Top">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto"/>
            <ColumnDefinition Width="Auto"/>
        </Grid.ColumnDefinitions>
        <Canvas Grid.Row="0" Grid.Column="1" Name="Plotter" Width="800" Height="600" ClipToBounds="True">
            <Polyline Name="logLine" Stroke="LightSteelBlue" StrokeThickness="2.0" />
            <Polyline Name="linearLine" Stroke="SteelBlue" StrokeThickness="2.0" />
            <Polyline Name="nlogLine" Stroke="LightSteelBlue" StrokeThickness="1.0" />
            <Polyline Name="quadraticLine" Stroke="SteelBlue" StrokeThickness="2.0" />
            <Polyline Name="cubicLine" Stroke="LightSteelBlue" StrokeThickness="2.0" />
            <Polyline Name="expLine" Stroke="Red" StrokeThickness="1.0" />
            <Polyline Name="GridLine" Stroke="Black" StrokeThickness="1.0" StrokeDashArray="4 3" />
        </Canvas>
        <TextBlock Grid.Row="1" Grid.Column="1" HorizontalAlignment="Right">N</TextBlock>
        <TextBlock Grid.Row="0" Grid.Column="0" HorizontalAlignment="Left">f</TextBlock>
        <TextBlock Grid.Row="1" Grid.Column="0" HorizontalAlignment="Right">0</TextBlock>
        <TextBlock Grid.Row="0" Grid.Column="1" HorizontalAlignment="Center" FontSize="20">Time Complexity Graphs</TextBlock>
        <Line Stroke="Black" Y2="600" StrokeThickness="2" HorizontalAlignment="Right" VerticalAlignment="Stretch"/>
        <Line Grid.Column="1"  Stroke="Black" X2="800" StrokeThickness="2" VerticalAlignment="Bottom" HorizontalAlignment="Stretch"/>
        <StackPanel Grid.Row="2" Grid.Column="1" Orientation="Horizontal" HorizontalAlignment="Center">
            <Button Width="70" Margin="10,0,10,10" Click="logN_Click">logN</Button>
            <Button Width="70" Margin="10,0,10,10" Click="N_Click">N</Button>
            <Button Width="70" Margin="10,0,10,10" Click="NlogN_Click">NlogN</Button>
            <Button Width="70" Margin="10,0,10,10" Click="N2_Click">N^2</Button>
            <Button Width="70" Margin="10,0,10,10" Click="N3_Click">N^3</Button>
            <Button Width="70" Margin="10,0,10,10" Click="eN_Click">2^N</Button>
            <Button Width="70" Margin="10,0,10,10" Click="All_Click">All</Button>
            <Button Width="70" Margin="10,0,10,10" Click="Quit_Click">Quit</Button>
        </StackPanel>
       
    </Grid>
</Window>

 

C# 파일입니다. 먼저 Canvas에 그리드와 축을 그립니다.

        public MainWindow()
        {
            InitializeComponent();
            DrawGridLine();
            DrawAxis();
        }

        private void DrawGridLine()
        {
            for (int i = 100; i < Plotter.Width; i += 100)
            {
                Line l = new Line();
                l.X1 = i; l.Y1 = 0;
                l.X2 = i; l.Y2 = Plotter.Height;
                l.Stroke = Brushes.LightGreen;
                l.StrokeDashArray = DoubleCollection.Parse("4, 3");
                Plotter.Children.Add(l);
            }

            for (int i = 100; i < Plotter.Width; i += 100)
            {
                Line l = new Line();
                l.X1 = 0; l.Y1 = i;
                l.X2 = Plotter.Width; l.Y2 = i;
                l.Stroke = Brushes.LightGreen;
                l.StrokeDashArray = DoubleCollection.Parse("4, 3");
                Plotter.Children.Add(l);
            }
        }
       
        private void DrawAxis()
        {
            for (int i = 1; i <= 6; i++) // 세로축
            {
                TextBlock txt = new TextBlock();
                txt.Text = (i * 1000).ToString();
                txt.Margin = new Thickness(10, Plotter.Height - i*100, 0, 0);
                Plotter.Children.Add(txt);
            }
            for (int i = 1; i <= 8; i++) // 가로축
            {
                TextBlock txt = new TextBlock();
                txt.Text = (i * 100).ToString();
                if(i==8)
                    txt.Margin = new Thickness(i * 100 - 20, Plotter.Height - 20, 0, 0);
                else
                    txt.Margin = new Thickness(i*100, Plotter.Height - 20, 0, 0);
                Plotter.Children.Add(txt);
            }
        }

다음은 각 함수에 대한 타이머를 정의하고, 버튼이 눌렸을 때의 이벤트처리함수를 하나씩 만들어 줍니다.

        DispatcherTimer tLog = null;
        DispatcherTimer tN = null;
        DispatcherTimer tNLog = null;
        DispatcherTimer tN2 = null;
        DispatcherTimer tN3 = null;
        DispatcherTimer teN = null;

        // ----------------------------------------------------------------------Log
        private void logN_Click(object sender, RoutedEventArgs e)
        {           
            tLog = new DispatcherTimer();
            tLog.Interval = new TimeSpan(0, 0, 0, 0, 10);  // 0.01초
            tLog.Tick += tLog_Tick;
            tLog.Start();
        }

        double x = 1.0;
        double dx = 1.0;

        void tLog_Tick(object sender, EventArgs e)
        {
            x += dx;
            if (x >= Plotter.Width)
            {
                tLog.Stop();
                TextBlock txt = new TextBlock();
                txt.Text = "Log(" + x.ToString() + ") = " + Math.Log(x, 2.0).ToString();
                txt.Margin = new Thickness(600, 100, 0, 0);
                Plotter.Children.Add(txt);
            }
            logLine.Points.Add(new Point(x, Plotter.Height - Math.Log(x, 2.0)));
            Console.WriteLine("{0} {1}", x, Math.Log(x, 2.0));
        }

 

위의 코드는 로그함수를 처리한 것입니다. 다른 모든 버튼에도 위와 같이 함수를 만들어 줍니다. 그래프로 그릴 함수가 6개인데, 처리하는 부분이 모두 비슷합니다. 더 효율적으로 코딩할 방법이 있을 것 같은데... 알려주시면 감사하겠습니다.

BeeEye Dmu