-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.xml
875 lines (641 loc) · 50.6 KB
/
index.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
<title>tantk land</title>
<link>https://o-tantk.github.io/</link>
<description>Recent content on tantk land</description>
<generator>Hugo -- gohugo.io</generator>
<language>euc-kr</language>
<managingEditor>[email protected] (tantk)</managingEditor>
<webMaster>[email protected] (tantk)</webMaster>
<lastBuildDate>Tue, 15 Nov 2016 22:45:08 +0900</lastBuildDate>
<atom:link href="https://o-tantk.github.io/index.xml" rel="self" type="application/rss+xml" />
<item>
<title>회전 행렬(Rotation matrix)의 유도</title>
<link>https://o-tantk.github.io/posts/derive-rotation-matrix/</link>
<pubDate>Tue, 15 Nov 2016 22:45:08 +0900</pubDate>
<author>[email protected] (tantk)</author>
<guid>https://o-tantk.github.io/posts/derive-rotation-matrix/</guid>
<description>
<script type="text/javascript"
src="https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML">
</script>
<h1 id="사설:d680e8a854a7cbad6d490c445cba2eba">사설</h1>
<p>회전 행렬(Rotation matrix)은 보통 한 번 구현해 놓고, 사용만 하기 때문에 좀처럼 외워지지 않았다. 특히 <code>cos</code>, <code>sin</code>의 위치는 기억해도 <code>-</code>가 어느 <code>sin</code>에 붙는지 자꾸 헷갈렸다.</p>
<p>그래서, 행렬식 자체를 외우기 보다 유도하는 방법을 외우기로 했다.</p>
<p>회전 행렬을 유도하는 방법에는 여러가지가 있다. 삼각형의 합동을 이용한 방법(고등수학), 벡터의 내적을 이용한 방법 등&hellip;. 이 방법들을 사용해도 전혀 어려울 것은 없지만, 팟 하고 떠올리기엔 조금 무리가 있다.</p>
<p>좀 더 쉬운 유도 방법을 찾아보니, 다행히 <strong>가장 쉽다</strong>고 할 수 있을 유도 방법이 존재했다. 바로 회전 행렬이 선형 변환(선형 사상)임을 이용해 유도하는 것이다.</p>
<h2 id="선형-변환-linear-transformation:d680e8a854a7cbad6d490c445cba2eba">선형 변환(Linear transformation)</h2>
<p>두 벡터 공간 사이의 변환 \( f \)와 임의의 상수 \( c \), 두 벡터 \( \alpha \), \( \beta \)가 다음을 만족하는 경우, \( f \)를 선형 변환이라 한다.</p>
<ul>
<li>\( f\left( \alpha +\beta \right) =f\left( \alpha \right) +f\left( \beta \right) \)</li>
<li>\( f\left( c\alpha \right) =cf\left( \alpha \right) \)</li>
</ul>
<p>선형 변환을 만족하는 대표적인 변환이 바로 회전이며, 확대(Scaling), 찌그러트림(Shear), 대칭(Reflection), 사영(Projection) 등도 여기에 해당한다.</p>
<p>\( \theta \)만큼 회전하고 \( \omega \)만큼 회전하든, \( \omega \)만큼 회전하고 \( \theta \)만큼 회전하든, 그 결과는 \( (\theta + \omega) \)임을 생각해보면, 회전이 선형 변환임을 금새 알 수 있다.</p>
<p>참고로 이동(Translation)은 선형 변환이 아니다. 때문에 하나의 행렬로 변환을 표현하기 위해, 동차좌표계(Homogeneous coordinates)를 이용한 아핀 변환(Affine transformation)이 사용되는 것이다.</p>
<h1 id="유도:d680e8a854a7cbad6d490c445cba2eba">유도</h1>
<p>먼저 기저 벡터인 \( ( 1, 0 ) \)과 \( ( 0, 1 ) \)를 \( \theta \)만큼 회전시켜 보자.
<img src="graph.png" alt="기저 벡터의 회전" />
그림과 같이 \( ( 1, 0 ) \)을 \( \theta \)만큼 회전하면 \( ( \cos { \theta }, \sin { \theta } ) \), \( ( 0, 1 ) \)을 \( \theta \)만큼 회전하면 \( ( -\sin { \theta }, \cos { \theta } ) \)이 된다.</p>
<p>그리고, 회전 행렬 \( R \)은 선형 변환이므로 다음이 성립된다.</p>
<div>$$
\begin{align*} R\begin{bmatrix} x \\ y \end{bmatrix} &=R\begin{bmatrix} x \\ 0 \end{bmatrix}+R\begin{bmatrix} 0 \\ y \end{bmatrix}\\ &=R\begin{bmatrix} 1 \\ 0 \end{bmatrix}x+R\begin{bmatrix} 0 \\ 1 \end{bmatrix}y \end{align*}
$$</div>
<p>여기서 \( R (1, 0) \)과 \( R (0, 1) \)이 바로 \( ( \cos { \theta }, \sin { \theta } ) \), \( ( -\sin { \theta }, \cos { \theta } ) \)이므로, 대입하면 유도가 끝난다.</p>
<div>$$
\begin{align*} R\begin{bmatrix} x \\ y \end{bmatrix} &=\begin{bmatrix} \cos { \theta } \\ \sin { \theta } \end{bmatrix}x+\begin{bmatrix} -\sin { \theta } \\ \cos { \theta } \end{bmatrix}y\\ &=\begin{bmatrix} \cos { \theta } & -\sin { \theta } \\ \sin { \theta } & \cos { \theta } \end{bmatrix}\begin{bmatrix} x \\ y \end{bmatrix} \\ \\ R &=\begin{bmatrix} \cos { \theta } & -\sin { \theta } \\ \sin { \theta } & \cos { \theta } \end{bmatrix} \end{align*}
$$</div>
<p>정말 쉽다.</p>
<p>3차원의 경우는 회전축에 대해 \( R \overrightarrow { v } = \overrightarrow { v } \)이므로 더 생각할 것도 없다.</p>
<div>$$
\begin{align*} { R }_{ z }\begin{bmatrix} x \\ y \\ z \end{bmatrix} &={ R }_{ z }\begin{bmatrix} x \\ 0 \\ 0 \end{bmatrix}+{ R }_{ z }\begin{bmatrix} 0 \\ y \\ 0 \end{bmatrix}+{ R }_{ z }\begin{bmatrix} 0 \\ 0 \\ z \end{bmatrix}\\ &={ R }_{ z }\begin{bmatrix} 1 \\ 0 \\ 0 \end{bmatrix}x+{ R }_{ z }\begin{bmatrix} 0 \\ 1 \\ 0 \end{bmatrix}y+{ R }_{ z }\begin{bmatrix} 0 \\ 0 \\ 1 \end{bmatrix}z\\ &=\begin{bmatrix} \cos { \theta } \\ \sin { \theta } \\ 0 \end{bmatrix}x+\begin{bmatrix} -\sin { \theta } \\ \cos { \theta } \\ 0 \end{bmatrix}y+\begin{bmatrix} 0 \\ 0 \\ 1 \end{bmatrix}z\\ &=\begin{bmatrix} \cos { \theta } & -\sin { \theta } & 0 \\ \sin { \theta } & \cos { \theta } & 0 \\ 0 & 0 & 1 \end{bmatrix}\begin{bmatrix} x \\ y \\ z \end{bmatrix} \\ \\ { R }_{ z } &=\begin{bmatrix} \cos { \theta } & -\sin { \theta } & 0 \\ \sin { \theta } & \cos { \theta } & 0 \\ 0 & 0 & 1 \end{bmatrix} \end{align*}
$$</div>
<p>같은 방법으로 \( { R }_{ x } \), \( { R }_{ y } \)도 쉽게 구할 수 있다.</p>
<h1 id="references:d680e8a854a7cbad6d490c445cba2eba">References</h1>
<ul>
<li><a href="https://ko.wikipedia.org/wiki/선형변환">https://ko.wikipedia.org/wiki/선형변환</a></li>
</ul>
</description>
</item>
<item>
<title>중앙값(Median) 찾기</title>
<link>https://o-tantk.github.io/posts/finding-median/</link>
<pubDate>Sat, 12 Nov 2016 01:47:05 +0900</pubDate>
<author>[email protected] (tantk)</author>
<guid>https://o-tantk.github.io/posts/finding-median/</guid>
<description>
<h1 id="중앙값-median:d680e8a854a7cbad6d490c445cba2eba">중앙값(Median)</h1>
<p>중앙값이란, 어떤 배열을 정렬했을 때 정 가운데에 위치하는 값을 말한다. (수학적으로는 확률분포가 0.5인 값이다.)<br />
예를들어, <code>[1, 2, 3]</code>의 배열에서는 <code>2</code>가 중앙값이다.</p>
<p>원소의 개수가 짝수인 경우엔, 가운데 두 원소의 평균이 중앙값이 된다.<br />
<code>[1, 2, 3, 4]</code>에서는 <code>2</code>와 <code>3</code>의 평균인 <code>2.5</code>가 중앙값인 것이다.</p>
<h2 id="중앙값-찾기:d680e8a854a7cbad6d490c445cba2eba">중앙값 찾기</h2>
<p>중앙값은 <strong>정렬된 배열</strong>에서 찾아야 한다.<br />
배열이 정렬되어 있다면 땡큐지만, 정렬되어 있지 않다면 정렬해야 하므로, 가장 먼저 정렬을 수행해야 한다.</p>
<p>정렬 후엔, 단순히 배열 크기의 절반에 해당하는 인덱스에 접근해 반환하면 된다.</p>
<pre><code class="language-c++">float median(vector&lt;int&gt; &amp;arr){
sort(arr.begin(), arr.end());
if(arr.size() % 2 == 1){
return (float)arr[arr.size() / 2];
}
else{
return ((float)arr[arr.size() / 2] + (float)arr[arr.size() / 2 + 1]) / 2.0f;
}
}
</code></pre>
<p><code>int</code>형 자료를 갖는 배열임에도 <code>float</code>형을 반환하는 이유는, 짝수개에 대한 중앙값의 정의 때문이다. 물론 소수점 이하가 필요 없다면 그대로 반환해도 되겠다. 반올림이 아닌 절삭인 것도 감안하고.</p>
<h2 id="실시간으로-중앙값-running-median-찾기:d680e8a854a7cbad6d490c445cba2eba">실시간으로 중앙값(Running Median) 찾기</h2>
<p>중앙값을 찾기 위해선 배열의 정렬이 선행되어야 한다. 그렇다면, 데이터가 지속적으로 추가/삭제되고 있는 상황에서 중앙값을 찾으려면 어떻게 해야 할까? 위의 방법을 이용한다면, 중앙값을 찾을 때마다 배열 전체에 대한 정렬을 시도하므로 엄청난 시간 낭비가 아닐 수 없다.</p>
<p>그래서 값을 찾을 때마다 정렬하기 보다는, 자료구조 자체를 바꿔야 하겠다. 수행 속도가 훨씬 빠른, 추가/삭제가 정렬과 함께 이루어지는 자료 구조를 이용하는 것이다. 추가/삭제가 정렬과 함께 이루어지는 자료 구조는 여러가지가 있지만, 중앙값을 찾는 데는 <a href="https://ko.wikipedia.org/wiki/힙_(자료_구조)">Heap</a>을 이용할 수 있다.</p>
<h3 id="알고리즘:d680e8a854a7cbad6d490c445cba2eba">알고리즘</h3>
<blockquote>
<ul>
<li>Max heap은 최대값에 바로 접근할 수 있다.</li>
<li>Min heap은 최소값에 바로 접근할 수 있다.</li>
</ul>
</blockquote>
<p>Heap의 두 성질 이용해, 중앙값보다 작은 값들은 Max heap에, 중앙값보다 큰 값들은 Min heap에 저장하는 방식으로 중앙값을 찾는 알고리즘을 구현할 수 있다. 아래의 상황을 보자.
<img src="diagram00.png" alt="첫 번째 단계" />
먼저, 작은 값과 큰 값을 나눌 기준이 필요하다. 중앙값이 아니라 굳이 기준이라 표현한 이유는, 개수가 짝수일 때를 고려해서이다.<br />
기준은 어차피 가운데에 있으니, 변수를 따로 만들 필요 없이 Max heap 또는 Min heap에 포함시키면 된다. 여기선 Max heap을 이용했다.
<img src="diagram01.png" alt="두 번째 단계" />
첫 번째 입력은 별 수 없이 기준이 된다. 홀수개이므로 중앙값은 <code>3</code>이다.<br />
나머지는 기준보다 작다면 Max heap에, 크다면 Min heap에 넣는다. 그리고, Max heap과 Min heap의 크기가 균형되도록 조절한다. 다만, 기준을 고려해 기준을 가진쪽이 하나는 더 클 수 있도록 허용한다.
<img src="diagram02.png" alt="세 번째 단계" />
<img src="diagram03.png" alt="네 번째 단계" />
<code>2</code>는 기준보다 작으니 Max heap에 넣지만 균형을 맞추기 위해 Max heap의 최대값을 이동시킨다. 짝수개이므로 중앙값은 <code>2</code>와 <code>3</code>의 평균이다.
<img src="diagram04.png" alt="다섯 번째 단계" />
<img src="diagram05.png" alt="여섯 번째 단계" />
<code>4</code>는 기준보다 크니 Min heap에 넣지만 균형을 맞추기 위해 Min heap의 최소값을 이동시킨다. 홀수개이므로 중앙값은 <code>3</code>이다.
<img src="diagram06.png" alt="일곱 번째 단계" />
<img src="diagram07.png" alt="여덟 번째 단계" />
<code>1</code>은 기준보다 작으니 Max heap에 넣지만 균형을 맞추기 위해 Max heap의 최대값을 이동시킨다. 짝수개이므로 중앙값은 <code>2</code>와 <code>3</code>의 평균이다.</p>
<p>입력이 들어올 때마다 한 번의 데이터 이동만으로 실시간으로 중앙값을 찾고 있다. Heap의 시간복잡도가 <code>O(log N)</code>이므로 최악의 경우라도 &lsquo;삽입 + 삭제 + 삽입&rsquo;의 <code>O(3 log N)</code>의 성능을 보인다.</p>
<h3 id="구현:d680e8a854a7cbad6d490c445cba2eba">구현</h3>
<p>Heap을 직접 구현하는 귀찮은 짓을 할 수도 있겠지만, STL에서 이미 <code>priority_queue</code>라는 이름으로 제공하고 있으므로 이용하면 된다.</p>
<pre><code class="language-c++">priority_queue&lt;int&gt; max_heap;
priority_queue&lt;int, vector&lt;int&gt;, greater&lt;int&gt; &gt; min_heap;
</code></pre>
<p><code>priority_queue</code>는 기본으로 선언하면 오름차순 정렬인 Max heap이 된다. <code>greater&lt;int&gt;</code>를 템플릿 인자로 주면 내림차순 정렬인 Min heap을 선언할 수 있다.<br />
두 번째 템플릿 인자는 <code>priority_queue</code>의 컨테이너를 결정한다. 무난하게 <code>vector</code>로 선언해주면 된다. <code>priority_queue</code>에 대한 자세한 설명을 원한다면 <a href="http://www.cplusplus.com/reference/queue/priority_queue/?kw=priority_queue">레퍼런스</a>를 참고하길 바란다.</p>
<p>다음의 코드는 <code>int number</code>에 <code>cin</code>을 통해서 지속적으로 값을 읽어들이는 상황이라 가정한 것이다.</p>
<pre><code class="language-c++">priority_queue&lt;int&gt; smaller; // Max heap
priority_queue&lt;int, vector&lt;int&gt;, greater&lt;int&gt; &gt; bigger; // Min heap
int number;
cin &gt;&gt; number; // 첫 번째 입력.
smaller.push(number); // 기준이 smaller에 있다고 가정하므로, 첫 입력은 smaller에 넣는다.
while(true){
cin &gt;&gt; number; // 두 번째 이후의 입력.
if(number &lt; smaller.top()){
// 기준보다 작은 경우 smaller에 넣는다.
smaller.push(number);
}
else{
// 기준보다 큰 경우 bigger에 넣는다.
bigger.push(number);
}
// smaller의 크기와 bigger의 크기가 같거나, smaller의 크기가 하나 더 크게 유지되도록 데이터를 옮긴다.
if(smaller.size() &lt; bigger.size()){
smaller.push(bigger.top());
bigger.pop();
}
else if(smaller.size() &gt; bigger.size() + 1){
bigger.push(smaller.top());
smaller.pop();
}
// smaller와 bigger의 크기가 같다면 총 개수는 짝수이다.
if(smaller.size() == bigger.size()){
cout &lt;&lt; ((float)smaller.top() + (float)bigger.top()) * 0.5f &lt;&lt; endl;
}
else{
cout &lt;&lt; smaller.top() &lt;&lt; endl;
}
}
</code></pre>
<h2 id="세-수-triple-의-중앙값-찾기:d680e8a854a7cbad6d490c445cba2eba">세 수(Triple)의 중앙값 찾기</h2>
<p>퀵정렬을 설계할 때 Pivot을 선택하는 방법에는 여러가지가 있지만, 대표적으로는 임의의 한 원소를 선택하는 방법과 <strong>처음, 중간, 끝의 원소 중 중앙값인 원소를 선택하는 방법</strong>의 두 가지가 있다.</p>
<p>퀵정렬에서 써먹을 수 있도록 세 수 중에서 중앙값을 찾는 방법을 생각해보자.<br />
기본적인 방법은, 다량의 조건문을 이용해 찾는 것이다.</p>
<pre><code class="language-c++">int median(int a, int b, int c){
if (a &gt; b){
if (b &gt; c) return b;
else if (a &gt; c) return c;
else return a;
}
else{
if (a &gt; c) return a;
else if (b &gt; c) return c;
else return b;
}
}
</code></pre>
<p>조건부 연산자(<code>exp?exp:exp</code>)를 이용하면 코드가 좀 더 짧아지겠지만, 해석하기 어려워 진다.</p>
<p>교환 법칙이 성립되는 XOR의 성질을 이용한다면, 분기 없이 중앙값을 찾을 수도 있다.</p>
<pre><code class="language-c++">int median(int a, int b, int c){
int maximum = max(max(a, b), c);
int minimum = min(min(a, b), c);
return a ^ b ^ c ^ maximium ^ minimum;
}
</code></pre>
<p>세 수 모두를 XOR로 결합하고, 다시 최대값 최소값을 XOR로 제외함으로써 중앙값을 구했다.<br />
XOR에 관한 내용은 <a href="https://o-tantk.github.io/posts/reverse-array/">이 포스트</a>를 참조하길 바란다.</p>
</description>
</item>
<item>
<title>랜덤 뽑기</title>
<link>https://o-tantk.github.io/posts/randomized-array/</link>
<pubDate>Thu, 20 Oct 2016 18:26:57 +0900</pubDate>
<author>[email protected] (tantk)</author>
<guid>https://o-tantk.github.io/posts/randomized-array/</guid>
<description>
<h1 id="임의의-접근:d680e8a854a7cbad6d490c445cba2eba">임의의 접근</h1>
<p>&lsquo;임의(랜덤)&lsquo;라는 말을 들으면 <code>rand()</code>를 이용해 무작위로 접근하는 방법이 가장 먼저 떠오를 것이다. 그러나 당연하게도 무작위 접근은 중복 접근을 방지하지 못 한다.<br />
그렇다면, 중복을 피해서 배열에 임의로 접근하려면 어떻게 해야 할까.</p>
<h2 id="방문-여부를-기록하는-방법:d680e8a854a7cbad6d490c445cba2eba">방문 여부를 기록하는 방법</h2>
<p>가장 손쉽게 생각해낼 수 있는 방법은, 임의 접근하는 배열의 각 원소마다 방문 여부를 기록하고, 중복된 접근이면 새로운 임의 접근을 시도하도록 구현하는 것이다.</p>
<p>코드로 보면 다음과 같다.</p>
<pre><code class="language-c++">// arr는 std::vector를 이용한 임의의 배열.
std::vector&lt;bool&gt; visited(arr.size(), false);
int indexToVisit = rand() % arr.size(); // [0, size)
while(visited[indexToVisit]) {
indexToVisit = rand() % arr.size();
}
visited[indexToVisit] = true;
arr[indexToVisit] ...
</code></pre>
<p>그러나, 이 방식에는 문제가 있는데, 위 코드에서 볼 수 있듯이 방문 기록이 없는 인덱스가 나올때까지 새로운 접근을 반복 시도한다는 점이다. 이는 방문한 원소의 수가 늘어갈수록, 배열의 크기가 클수록 더 큰 문제가 된다. 배열의 대부분을 방문하고 나면, 무한에 가까운 루프를 시도할지도 모른다.</p>
<h2 id="접근-영역을-한정하는-방법:d680e8a854a7cbad6d490c445cba2eba">접근 영역을 한정하는 방법</h2>
<p>이 문제를, 여러개의 공이 들어있는 주머니에서 임의의 한 개를 뽑는 문제라고 생각해 보자. 공을 뽑을 때 전에 뽑았던 공을 다시 뽑지 않으려면, 뽑은 공을 주머니에 도로 넣지 않고 다른 곳에 보관해두면 된다.<br />
그러나, 배열에서 이를 적용하기란 쉽지 않은 일이다. 원소를 마음대로 떼었다 붙였다 할 수 있는 것이 아니니까. 그렇다면 어떻게 해야 좋을까? 데이터 스왑을 이용하면 생각보다 간단히 해결할 수 있다.</p>
<ol>
<li>먼저, 배열 전체를 임의 접근할 수 있는 영역으로 설정해 놓는다.<br /></li>
<li>방문할 임의의 원소를 영역 내에서 선택하고, 해당 원소를 영역의 끝으로 옮겨놓는다. 동시에 영역의 크기를 한 칸 줄인다.<br /></li>
<li>옮겨놓은 원소를 사용한다.<br /></li>
<li>새로운 임의 접근 시, 2-3을 수행한다.</li>
</ol>
<p>여기서 <strong>원소를 옮긴다</strong>의 의미가, 배열 구조 전체를 수정해야 하는 <strong>제거 후 삽입</strong>이 아닌, <strong>데이터 스왑</strong>을 이용한 방식이란 것이 중요하다.</p>
<p>아래의 그림을 보면 쉽게 이해할 수 있을 것이다.
<img src="flowchart.png" alt="배열을 섞는 과정" /></p>
<p>바로 코드로 적용해 보자.</p>
<pre><code class="language-c++">// arr는 std::vector를 이용한 임의의 배열.
int range = arr.size();
int randomIndex = rand() % range; // [0, range)
// 데이터 스왑.
int temp = arr[randomIndex];
arr[randomIndex] = arr[range - 1];
arr[range - 1] = temp;
--range; // 영역을 줄인다.
arr[range] ...
</code></pre>
<p><code>randomIndex</code>가 가리키는 영역 내의 임의의 원소와 <code>range</code>가 가리키는 영역 끝의 원소를 스왑함으로써, <code>range</code>가 영역 내의 임의의 원소를 가리키도록 만들었다.</p>
<h3 id="인덱스-배열을-이용:d680e8a854a7cbad6d490c445cba2eba">인덱스 배열을 이용</h3>
<p>그러나, 이 방법도 결국 데이터 스왑을 통해 배열에 변형을 가하고 있다.<br />
위 예제와 같이 원본 배열이 변형되어도 되고, 배열에 저장된 데이터가 레지스터를 이용하는 기본 자료형(<code>int</code>, <code>char</code>, &hellip;)이면 전혀 문제될 것이 없지만, 객체와 같이 데이터 스왑에 필요한 비용이 큰 자료형의 경우엔 성능에 크게 영향을 끼칠 것이다. 그러므로 직접 원본 배열을 이용하는 것 보다는, 따로 임의 접근을 위한 인덱스 배열을 두고 이를 섞어 쓰는 편이 낫다.</p>
<p>물론, 인덱스 배열이 차지하는 메모리도 무시할 수 없지만, 일반적으로 기본 자료형으로 인한 공간 복잡도보다는 객체 복사로 인한 시간 복잡도가 더 큰 문제가 될 것이다.</p>
<p>다음은 클래스화 한 인덱스 배열의 코드이다.</p>
<pre><code class="language-c++">class ShuffledIndices{
private:
std::vector&lt;int&gt; indices;
int range;
public:
ShuffledIndices(int size): indices(size), range(size) {
for(int i=0; i&lt;size; ++i){
indices[i] = i;
}
}
int getIndex() {
int randomIndex = rand() % range; // [0, range)
// 데이터 스왑.
int temp = indices[randomIndex];
indices[randomIndex] = indices[range - 1];
indices[range - 1] = temp;
// 영역을 줄인다. 더 이상 접근할 영역이 없으면 초기화 한다.
if(--range &lt; 1){
range = indices.size();
}
return temp;
}
};
</code></pre>
<p>생성자에서 배열과 동일한 크기의 인덱스 배열을 만들고, <code>getIndex()</code>를 호출할 때마다 임의 접근할 인덱스를 반환한다. 추가된 점은, 더 이상 임의 접근할 영역이 없다면 <code>range</code>를 초기화 하고, 다시 인덱스 배열 전체를 이용하도록 만든 것이다.</p>
<p>아래와 같이 쓰면 된다.</p>
<pre><code class="language-c++">// arr는 std::vector를 이용한 임의의 배열.
ShuffledIndices si(arr.size());
for(int i=0; i&lt;arr.size(); ++i){
arr[si.getIndex()] ...
}
</code></pre>
<h2 id="배열-섞기:d680e8a854a7cbad6d490c445cba2eba">배열 섞기</h2>
<p>목적이 배열을 섞는 것이라면 굳이 인덱스 배열을 쓸 필요가 없다.</p>
<pre><code class="language-c++">template &lt;typename type_t&gt;
void shuffle(std::vector&lt;type_t&gt; &amp;arr) {
int range = arr.size();
for(int i=0; i&lt;arr.size(); ++i) {
int randomIndex = rand() % range; // [0, range)
// 데이터 스왑.
type_t temp = arr[randomIndex];
arr[randomIndex] = arr[range - 1];
arr[range - 1] = temp;
--range; // 영역을 줄인다.
}
}
</code></pre>
<p><code>getIndex()</code>를 조금 변형하여 배열을 섞는 함수를 작성했다. 달라진 점은 원본 배열을 직접 이용한다는 점과 여러 자료형에 적용할 수 있도록 <code>template</code>을 사용한 것 뿐이다.</p>
</description>
</item>
<item>
<title>배열 뒤집기</title>
<link>https://o-tantk.github.io/posts/reverse-array/</link>
<pubDate>Tue, 20 Sep 2016 01:23:50 +0900</pubDate>
<author>[email protected] (tantk)</author>
<guid>https://o-tantk.github.io/posts/reverse-array/</guid>
<description>
<h1 id="문제:d680e8a854a7cbad6d490c445cba2eba">문제</h1>
<p>배열 안의 데이터 순서를 역순으로 바꾼다고 하자.<br />
<code>['a', 'b,' 'c', 'd']</code>와 같은 배열을 <code>['d', 'c', 'b', 'a']</code>와 같이 뒤집고 싶다는 것이다.<br />
C#, Python과 같이 사용자 친화적 언어들은 <code>.reverse()</code>하면 아주 손쉽게 배열을 뒤집을 수 있지만, C와 같은 언어에서는 한 번쯤 생각해 볼 수 있는 문제이다.</p>
<h2 id="새로운-배열에-역순으로-복사:d680e8a854a7cbad6d490c445cba2eba">새로운 배열에 역순으로 복사</h2>
<p>아마도 가장 단순 무식하게 떠올릴 수 있는 방법은, 똑같은 크기의 배열을 새로 만들어서 역순으로 데이터를 옮기는 것이다.</p>
<pre><code class="language-c">char new_array[SIZE];
for(i = 0; i &lt; SIZE; ++i) {
new_array[SIZE - 1 - i] = old_array[i];
}
</code></pre>
<p>간단히 옮기는 것 까지는 좋았는데, 배열 크기 만큼의 새로운 메모리가 필요하다니 낭비가 너무 심하다. 게다가 원본 저장소 <code>old_array</code>를 그대로 사용하고 싶다면 <code>memcpy</code>를 추가로 수행해줘야 한다.</p>
<p>뭐, 위 방법을 쓸리는 없고, 그냥 한 번 생각해보자는 것이니까 다음을 보자.</p>
<h2 id="데이터-스왑을-이용:d680e8a854a7cbad6d490c445cba2eba">데이터 스왑을 이용</h2>
<p>일반적으로 사용되는 가장 흔한 형태는, 아래와 같이 데이터 스왑을 이용해서 배열을 뒤집는 방법일 것이다.</p>
<pre><code class="language-c">char temp;
for(i = 0; i &lt; SIZE / 2; ++i) {
temp = array[i];
array[i] = array[SIZE - 1 - i];
array[SIZE - 1 - i] = temp;
}
</code></pre>
<p>배열의 양 끝에서부터 중간 지점까지 각각 차례로 한 칸씩 이동하며 데이터를 교환함으로써 배열의 순서를 뒤집고 있다. 데이터 스왑에는 임시 메모리를 이용한 전형적인 방법이 사용되었다.<br />
이 정도면 아주 작은 추가 메모리로 원본 저장소도 유지할 수 있고, 연산횟수도 반으로 줄었으니 더할 나위 없어 보인다.</p>
<p>그러나 모종의 변태들은, 새하얀 도화지의 까만 점 처럼 <code>temp</code>가 자꾸 눈에 밟히고 치명적인 오점인 것처럼 생각할지도 모른다.</p>
<h1 id="배타적-논리합-xor-을-이용한-데이터-스왑:d680e8a854a7cbad6d490c445cba2eba">배타적 논리합(XOR)을 이용한 데이터 스왑</h1>
<p>비트 연산 XOR을 이용하면 여분의 메모리를 사용하지 않고도 데이터를 교환할 수 있다.</p>
<pre><code class="language-c">char a = 'A', b = 'B';
a ^= b;
b ^= a;
a ^= b;
// DONE. a == 'B', b == 'A'.
</code></pre>
<p>정말 간단하다.</p>
<p>어떻게 이게 가능한걸까? 이는 배타적 논리합이 갖는, 두 피연산자의 서로 다른 성질만을 남긴다는 점을 교묘하게 이용하고 있기 때문이다.<br />
a와 b가 집합이라고 생각하고 위의 코드에 대입해보자.
<center></p>
<table>
<thead>
<tr>
<th align="left">과정</th>
<th align="left">a</th>
<th align="left">b</th>
</tr>
</thead>
<tbody>
<tr>
<td align="left">초기화</td>
<td align="left">(ab 공통의 성질, a의 유일한 성질)</td>
<td align="left">(ab 공통의 성질, b의 유일한 성질)</td>
</tr>
<tr>
<td align="left">a = a ^ b</td>
<td align="left">(ab 공통, a의 유일) ^ (ab 공통, b의 유일)<br \>= (a의 유일, b의 유일)</td>
<td align="left">(ab 공통의 성질, b의 유일한 성질)</td>
</tr>
<tr>
<td align="left">b = b ^ a</td>
<td align="left">(a의 유일, b의 유일)</td>
<td align="left">(ab 공통, b의 유일) ^ (a의 유일, b의 유일)<br \>= (ab 공통, a의 유일)</td>
</tr>
<tr>
<td align="left">a = a ^ b</td>
<td align="left">(a의 유일, b의 유일) ^ (ab 공통, a의 유일)<br \>= (ab 공통, b의 유일)</td>
<td align="left">(ab 공통, a의 유일)</td>
</tr>
</tbody>
</table>
<p></center></p>
<p>벤 다이어그램으로 그리면 다음과 같다.
<img src="venn_diagram.png" alt="데이터 교환 과정" /></p>
<p>XOR은 교환 법칙이 성립한다는 것을 이용해 수학적으로 증명할 수도 있다.
<center></p>
<table>
<thead>
<tr>
<th align="left">과정</th>
<th align="left">a</th>
<th align="left">b</th>
</tr>
</thead>
<tbody>
<tr>
<td align="left">초기화</td>
<td align="left">A</td>
<td align="left">B</td>
</tr>
<tr>
<td align="left">a = a ^ b</td>
<td align="left">A ^ B</td>
<td align="left">B</td>
</tr>
<tr>
<td align="left">b = b ^ a</td>
<td align="left">A ^ B</td>
<td align="left">B ^ A ^ B<br \>= A ^ B ^ B (교환 법칙)<br \>= A</td>
</tr>
<tr>
<td align="left">a = a ^ b</td>
<td align="left">A ^ B ^ A<br \>= A ^ A ^ B (교환 법칙)<br \>= B</td>
<td align="left">A</td>
</tr>
</tbody>
</table>
<p></center></p>
<h2 id="다른-자료형에의-적용:d680e8a854a7cbad6d490c445cba2eba">다른 자료형에의 적용</h2>
<p>메모리 내용 자체를 교환하는 방식이니까 어떤 데이터든 교환이 가능할까?<br />
비트 연산은 정수형 자료형만 가능하므로 일반적으로는 다른 자료형에 적용할 수 없지만, 약간의 트릭으로 실수형 자료형과 같은 다른 자료형에도 적용할 수 있다.</p>
<p>바로 포인터를 이용하는 것이다.</p>
<pre><code class="language-c++">float a = 1.0f, b = -1.0f;
*(reinterpret_cast&lt;int *&gt;(&amp;a)) ^= *(reinterpret_cast&lt;int *&gt;(&amp;b));
*(reinterpret_cast&lt;int *&gt;(&amp;b)) ^= *(reinterpret_cast&lt;int *&gt;(&amp;a));
*(reinterpret_cast&lt;int *&gt;(&amp;a)) ^= *(reinterpret_cast&lt;int *&gt;(&amp;b));
</code></pre>
<p>캐스트 연산자 때문에 뭔가 복잡해 보이지만, float 자료형이 int 자료형인 것처럼 컴파일러를 속였을 뿐이다.<br />
비록 정수형 자료형의 크기와 동일한 크기를 가진 자료형만 해당되지만, 어엿하게 실수형 자료형에도 적용할 수 있다는 말이다.</p>
<h2 id="xor을-이용하면-더-좋을까:d680e8a854a7cbad6d490c445cba2eba">XOR을 이용하면 더 좋을까?</h2>
<p>그렇다면, XOR을 이용한 방식이 정말 더 좋은지 확인해보자.</p>
<p>먼저 XOR을 이용한 방법의 어셈블리 코드를 보자.</p>
<pre><code class="language-c++">; 7 : *(reinterpret_cast&lt;int *&gt;(&amp;a)) ^= *(reinterpret_cast&lt;int *&gt;(&amp;b));
00042 8b 45 f4 mov eax, DWORD PTR _a$[ebp]
00045 33 45 e8 xor eax, DWORD PTR _b$[ebp]
00048 89 45 f4 mov DWORD PTR _a$[ebp], eax
; 8 : *(reinterpret_cast&lt;int *&gt;(&amp;b)) ^= *(reinterpret_cast&lt;int *&gt;(&amp;a));
0004b 8b 45 e8 mov eax, DWORD PTR _b$[ebp]
0004e 33 45 f4 xor eax, DWORD PTR _a$[ebp]
00051 89 45 e8 mov DWORD PTR _b$[ebp], eax
; 9 : *(reinterpret_cast&lt;int *&gt;(&amp;a)) ^= *(reinterpret_cast&lt;int *&gt;(&amp;b));
00054 8b 45 f4 mov eax, DWORD PTR _a$[ebp]
00057 33 45 e8 xor eax, DWORD PTR _b$[ebp]
0005a 89 45 f4 mov DWORD PTR _a$[ebp], eax
</code></pre>
<p>다음은 임시 메모리를 이용한 방법의 어셈블리 코드이다.</p>
<pre><code class="language-c++">; 12 : temp = a;
0005d f3 0f 10 45 f4 movss xmm0, DWORD PTR _a$[ebp]
00062 f3 0f 11 45 dc movss DWORD PTR _temp$[ebp], xmm0
; 13 : a = b;
00067 f3 0f 10 45 e8 movss xmm0, DWORD PTR _b$[ebp]
0006c f3 0f 11 45 f4 movss DWORD PTR _a$[ebp], xmm0
; 14 : b = temp;
00071 f3 0f 10 45 dc movss xmm0, DWORD PTR _temp$[ebp]
00076 f3 0f 11 45 e8 movss DWORD PTR _b$[ebp], xmm0
</code></pre>
<p>전자는 후자에 비해 XOR 연산을 위한 CPU 사이클이 하나씩 더 들어가 있다.<br />
어셈블리 코드를 볼 줄 모른다고 해도, 척 보면 XOR을 이용한 방식의 코드 길이가 3줄 더 많다는 것을 알 수 있다. 이는 CPU의 연산이 세 번 더 수행된다는 뜻이다.</p>
<p>실제로 얼마나 성능 차가 나는지 실험 결과를 보자.<br />
다음은 특정 배열에 대해 무작위(<a href="https://ko.wikipedia.org/wiki/분기_예측">분기 예측</a> 방지) 원소 교환을 9999999회 수행하는데 걸린 시간이다. (3.3Ghz CPU)
<center></p>
<table>
<thead>
<tr>
<th align="left">방법</th>
<th align="center">소요 시간</th>
</tr>
</thead>
<tbody>
<tr>
<td align="left">XOR</td>
<td align="center">42 ms</td>
</tr>
<tr>
<td align="left">임시 메모리</td>
<td align="center">15 ms</td>
</tr>
</tbody>
</table>
<p></center>
확실히 임시 메모리를 이용한 방법이 더 빠르긴 하지만, 이 정도의 미미한 차이는 무시할 수 있는 수준이다.</p>
<h1 id="결론:d680e8a854a7cbad6d490c445cba2eba">결론</h1>
<p>어째 의식이 흐르듯 배열 뒤집기에서 데이터 스왑으로 주제가 바뀐 것 같지만&hellip;</p>
<p>아무튼 결론적으로 얘기하자면, 배열을 뒤집을 땐 데이터 스왑을 이용한 방식으로 구현하는 것이 좋고, 데이터 스왑 방식으로는 임시 메모리를 이용하든 XOR을 이용하든 개발자 마음대로 라는 것이다.<br />
XOR을 이용한 방식은, 알아두면 &lsquo;이렇게 변태같이 데이터 스왑하는 방법도 있다!&rsquo; 하고 자랑할 수도 있다.</p>
<p>물론, 영상처리와 같이 10ms가 아쉬운 경우라면 임시 메모리를 이용한 방법이 더 적절하겠지만 말이다.</p>
<h1 id="references:d680e8a854a7cbad6d490c445cba2eba">References</h1>
<ul>
<li><a href="https://ko.wikipedia.org/wiki/XOR_교체_알고리즘">https://ko.wikipedia.org/wiki/XOR_교체_알고리즘</a></li>
</ul>
</description>
</item>
<item>
<title>콘솔 프로그램 실행기</title>
<link>https://o-tantk.github.io/posts/console-app-launcher/</link>
<pubDate>Fri, 17 Jun 2016 15:40:57 +0900</pubDate>
<author>[email protected] (tantk)</author>
<guid>https://o-tantk.github.io/posts/console-app-launcher/</guid>
<description>
<h1 id="사설:d680e8a854a7cbad6d490c445cba2eba">사설</h1>
<p>영상처리 과제를 할 때였다. 알고리즘의 파라미터를 다양하게 변화시켜 그에 따른 여러 결과를 보여야 했다.<br />
그런데, 윈도우에서 콘솔 프로그램의 파라미터를 계속 바꾸는 것이 여간 귀찮은게 아니었다. 특히 실행이 끝나면 무의식적으로 cmd 창을 끄는 버릇 때문에, 다시 cmd 창을 켜고 실행 파일 위치까지 이동하는 과정이 너무 귀찮았다.</p>
<p>Batch 파일은 작성하는 방법을 제대로 알지 못했고, 입력 파라미터가 정해진게 아니어서 미리 작업를 예약하는 방식은 쓸 수 없었다.
그렇다고 콘솔 프로그램을 GUI로 새로 작성하기에는 시간이 아깝다.</p>
<p>결국 C# winform을 이용해, 콘솔 프로그램의 위치를 기억하고 파라미터를 입력할 수 있는 간단한 프로그램을 작성하기로 했다.</p>
<h1 id="프로그램:d680e8a854a7cbad6d490c445cba2eba">프로그램</h1>
<p><img src="app.png" alt="프로그램 스크린샷" />
<center><a href="https://github.com/o-tantk/ConsoleAppLauncher">https://github.com/o-tantk/ConsoleAppLauncher</a></center></p>
<ul>
<li>콘솔 프로그램의 위치와 입력했던 파라미터를 기억한다.</li>
<li>콘솔 프로그램의 실행 및 파라미터 입력을 GUI 환경에서 수행한다.</li>
</ul>
<h2 id="파라미터-입력-및-실행:d680e8a854a7cbad6d490c445cba2eba">파라미터 입력 및 실행</h2>
<p>내가 원했던 것은, 콘솔 프로그램에 파라미터를 입력하고 실행하면 그 실행 내용과 결과가 cmd 창에 출력되는 모습이었는데, <code>System.Diagnotics.Process</code>로 콘솔 프로그램을 직접 실행시켰더니 프로그램 종료와 함께 cmd 창이 닫혔다.<br />
그래서, cmd부터 실행시키고 cmd에서 콘솔 프로그램을 실행시키는 방향으로 설계해야 했으며, 이는 콘솔 프로그램 및 파라미터가 담긴 명령줄을 cmd의 파라미터로 실행하여 해결했다.</p>
<p><a href="https://www.microsoft.com/resources/documentation/windows/xp/all/proddocs/en-us/cmd.mspx?mfr=true">cmd의 옵션</a>을 확인해 보니, cmd에 명령줄을 전달하려면 <code>/c</code> 또는 <code>/k</code> 옵션을 이용하면 된다.</p>
<blockquote>
<p>/c : Carries out the command specified by string and then stops.<br />
/k : Carries out the command specified by string and continues.</p>
</blockquote>
<p>둘 중 무슨 옵션을 이용해야 할까? 바로 창이 닫히는게 문제니까 <code>/k</code> 옵션을 쓰는게 좋을까? 그러나 <code>/k</code> 옵션을 이용하면 프로그램 종료 후 다음 명령을 기다리는 프롬프트 화면으로 넘어가기 때문에 창을 수작업으로 닫아줘야 한다. 결과 확인만 하면 되는데 귀찮은 작업이 늘어난다.</p>
<p>결국 <code>/c</code> 옵션을 이용해야 하는데, &lsquo;계속하려면 아무키나 누르세요&rsquo;를 띄우는게 좋겠다.<br />
처음 코딩을 배울 때 꼼수로 애용했던 <code>pause</code>를 이용하자. cmd를 띄우는 명령과 함께 사용해야 하니 <code>&amp;</code>를 이용하면 된다. (여러 명령을 함께 실행하는 방법은 <a href="https://www.microsoft.com/resources/documentation/windows/xp/all/proddocs/en-us/ntcmds_shelloverview.mspx?mfr=true">여기</a>를 참조)</p>
<p>명령줄은 다음과 같을 것이다.</p>
<pre><code class="language-nohighlight"># cmd.exe /c myapp.exe -myparam &amp; pause
</code></pre>
<p>이를 C# Winform에서 실행해 보자.</p>
<pre><code class="language-C#">// using System.Diagnotics;
// appPath: 프로그램 경로. ex) &quot;c:\myapp.exe&quot;
// tb_parameters: 파라미터가 입력된 Textbox.
Process app = new Process();
app.StartInfo.FileName = &quot;cmd.exe&quot;;
app.StartInfo.Arguments = &quot;/C &quot; + appPath + &quot; &quot; + tb_parameters.Text + &quot;&amp; pause&quot;;
app.Start();
</code></pre>
<p>이때, <code>Process</code>의 작업 디렉토리를 설정해주지 않으면 콘솔 프로그램 <strong>실행기의 위치</strong>가 작업 디렉토리가 되므로, <code>System.IO.Path.GetDirectoryName</code>으로 콘솔 프로그램의 위치를 가져와 작업 디렉토리로 설정해주자.</p>
<p>한 줄만 추가해 주면 된다.</p>
<pre><code class="language-C#">// using System.IO;
app.StartInfo.WorkingDirectory = Path.GetDirectoryName(appPath);
</code></pre>
<h2 id="레지스트리-저장:d680e8a854a7cbad6d490c445cba2eba">레지스트리 저장</h2>
<p>실행했던 콘솔 프로그램의 위치와 파라미터의 기억은 레지스트리를 이용한다.</p>
<p>레지스트리의 저장 위치는 <code>LOCAL_MACHINE</code>과 <code>CURRENT_USER</code>의 두 가지 선택지가 존재하는데, <code>LOCAL_MACHINE</code>은 컴퓨터 전역 / 모든 사용자에게 적용되는 필드로 저장할 때 관리자 권한을 요구한다. <code>CURRENT_USER</code>는 사용자 계정에 종속되는 필드로 프로그램의 환경 설정을 저장할 때 일반적으로 사용된다.</p>
<p>콘솔 프로그램 실행기는 특별한 정보를 저장하는 것이 아니니 <code>CURRENT_USER</code> 필드를 이용한다.<br />
그리고, 일반 소프트웨어는 보통 <code>Software</code> 필드를 이용하므로 서브 키를 <code>Software\\AppLauncher</code>로 만들자.<br />
C# 코드 상에선 다음과 같다.</p>
<pre><code class="language-C#">// using Microsoft.Win32;
RegistryKey subkey = Registry.CurrentUser.OpenSubKey(&quot;Software\\AppLauncher&quot;, true);
// 먼저 OpenSubKey로 이미 서브 키가 있는지 확인한다.
// 쓰기를 수행하려면 두 번째 인자로 true를 넘겨줘야 한다.
if (subkey == null)
{
subkey = Registry.CurrentUser.CreateSubKey(&quot;Software\\AppLauncher&quot;);
}
</code></pre>
<p>레지스트리에 저장할 것은 프로그램의 경로와 파라미터이다. 두 값 모두 <code>string</code>이므로 <code>RegistryValueKind.String</code>으로 지정하여 저장한다.</p>
<pre><code class="language-C#">subkey.SetValue(&quot;AppPath&quot;, appPath, RegistryValueKind.String);
subkey.SetValue(&quot;Parameters&quot;, tb_parameters.Text, RegistryValueKind.String);
</code></pre>
<p>레지스트리를 가져올 때는, <code>object</code> 객체를 반환하는 <code>RegistryKey.GetValue</code>를 이용한다.</p>
<pre><code class="language-C#">// using Microsoft.Win32;
RegistryKey subkey = Registry.CurrentUser.OpenSubKey(&quot;Software\\AppLauncher&quot;);
if (subkey != null)
{
appPath = subkey.GetValue(&quot;AppPath&quot;).ToString();
tb_parameters.Text = subkey.GetValue(&quot;Parameters&quot;).ToString();
}
</code></pre>
<h2 id="끝:d680e8a854a7cbad6d490c445cba2eba">끝</h2>
<p>핵심 기능을 구현하는 법은 알았으니 프로그램 구조를 적절히 설계만 하면 된다.</p>
<p>콘솔 프로그램을 실행하는 부분은 실행 버튼의 <code>Click</code> 이벤트에 구현하면 될 것이고&hellip;<br />
레지스트리를 가져올 때는 <code>Load</code> 이벤트를, 기록할 때는 <code>Form_Closed</code>를 이용하자.</p>
<h1 id="references:d680e8a854a7cbad6d490c445cba2eba">References</h1>
<ul>
<li><a href="http://www.differencebetween.net/technology/hardware-technology/difference-between-hkey_current_user-and-hkey_local_machine/">http://www.differencebetween.net/technology/hardware-technology/difference-between-hkey_current_user-and-hkey_local_machine/</a></li>
</ul>
</description>
</item>
<item>
<title>Search</title>
<link>https://o-tantk.github.io/search/</link>
<pubDate>Wed, 15 Jun 2016 04:50:09 +0900</pubDate>
<author>[email protected] (tantk)</author>
<guid>https://o-tantk.github.io/search/</guid>
<description></description>
</item>
<item>
<title>About</title>
<link>https://o-tantk.github.io/about/</link>
<pubDate>Mon, 02 May 2016 02:06:32 +0900</pubDate>
<author>[email protected] (tantk)</author>
<guid>https://o-tantk.github.io/about/</guid>
<description>
<h1 id="tantk-땅꾸:6083a88ee3411b0d17ce02d738f69d47">tantk == 땅꾸</h1>
<p>Game developer. Otaku.</p>
<ul>
<li>Programmer, devCat Studio, Nexon. 2017 ~</li>
</ul>
<h2 id="feel-free-to-contact:6083a88ee3411b0d17ce02d738f69d47">Feel free to contact</h2>
<ul>
<li><i class="fa fa-envelope"></i> <a href="mailto:[email protected]">tantk90 at hotmail.com</a><br /></li>
<li><i class="fa fa-github"></i> <a href="https://github.com/o-tantk">o-tantk at Github</a></li>
</ul>
<h2 id="skills:6083a88ee3411b0d17ce02d738f69d47">Skills</h2>
<ul>
<li>Languages: C++(Primary), C, C#, GLSL, Lua, CUDA, &hellip;<br /></li>
<li>Frameworks: STL, OpenGL, Unity3D, &hellip;</li>
</ul>
</description>
</item>
<item>
<title>땅꾸의 블로그</title>
<link>https://o-tantk.github.io/posts/hello-world/</link>
<pubDate>Mon, 02 May 2016 02:06:23 +0900</pubDate>
<author>[email protected] (tantk)</author>
<guid>https://o-tantk.github.io/posts/hello-world/</guid>
<description>
<h1 id="안녕하세요-땅꾸입니다:d680e8a854a7cbad6d490c445cba2eba">안녕하세요. 땅꾸입니다.</h1>
<p>이 블로그는 <a href="https://gohugo.io/">Hugo</a>라는 Static Website Engine을 이용해서 만들었고, <a href="https://github.com/">Github</a>를 이용해 호스팅 하고 있습니다.</p>
<h1 id="이-아래는-markdown-문서의-테스트이므로-무시하셔도-좋습니다:d680e8a854a7cbad6d490c445cba2eba">이 아래는 markdown 문서의 테스트이므로 무시하셔도 좋습니다.</h1>
<p>Windows OS의 경로 최대길이는 <code>MAX_PATH</code>라는 상수값 260으로 제한되어 있다.</p>
<p>{{//&lt; tweet 742203311508443136 &gt;}}</p>
<p>다음은 c++ 코드 샘플</p>
<pre><code class="language-c++">#include &lt;iostream&gt;
int main(int argc, char **argv){
std::cout &lt;&lt; &quot;hello world!&quot; &lt;&lt; std::endl;
return 0;
}
// 코드는 어디까지 길어질까아ㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏ 는 highlight.js는 자동 줄넘김이다.
</code></pre>
<p>다음은 glsl 코드 샘플</p>
<pre><code class="language-glsl">layout(location=0) in vec3 position;
void main(void){
gl_Position = position;
}
</code></pre>
<div style="position: relative; padding-bottom: 56.25%; padding-top: 30px; height: 0; overflow: hidden;">
<iframe src="//www.youtube.com/embed/KXSUEU7ISfQ" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%;" allowfullscreen frameborder="0"></iframe>
</div>
<p>
<figure >
<img src="%ed%98%b8%eb%ac%b4%ed%98%b8%eb%ac%b4.jpg" alt="호무호무" />
<figcaption>
호무호무한 호무라짱
</figcaption>
</figure>
위의 호무호무한 호무라짱을 보라! 너무나도 호무호무하다. 호무호무&hellip;</p>
<blockquote>
<p>호무호무는 너무나도 호무호무하기에 인용까지 해야 할 정도이다.<br />
두 번째 인용 줄</p>
<p>세 번째 인용 줄</p>
</blockquote>
<p>인용문 끝.</p>
<h3 id="shibe-doge:d680e8a854a7cbad6d490c445cba2eba">Shibe doge</h3>
<p>본 김에 시베도 보자
<img src="시베.jpg" alt="shibe doge" />
such wow</p>
</description>
</item>
</channel>
</rss>