michgan software studio

13명이 돈을 모았더니 63만원이 되었다.


이런 상황이라고 합시다. 누가 얼마를 냈을까가 궁금하지 않습니까?
그래서 축의금 계산기를 짜봤습니다. 거창하게 들어갈 것도 없고, 적당히 콘솔 프로그램으로 만들어보죠.

축의금은 일반적으로 한 사람당 3, 5, 10만원을 내므로, for loop 3개로 구성하면 되겠군요.

        public static void Main()
        {
            int total = 63;
            int people = 13;

            for(int a=0; a <= (total/3); ++a)
            {
                for(int b=0; b <= (total/5); ++b)
                {
                    for(int c=0; c <= (total/10); ++c)
                    {
                        int s = a*3 + b*5 + c*10;
                        if (s == total && (a+b+c) == people)
                        {
                            Console.WriteLine("3={0}, 5={1}, 10={2}", a, b, c);
                        }
                    }
                }
            }
        }


이 정도면 원하는 결과를 계산할 수 있을 듯 합니다.
답은 두 개 뿐이네요.
3=1, 5=12 혹은 3=6, 5=5, 10=2 입니다. 경우의 수가 의외로 적네요. 13명의 이름 명단이 있으면 평소 행실이랑 친분 관계를 고려하면 누가 얼마를 냈는지 쉽게 짐작 가능하겠습니다.


자, 개인적인 호기심은 여기서 끝났는데, 프로그래머의 오기가 발동하는군요. 이 문제를 풀기 위한 좀 더 nice한 해결책은 없을까 궁금합니다. 3중 for 루프가 맘에 걸려요. 사실 이 알고리즘은 자판기에서 거스름돈을 뱉어 내기 위한 알고리즘하고 비슷하죠. 조금 다른 점은 모든 경우의 수를 다 살펴봐야 한다는 점 정도?

축의금의 개수를 가변적으로 만들려면, 재귀 호출을 사용하는 알고리즘이 제일 간단하겠군요.

        public static void Main()
        {
            Recurse(new int[coins.Length], 0, 63);
        }

        static int[] coins = new int[] { 3, 5, 7, 10 };
        static int people = 13;
        static void Recurse(int[] key, int depth, int remain)
        {
            int coin = coins[depth];
            int maxLoop = remain / coin;
            int[] newKey = (int[])key.Clone();
            for (int i = 0; i <= maxLoop; ++i)
            {
                int newRemain = remain - i * coin;
                newKey[depth] = i;

                if (depth + 1 == coins.Length)
                {
                    var n = newKey.Aggregate(0, delegate(int c, int r)
                    {
                        return c + r;
                    });
                    if (n == people && newRemain == 0)
                    {
                        StringBuilder sb = new StringBuilder();
                        for (int j = 0; j < coins.Length; ++j)
                        {
                            sb.AppendFormat("{0}={1} ", coins[j], newKey[j]);
                        }
                        Console.WriteLine(sb.ToString());
                    }
                }
                else
                {
                    Recurse(newKey, depth + 1, newRemain);
                }
            }
        }

얼렁 뚱땅 만들어진 것 같습니다. 7만원짜리 축의금을 추가하면, 가능한 경우의 수가 10개로 확 늘어나는군요. 설마 누가 7만원을 냈겠어. ㅎㅎ

사실 위의 알고리즘도 마음에 쏙 들지는 않습니다. 특히 key를 newKey로 복사하는 부분, 이거 굳이 왜 이렇게 했는지 궁금하지 않으십니까. 그 이유는 parallelize를 염두에 두고 있기 때문입니다. Parallel foreach 구문을 사용해서 돌리면 쿼드 코어 CPU를 100% 활용할 수 있는 축의금 뒷조사 프로그램이 완성될 것입니다.

그러나 .... VS2010을 깔기 귀찮기 때문에 패스.
저작자 표시 비영리 동일 조건 변경 허락

.net compact framework에는 (3.5까지도) System.Threading.Semaphore 클래스가 빠져 있습니다. 그렇다고 Windows Mobile에서 Semaphore를 쓸 수 없는 것은 아닙니다. CE용의 Win32 API로는 CreateSemaphore 와 ReleaseSemaphore가 존재하기 때문이죠.

따라서 Semaphore 클래스가 필요하다면 Win32 API를 P/Invoke로 호출하는 wrapper 클래스를 만들면 되겠습니다. 

이 소스에서는 System.Threading namespace에 직접 Semaphore를 추가하고 있습니다. 원래는 일개 평민은 System namespace를 건드리면 안 되겠지만, .net CF와 일반 프레임워크 간의 호환성을 위해서 침범했다고 합시다.

주의: 내부적으로 호출하는 Win32 함수에 대한 에러 처리가 되어 있지 않습니다.

코드 보기


저작자 표시 비영리 동일 조건 변경 허락


Windows Mobile 6.0 SDK 를 설치하셨다면, 아래 위치에 샘플 소스코드가 같이 설치됩니다.

64 비트 Windows의 경우:
C:\Program Files (x86)\Windows Mobile 6 SDK\Samples\PocketPC\CS\GPS

32 비트 Windows의 경우:
C:\Program Files\Windows Mobile 6 SDK\Samples\PocketPC\CS\GPS

위도, 경도, 고도 정도만 측정하는 경우라면 이 소스를 사용해도 괜찮을거 같네요.

저작자 표시 비영리 동일 조건 변경 허락


이번 글에서는 Windows Mobile 휴대폰에서 기지국 정보를 구하는 방법을 소개합니다.

3G 네트워크에서는 기지국 하나가 커버할 수 있는 반경이 2-3km 정도 되기 때문에 기지국 정보만으로도 제한적인 위치 정보 기반 서비스를 할 수 있습니다. 특히 지하에서는 전파 전달이 잘 되지 않기 때문에 곳곳에 기지국을 설치해야 하죠. 때문에 기지국 ID를 알아낼 수 있다면 사용자가 현재 어느 지하철 역에 있는지, 어느 쇼핑몰 안에 있는지, 어느 대학교 안에 있는지를 구분해서 서비스를 제공할 수 있을 것입니다. 지하철에서는 배차 정보나 행선지 찾기 서비스에 응용할 수도 있고, 쇼핑몰에서는 약도나 상품 검색 서비스를 제공할 수 있을 것입니다.

C#으로 Cell ID를 가져오는 방법은 생각보다 복잡합니다. P/Invoke 라는 기술을 사용해야 하는데요. C 언어와 32비트 CPU에 대한 이해가 없으면 헤맬 가능성이 높습니다.

스크린 샷은 다음과 같습니다. 심플하게 MCC/MNC, LAC, CellId 를 찍어 주고 있습니다.

MCC는 국가 코드입니다. 450번이 우리나라인가봐요.
MNC는 통신망 코드입니다. 5번은 SKT 입니다. 전체 코드는 http://en.wikipedia.org/wiki/Mobile_Network_Code 에 나열되어 있습니다.
LAC하고 CellId가 지역/기지국 정보입니다.


코드에 대한 설명을 시작하겠습니다. 

너무 길어서 가려둡니다


저작자 표시 비영리 동일 조건 변경 허락


요즘 모바일용으로 새로운 프로그램을 하나 구상하고 있는데, 데이타베이스 기능이 필수적으로 필요합니다. 물론 MS에서는 SQL Server Compact 3.5 for Windows Mobile을 공개하고 있지만, 핸드폰에 저장된 데이타베이스 파일을 PC로 옮겨서 사용하는 상황을 생각해보면, 핸드폰의 데이타와 동기화하기 위한 MS SQL Server 까지 필요하게 되더군요. 그럴 바엔 공개 라이브러리를 택하는 편이 낫겠다 싶어 조금 찾아보았습니다.

그러다 SQLite 라는 라이브러리를 발견하였습니다. 속도가 얼마나 빠른지는 모르겠지만, 완전히 public domain으로 공개된 소스라 제 마음에 쏙 들더군요. 다운 받을 수 있는 경로는 http://www.sqlite.org 입니다. 원 제작자들은 C 함수로 API를 만들어 놨습니다만, 훌륭한 사람들이 C# 용으로도 개발해 놨더군요. 이것 역시 public domain으로 공개되어 있습니다. http://sqlite.phxsoftware.com/ 에서 다운 받을 수 있습니다.

제가 테스트해본 쪽은 후자쪽입니다. 인스톨 파일을 다운 받아 설치하고, C# 프로젝트를 하나 만든 후에 Reference에 System.Data.SQLite 를 추가해줍니다.

아래는 테스트해볼 겸해서 직접 짜본 코드입니다.

데이타 베이스 파일을 하나 만들고, table을 생성한 뒤에, C:\windows 밑에 있는 파일 목록을 집어 넣습니다. 이후에 1024 ~ 1048576 의 크기를 갖는 파일을 SELECT 하는데, ORDER BY Size로 정렬합니다.

일단 이 정도만 되더라도 모바일에서 쓰기엔 괜찮을거 같다는 생각이 듭니다.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.SQLite;
using System.IO;

namespace sqlitecs
{
    class Program
    {
        static void Main(string[] args)
        {
            //  Creating a database.
            SQLiteConnection.CreateFile("test.db");

            //  Open the database.
            using (var connection = new SQLiteConnection("Data Source=test.db"))
            {
                connection.Open();                 try
                {
                    //  Create table.
                    using (SQLiteCommand command = connection.CreateCommand())
                    {
                        command.CommandText = "CREATE TABLE File (Name text, Size bigint, Modified datetime);";
                        command.ExecuteNonQuery();
                    }

                    //  Insert datas.
                    using (SQLiteCommand command = connection.CreateCommand())
                    {
                        command.CommandText = "INSERT INTO File VALUES (?,?,?);";
                        SQLiteParameter p1 = new SQLiteParameter();
                        SQLiteParameter p2 = new SQLiteParameter();
                        SQLiteParameter p3 = new SQLiteParameter();
                        command.Parameters.Add(p1);
                        command.Parameters.Add(p2);
                        command.Parameters.Add(p3);

                        foreach (var fi in (new DirectoryInfo("c:\\Windows")).GetFiles())
                        {
                            p1.Value = fi.Name;
                            p2.Value = fi.Length;
                            p3.Value = fi.LastWriteTimeUtc;
                            command.ExecuteNonQuery();
                        }
                    }

                    //  Select files by its size between 1k and 1M
                    using (SQLiteCommand command = connection.CreateCommand())
                    {
                        command.CommandText = "SELECT * from File where Size >= 1024 and Size <= 1048576 order by Size;";
                        using (SQLiteDataReader reader = command.ExecuteReader())
                        {
                            while(reader.HasRows && reader.Read())
                            {
                                Console.WriteLine("{0}, {1}, {2}", reader.GetString(0), reader.GetInt64(1), reader.GetDateTime(2));
                            }
                        }
                    }
                }
                finally
                {
                    connection.Close();
                }
            }
        }
    }
}



저작자 표시 비영리 동일 조건 변경 허락
  1. 김상진  댓글주소  수정/삭제  댓글쓰기

    안녕하세요 이번에 윈도우 모바일을 처음으로 개발하게 됬는데요

    c#에서 sqlite를 사용하려고 위와같이 따라 했는데 아래와 같은 오류가 나오더라구요.

    무슨 에러일 까요? wm쪽은 처음이다 보니 경험도 없어 잘 모르겠네요.ㅠㅠ

    --------------------------------------------------------------------
    'System.Data.Common.DbConnection' 형식이 참조되지 않은 어셈블리에 정의되었습니다.
    'System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'
    어셈블리에 참조를 추가해야 합니다.

    2010/02/22 10:48
  2. 세상살기  댓글주소  수정/삭제  댓글쓰기

    좋은 정보 감사합니다..

    다만, 위의 Insert 문장은 트랜잭션 처리를 건별로 자동적으로 수행하기 때문에 속도가 무척 느립니다. c 버젼이나 php 버젼등에서는 begin ; loop {insert } ; commit ; 이런 순으로 바꾸어서 처리해서 속도를 올리죠.

    C#은 없는 줄 알았는데 아래처럼 하면 insert 속도가 대략 200배 정도 차이가 날 정도로 빨라지네요..;;;

    using (SQLiteTransaction dbTrans = connection.BeginTransaction())
    {
    using (SQLiteCommand command = connection.CreateCommand())
    {
    command.CommandText = "INSERT INTO File VALUES (?,?,?);";
    SQLiteParameter p1 = new SQLiteParameter();
    SQLiteParameter p2 = new SQLiteParameter();
    SQLiteParameter p3 = new SQLiteParameter();
    command.Parameters.Add(p1);
    command.Parameters.Add(p2);
    command.Parameters.Add(p3);
    foreach (var fi in (new DirectoryInfo("c:\\Windows")).GetFiles())
    {
    p1.Value = fi.Name;
    p2.Value = fi.Length;
    p3.Value = fi.LastWriteTimeUtc;
    command.ExecuteNonQuery();
    label1.Text = "B-INSERT TABLE";
    }
    }
    dbTrans.Commit();
    }

    2010/03/25 15:18

직접 만든 인코딩 변환 라이브러리의 속도를 테스트해보았습니다. MBCS에서 UNICODE로 혹은 그 반대로 매핑하는 2차원 배열을 만들어 놓고 참조하는 방식입니다. 테이블 데이타를 static 형식으로 코드에 삽입시켰더니, exe 파일이 400-500 kb 정도가 되는군요. (코드 페이지 = 932, 936, 949, 950)

꽁수를 안 쓰고 2바이트 매핑 테이블을 그대로 탑재시키면, 1M 정도였을 겁니다. 지금은 바이트 범위를 조사해서 필요 없는 부분은 저장하지 않는 방식을 사용합니다. gz 스트림으로 압축시키면 실행 파일의 크기를 더 줄일 수 있기(333k에서 256k로)는 한데, 용량 감소폭 대비 번거로움을 따져보면 그냥 쓰는게 나을거 같습니다.

아무튼 직접 만든 인코딩 변환 라이브러리의 속도를 비교해봅시다. 비교 대상은 System.Text.Encoding 객체입니다. libiconv도 하고 싶으나 솔직히 귀찮습니다.

400kb 정도 되는 한글 텍스트 파일(243943 유니코드 글자)을 가지고 MBCS로 인코딩하고 디코딩하는 속도를 재었습니다. 5번 재어서 평균 냈고 사용한 기종은 소니 엑스페리아 X1, Windows 6.1 한글판입니다. 컴파일러는 VisualStudio 2008 sp1 + Windows Mobile 6 SDK 입니다.

단위는 msec.
  내거 Enc  내거 Dec  C# Enc  C# Dec 
 Unicode   19 19   25    25 
 UnicodeBE   49 51  132    72 
 UTF-8   60  86    88  134 
 CP949 160  77  214    52 

Encoding: Unicode -> MBCS. 즉, WideCharToMultiByte
Decoding: MBCS -> Unicode, 즉, MultiByteToWideChar

의외로 MS가 제공하는 라이브러리가 느렸군요.

생각보다 C#이 빠르다는 사실을 알 수 있습니다. 물론 빠른 속도를 내기 위해서 코드를 좀 간결하게 바꾸는 작업을 진행하긴 했지만 말입니다. 이것이 바로 JIT의 힘입니다. 좋군요.
저작자 표시 비영리 동일 조건 변경 허락

1 
BLOG main image
michgan software studio
Copyright (c) 1992-2008 michgan
by michgan

카테고리

분류 전체보기 (60)
release (34)
document (23)
review (1)