<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>fluid_silver.log</title>
        <link>https://velog.io/</link>
        <description></description>
        <lastBuildDate>Thu, 09 Feb 2023 08:58:37 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <copyright>Copyright (C) 2019. fluid_silver.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/fluid_silver" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[DFS 알고리즘 (+백준 1260번)]]></title>
            <link>https://velog.io/@fluid_silver/DFS-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98</link>
            <guid>https://velog.io/@fluid_silver/DFS-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98</guid>
            <pubDate>Thu, 09 Feb 2023 08:58:37 GMT</pubDate>
            <description><![CDATA[<h2 id="개념">개념</h2>
<p><strong>깊이 우선 탐색</strong> (depth First Search), DFS 알고리즘은 맹목적 탐색 기법 중 하나이다.</p>
<p>맹목적 탐색 기법이란,
정해진 순서에 따라 상태 공간 그래프를 점진적으로 생성해가면서 해를 탐색하는 방법이다.</p>
<p>맹목적 탐색 기법에는 깊이 우선 탐색, 너비 우선 탐색, 반복적 깊이심화 탐색 등이 있으며
이 글에서는 깊이 우선 탐색을 설명할 것이다.
(너비 우선 탐색은 여기 -&gt; <a href="https://velog.io/@fluid_silver/BFS-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98">https://velog.io/@fluid_silver/BFS-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98</a>)</p>
<p>먼저 그림을 보자. (출처 : 학교 수업 pdf)</p>
<p><img src="https://velog.velcdn.com/images/fluid_silver/post/05c562ad-8c48-4453-ac92-c83f9d0239d9/image.png" alt=""></p>
<p><strong>한 방향으로 끝까지</strong> 가다가 더 이상 갈 수 없게 되면 
가장 가까운 갈림길로 돌아와서 다른 방향으로 다시 탐색을 진행하는 방법이다. (백트래킹)</p>
<p>목표 정점이 있다면, 모든 정점을 방문하지 않고 <strong>목표 정점에 도달</strong>했을 때 종료한다. 
<strong>스택</strong>(<code>stack</code>)을 사용하여 구현한다. </p>
<h2 id="알고리즘">알고리즘</h2>
<ol>
<li>시작 정점을 스택에 저장</li>
<li>스택의 맨 뒤 정점의 이웃을 <code>neighbor</code>에 저장</li>
<li><code>neighbor</code> 중에 방문하지 않은 정점 하나를 택하여 스택에 저장,
모두 방문한 정점인 경우 선택된 정점을 스택에서 제거</li>
<li>2~3 과정을 반복</li>
<li>스택에 포함된 정점이 없을 때 알고리즘을 마침</li>
</ol>
<h2 id="코드">코드</h2>
<p>백준 1260번 DFS와 BFS : <a href="https://www.acmicpc.net/problem/1260">https://www.acmicpc.net/problem/1260</a></p>
<blockquote>
<p>C++</p>
</blockquote>
<pre><code class="language-c">#include&lt;iostream&gt;
#include&lt;vector&gt;
#include&lt;map&gt;
#include&lt;stack&gt;
#include&lt;algorithm&gt;
using namespace std;
typedef struct Node Node;
struct Node {                    // Node
    int data = 0;
    Node* next = NULL;
};
int main() {
    int N, M, V;
    cin &gt;&gt; N &gt;&gt; M &gt;&gt; V;
    vector&lt;Node&gt; head(N + 1);
    for (int i = 0; i &lt; M; i++) {
        int a, b;
        cin &gt;&gt; a &gt;&gt; b;
        Node* tmp = new Node();    // linked list
        tmp-&gt;data = b;
        tmp-&gt;next = head[a].next;
        head[a].next = tmp;
        Node* tmp2 = new Node();
        tmp2-&gt;data = a;
        tmp2-&gt;next = head[b].next;
        head[b].next = tmp2;
    }
    stack&lt;int&gt; s;
    map&lt;int, int&gt; visited;        // 방문한 정점 저장
    s.push(V);
    cout &lt;&lt; V &lt;&lt; &quot; &quot;;
    visited.insert({ V, 1 });
    while (size(s) != 0) {        // 스택에 정점이 없을 때까지
        int curr = s.top();        // 스택의 맨 뒤 정점
        Node head_tmp = head[curr];
        vector&lt;int&gt; neighbor;
        while (head_tmp.next != NULL) {    // 이웃 정점 저장
            head_tmp = *head_tmp.next;
            neighbor.push_back(head_tmp.data);
        }
        if (size(neighbor) == 0) {
            s.pop();
        }
        sort(neighbor.begin(), neighbor.end());    // 번호가 작은 것 방문
        for (int i = 0; i &lt; size(neighbor); i++) {
            if (visited.find(neighbor[i]) == visited.end()) {    // 방문하지 않은 정점
                s.push(neighbor[i]);        // 스택에 저장
                cout &lt;&lt; neighbor[i] &lt;&lt; &quot; &quot;;
                visited.insert({ neighbor[i], 1});
                break;
            }
            else if (i == size(neighbor) - 1) {        // 모두 방문한 정점인 경우
                s.pop();                // 정점 제거
            }
        }
    }
    return 0;
}</code></pre>
<blockquote>
<p>Python</p>
</blockquote>
<pre><code class="language-python"># 나중에 추가해보겠음</code></pre>
<h2 id="특징">특징</h2>
<p>DFS는 메모리 공간 사용이 <strong>효율적</strong>이지만
목표 정점을 찾으면 종료하기 때문에 <strong>최단 경로는 보장할 수 없다</strong>. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[XOR Linked List]]></title>
            <link>https://velog.io/@fluid_silver/XOR-Linked-List-XOR-Binary-Search-Tree</link>
            <guid>https://velog.io/@fluid_silver/XOR-Linked-List-XOR-Binary-Search-Tree</guid>
            <pubDate>Tue, 07 Feb 2023 07:24:30 GMT</pubDate>
            <description><![CDATA[<h2 id="xor-linked-list">XOR Linked List</h2>
<p><strong>XOR Linked List</strong>는 메모리를 효율적으로 이용하는 Doubly Linked List이다.</p>
<p>원래 Doubly Linked List는 이전 노드와 다음 노드의 주소를 저장하기 위해 하나의 노드에 <strong>두 개의 공간</strong>을 필요로 한다. </p>
<p>그런데 XOR Linked List는 <strong>하나의 공간</strong>에 이전 노드와 다음 노드의 주소 둘 다 저장한다. 
그러므로 <strong>메모리 사용이 줄어든다</strong>!</p>
<p><img src="https://velog.velcdn.com/images/fluid_silver/post/87741523-c4e3-4ebd-bc27-5f45e55b1ca6/image.png" alt=""></p>
<p>그러면 각 노드마다 저장된 <code>npx</code>로 이전 노드와 다음 노드의 주소를 어떻게 구할까?</p>
<p><strong>이전 노드의 주소</strong>를 구하기 위해서는 <code>npx</code>와 다음 노드의 주소,
<strong>다음 노드의 주소</strong>를 구하기 위해서는 <code>npx</code>와 이전 노드의 주소가 필요하다. 
(진행 방향 기준으로 <strong>이전 노드의 주소</strong>를 저장하면 됨)</p>
<p>예를 들어, 노드 B에서 C로 가는 주소를 구하고 싶다면 <code>npx(B)</code>와 <code>addr(A)</code>(이전 노드의 주소)를 사용한다. </p>
<blockquote>
<p><code>npx(B)</code> XOR <code>addr(A)</code>
= ( <code>addr(A)</code> XOR <code>addr(C)</code> ) XOR <code>addr(A)</code>
= <code>addr(A)</code> XOR <code>addr(C)</code> XOR <code>addr(A)</code>
= <code>addr(C)</code></p>
</blockquote>
<p>이렇게 C의 주소를 구할 수 있다. </p>
<p>.</p>
<p>.</p>
<p>.</p>
<p>(위 내용을 이용하여 XOR Binary Search Tree를 만들 수 있는가?)</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[BFS 알고리즘 (+백준 1260번)]]></title>
            <link>https://velog.io/@fluid_silver/BFS-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98</link>
            <guid>https://velog.io/@fluid_silver/BFS-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98</guid>
            <pubDate>Mon, 06 Feb 2023 13:05:16 GMT</pubDate>
            <description><![CDATA[<h2 id="개념">개념</h2>
<p><strong>너비 우선 탐색</strong> (Breadth First Search), BFS 알고리즘은 맹목적 탐색 기법 중 하나이다. </p>
<p>맹목적 탐색 기법이란, 
정해진 순서에 따라 상태 공간 그래프를 점진적으로 생성해가면서 해를 탐색하는 방법이다. </p>
<p>맹목적 탐색 기법에는 깊이 우선 탐색, 너비 우선 탐색, 반복적 깊이심화 탐색 등이 있으며
이 글에서는 너비 우선 탐색을 설명할 것이다.
(깊이 우선 탐색은 여기 -&gt; <a href="https://velog.io/@fluid_silver/DFS-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98">https://velog.io/@fluid_silver/DFS-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98</a>)</p>
<p>먼저 그림을 보자. (출처 : 학교 수업 pdf)
<img src="https://velog.velcdn.com/images/fluid_silver/post/fd51aa59-8f5d-48f9-b649-58051bf64522/image.png" alt=""></p>
<p>시작 정점으로부터 <strong>가까운 정점을 먼저 방문</strong>하고, 
멀리 떨어져 있는 정점을 나중에 방문하는 순회 방법이다. (결국 다 방문함)
<strong>큐</strong>(<code>queue</code>)를 사용하여 구현한다.</p>
<h2 id="알고리즘">알고리즘</h2>
<ol>
<li>시작 정점을 큐에 저장</li>
<li>큐의 맨 앞 정점의 이웃을 <code>neighbor</code>에 저장</li>
<li>선택된 정점을 큐에서 제거</li>
<li><code>neighbor</code>에 저장된 정점(+방문하지 않은 정점)들을 큐에 저장</li>
<li>2~4 과정을 반복</li>
<li>큐에 포함된 정점이 없을 때 알고리즘을 마침</li>
</ol>
<h2 id="코드">코드</h2>
<p>백준 1260번 DFS와 BFS : <a href="https://www.acmicpc.net/problem/1260">https://www.acmicpc.net/problem/1260</a></p>
<blockquote>
<p>C++</p>
</blockquote>
<pre><code class="language-c">#include&lt;iostream&gt;
#include&lt;vector&gt;
#include&lt;queue&gt;
#include&lt;map&gt;
#include&lt;algorithm&gt;
using namespace std;
typedef struct Node Node;
struct Node {                    // Node
    int data = 0;
    Node* next = NULL;
};
int main() {
    int N, M, V;
    cin &gt;&gt; N &gt;&gt; M &gt;&gt; V;
    vector&lt;Node&gt; head(N + 1);
    for (int i = 0; i &lt; M; i++) {
        int a, b;
        cin &gt;&gt; a &gt;&gt; b;
        Node* tmp = new Node();    // linked list
        tmp-&gt;data = b;
        tmp-&gt;next = head[a].next;
        head[a].next = tmp;
        Node* tmp2 = new Node();
        tmp2-&gt;data = a;
        tmp2-&gt;next = head[b].next;
        head[b].next = tmp2;
    }
    queue&lt;int&gt; q;
    map&lt;int, int&gt; visited;        // 방문한 정점 저장
    q.push(V);
    cout &lt;&lt; V &lt;&lt; &quot; &quot;;
    visited.insert({ V, 1 });
    while (size(q) != 0) {        // 큐에 정점이 없을 때까지
        int curr = q.front();    // 큐의 맨 앞 정점
        Node head_tmp = head[curr];
        vector&lt;int&gt; neighbor;
        while (head_tmp.next != NULL) {    // 이웃 정점 저장
            head_tmp = *head_tmp.next;
            neighbor.push_back(head_tmp.data);
        }
        q.pop();                // 선택된 정점 큐에서 제거
        sort(neighbor.begin(), neighbor.end());    // 번호가 작은 것 먼저 방문
        for (int i = 0; i &lt; size(neighbor); i++) {
            if (visited.find(neighbor[i]) == visited.end()) {    // 방문하지 않은 정점
                q.push(neighbor[i]);        // 큐에 저장
                cout &lt;&lt; neighbor[i] &lt;&lt; &quot; &quot;;
                visited.insert({ neighbor[i], 1});
            }
        }
    }
    return 0;
}</code></pre>
<blockquote>
<p>Python</p>
</blockquote>
<pre><code class="language-python"># 나중에 추가해보겠음</code></pre>
<h2 id="특징">특징</h2>
<p>BFS는 <strong>최단 경로 해</strong>의 탐색을 보장하지만 
<strong>모든 이웃 정점을 방문</strong>하기 때문에 메모리 공간 사용이 <strong>비효율적</strong>이다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[C] 제네릭 프로그래밍 / 함수형 포인터, void 포인터]]></title>
            <link>https://velog.io/@fluid_silver/C-%EC%A0%9C%EB%84%A4%EB%A6%AD-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%ED%95%A8%EC%88%98%ED%98%95-%ED%8F%AC%EC%9D%B8%ED%84%B0-void-%ED%8F%AC%EC%9D%B8%ED%84%B0</link>
            <guid>https://velog.io/@fluid_silver/C-%EC%A0%9C%EB%84%A4%EB%A6%AD-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%ED%95%A8%EC%88%98%ED%98%95-%ED%8F%AC%EC%9D%B8%ED%84%B0-void-%ED%8F%AC%EC%9D%B8%ED%84%B0</guid>
            <pubDate>Mon, 18 Jul 2022 08:14:01 GMT</pubDate>
            <description><![CDATA[<h2 id="제네릭-프로그래밍">제네릭 프로그래밍</h2>
<p>제네릭 프로그래밍(generic programming)이란, 데이터 형식에 의존하지 않고, 하나의 값이 여러 다른 데이터 타입들을 가질 수 있도록 하는 프로그래밍 방식이다. </p>
<p>즉, 프로그래머가 어떤 자료형을 사용할 지 미리 알 수 없기 때문에 <strong>어떤 자료형이든 상관없이</strong> 실행할 수 있도록 프로그래밍한 것이다. </p>
<p>이 글에서는 C언어로 제네릭 프로그래밍을 직접 해 볼 것이지만, 이해하기 쉽도록 미리 예시를 들어보자면 C++ STL 중 <code>vector</code>를 생성할 때 <code>vector&lt;int&gt;</code>, <code>vector&lt;double&gt;</code> 등 <code>&lt; &gt;</code> 안에 원하는 자료형을 적어 사용하는 것을 볼 수 있다. </p>
<p>그리고 C++의 템플릿은 제네릭 프로그래밍으로 되어있다. </p>
<h2 id="함수형-포인터">함수형 포인터</h2>
<p>제네릭 프로그래밍을 설명하기 전에, 함수형 포인터를 먼저 설명하겠다. 
함수형 포인터란, 말 그대로 함수를 포인터로 사용하는 것인데 예시를 먼저 보자.</p>
<pre><code>#include&lt;stdio.h&gt;
int maxv(int a, int b) {
    return a &gt; b ? a : b;
}

int main() {
    int(* c)(int, int) = maxv;    // 함수형 포인터
    printf(&quot;%d&quot;, c(5,3));
    return 0;
}</code></pre><p>먼저, 큰 값을 반환하는 <code>maxv</code> 함수를 만들었다. 
그리고 <code>int(* c)(int, int)</code>라고 쓴 부분이 함수형 포인터를 만든 것이다. </p>
<p><strong>반환값 자료형</strong>(ex. <code>int</code>), <strong>함수형 포인터 이름</strong>(ex. <code>c</code>), 괄호 안에는 <strong>매개변수 자료형</strong>(ex. <code>(int, int)</code>)을 순서대로 써주면 된다. 
그리고 코드에 쓴 것처럼 <code>int(* c)(int, int) = maxv;</code>라고 쓰면 함수형 포인터 <code>c</code>에는 <strong>함수 <code>maxv</code>의 주소</strong>가 들어가는 것이다. </p>
<p>그 후에는 함수형 포인터 <code>c</code>를 함수처럼 사용할 수 있다. 
(ex. <code>c(5, 3)</code>과 <code>maxv(5, 3)</code>은 같다.)</p>
<h2 id="void-포인터">void 포인터</h2>
<p>void 포인터에 대해서도 간단하게 설명하겠다. 
제네릭 프로그래밍을 하면서 <code>void*</code>는 계속 쓰이기 때문에 꼭 알아야 한다. </p>
<p>먼저 포인터는 <strong>주소값</strong>을 저장해준다. 
<code>int*</code>를 쓰면 저장된 시작 주소로부터 4byte의 데이터(<code>int</code>형 데이터)를 읽어낼 것이고,
<code>double*</code>를 쓰면 저장된 시작 주소로부터 8byte의 데이터(<code>double</code>형 데이터)를 읽어낼 것이다. </p>
<p>그런데 우리는 저장하려는 주소에 <code>int</code>형 데이터가 들어갈지 <code>double</code>형 데이터가 들어갈지 모르기 때문에 임시로 <code>void*</code>를 쓴다. </p>
<p><code>void*</code>는 주소가 가리키는 데이터의 자료형은 모르겠지만 일단 <strong>주소만 저장</strong>되어있다. 
그렇기 때문에 사용할 때 <strong>사용하려는 데이터의 자료형을 가리키는 포인터</strong>로 바꿔야한다. </p>
<h2 id="제네릭-프로그래밍-예제">제네릭 프로그래밍 예제</h2>
<p>그러면, 제네릭 프로그래밍으로 배열에 원하는 숫자가 있는지 찾아서 위치를 반환하는 함수 <strong>linear_search</strong>를 만들어보겠다. </p>
<p>이 예제는 배열에 1~100의 숫자가 들어가 있고 (자료형은 찾는 값에 따라 정해짐), 
찾는 값이 <code>int</code>, <code>double</code> 등 숫자 자료형으로 이루어져 있지만 <strong>자료형이 무엇인지 정해져 있지 않기 때문에</strong> 제네릭 프로그래밍을 해야한다. </p>
<blockquote>
<h4 id="linear_search-함수-코드">linear_search 함수 코드</h4>
</blockquote>
<pre><code>#include&lt;stdio.h&gt;
#include&lt;math.h&gt;
#include&lt;stdlib.h&gt;
int linear_search(void* _arr, int size, int esize, void* _key) {
    char* arr = (char*)_arr;
    char* key = (char*)_key;
    for (int i = 0; i &lt; size; i++) {
        int cnt = 0;
        for (int j = 0; j &lt; esize; j++) {    // 자료형 크기만큼 반복하며 숫자 하나를 비교
            if (*(arr + (esize * i) + j) == *(key + j)) {
                cnt++;
            }
        }
        if (cnt == esize) {
            return i;
        }
    }
    return -1;
}</code></pre><p>linear_search의 매개변수는 자료형이 <code>void*</code>인 배열, 자료형이 <code>int</code>인 배열의 크기, 자료형이 <code>int</code>인 찾는 값 key의 자료형 크기, 자료형이 <code>void*</code>인 찾는 값으로 이루어져 있다.
<strong>배열과 찾는 값의 자료형이 <code>void*</code></strong> 인 이유는 위에서도 말했듯이 자료형에 상관없이 찾을 수 있도록 하기 위해서이다. </p>
<p>그 다음 줄을 보면 배열과 찾는 값을 <code>char*</code>로 바꿔주는데, 그 이유는 <strong>1byte</strong>인 <code>char*</code>로 <strong>어떤 자료형이든 비교</strong>할 수 있기 때문이다. </p>
<p>자세히 설명하자면, 위에서 void 포인터는 사용할 때 사용하려는 데이터의 자료형을 가리키는 포인터로 바꿔야한다고 했는데 
우리는 자료형을 모르기 때문에 <strong>사용하려는 데이터의 자료형으로 바꾸는 코드를 함수 안에 작성할 수 없다.</strong></p>
<p>따라서 <strong>byte를 하나씩 비교</strong>하며 찾아줄 것인데, <code>char*</code>은 1byte의 데이터를 가리키기 때문에 
<code>int</code>형은 1byte를 4번, <code>double</code>형은 1byte를 8번 반복하여 같은지 비교할 수 있다. </p>
<p>그 다음 줄에 나오는 <code>for</code>문이 비교하는 과정을 나타낸다. 
배열을 차례대로 돌면서 비교하다가 찾는 값과 같은 값을 발견하면 인덱스를 반환한다.
찾지 못하면 -1을 반환한다. </p>
<blockquote>
<h4 id="main-코드-double형-데이터-찾기">main 코드 (double형 데이터 찾기)</h4>
</blockquote>
<pre><code>int main() {
    void* ptr = malloc(sizeof(double) * 100);    // 동적할당 (double형 배열)
    double* a = ptr;    // double형 배열
    for (int i = 0; i &lt; 100; i++) {        // 배열 초기화
        a[i] = i;
    }
    for (int i = 0; i &lt; 100; i++) {        // 배열 출력
        printf(&quot;%f\n&quot;, *(a+i));
    }
    double key = 24;    // 찾는 값
    int ans = linear_search(a, 100, sizeof(double), &amp;key);
    printf(&quot;%d&quot;, ans);
    return 0;
}</code></pre><p>linear_search 함수의 매개변수 자료형을 보면 원래 <code>void*</code>를 넣어야 하는 자리에 그냥 <code>double*</code>를 넣은 것을 볼 수 있다.
<code>void*</code>에 넣을 때는 굳이 <code>void*</code>로 바꿔주지 않고 넣어도 된다. 하지만 헷갈린다면 형변환을 해줘도 좋다. </p>
<h3 id="문제점">문제점</h3>
<p><code>int</code>형 데이터를 찾을 때는 문제가 없을 수 있지만, <code>double</code>형 데이터를 찾을 때 배열을 초기화하는 과정을 다르게 하면 <strong>부동소수점</strong>으로 인해 찾지 못하는 경우가 생긴다.</p>
<blockquote>
<h4 id="main-코드-배열-초기화-수정">main 코드 배열 초기화 수정</h4>
</blockquote>
<pre><code>int main() {
    void* ptr = malloc(sizeof(double) * 100);    // 동적할당 (double형 배열)
    double* a = ptr;    // double형 배열
    for (int i = 0; i &lt; 100; i++) {        // 배열 초기화 다른 방법
        a[i] = sqrt(i) * sqrt(i);
    }
    for (int i = 0; i &lt; 100; i++) {        // 배열 출력
        printf(&quot;%f\n&quot;, *(a+i));
    }
    double key = 24;    // 찾는 값
    int ans = linear_search(a, 100, sizeof(double), &amp;key);
    printf(&quot;%d&quot;, ans);
    return 0;
}</code></pre><p>배열 초기화를 할 때 넣으려는 값 <code>i</code>에 <strong>루트를 씌우고, 다시 제곱</strong>을 해줬다. 
이렇게 초기화를 하고 배열을 출력해 보면 값이 똑같이 출력되는 것을 볼 수 있다.</p>
<p>하지만 컴퓨터는 찾는 값 24를 찾지 못한다. 그 이유는 우리가 <strong>byte를 하나씩 비교</strong>해가며 같은 값인지 찾았기 때문이다. </p>
<p>사람이 직접 계산하기로는 루트를 씌우고 다시 제곱을 하는 것은 루트가 그냥 없어진다고 생각할 수 있다. 하지만 컴퓨터는 루트를 계산하고, 계산한 값을 다시 제곱한다. 
$$\sqrt{24}$$는 무리수이다. 그렇기 때문에 컴퓨터는 정확한 값을 다 적어낼 수가 없다. 정확하지 않은 값을 제곱하여 나타내니 <strong>아주 작은 오차</strong>가 생기게 된다. </p>
<p>배열을 출력할 때는 아주 작은 오차가 보이지 않았지만, byte를 비교하니까 다른 점이 생긴 것이다. </p>
<h3 id="문제점-해결">문제점 해결</h3>
<p>문제점을 해결하기 위해 <code>int</code>형일 때와 <code>double</code>형일 때의 숫자 비교 방법을 나누어 함수를 만들어 줄 것이다. 
그리고 그 함수를 linear_search 매개변수에 넣어줄 것인데, 이 때 <strong>함수형 포인터</strong>가 사용이 된다.</p>
<blockquote>
<h4 id="새로운-linear_search-함수-코드">새로운 linear_search 함수 코드</h4>
</blockquote>
<pre><code>#include&lt;stdio.h&gt;
#include&lt;stdlib.h&gt;
#include&lt;math.h&gt;
int int_compare(const void* a, const void* b) {
    int* aa = (int*)a;
    int* bb = (int*)b;
    return (* bb &gt; *aa) ? -1 : (*aa &gt; *bb);
}
int double_compare(const void* a, const void* b) {
    double* aa = (double*)a;
    double* bb = (double*)b;
    if (fabs(*bb - *aa) &lt; 0.0001) {
        return 0;
    }
    if (*aa &gt; *bb) {
        return 1;
    }
    else {
        return -1;
    }
}
int linear_search(void* _arr, int size, int esize, void* _key, int (*compare_function)(const void*, const void*)) {
    char* arr = (char*)_arr;
    char* key = (char*)_key;
    for (int i = 0; i &lt; size; i++) {
        if (compare_function(arr + esize * i, key) == 0) {
            return i;
        }
    }
    return -1;
}</code></pre><p>int_compare 함수와 double_compare 함수를 설명하기 전에, <strong>새로운 linear_search</strong> 함수를 먼저 설명하겠다.
위에 있던 linear_search 함수와 달라진 점은 
매개변수에 <code>int (*compare_function)(const void*, const void*)</code>가 추가된 것이다. 
이 코드에 함수형 포인터와 void 포인터가 모두 쓰였다. </p>
<p>함수형 포인터를 쓴 이유는 int_compare 함수와 double_compare 함수 중 어떤 함수를 사용할 지 모르기 때문이고, 매개변수에 void 포인터를 쓴 이유는 마찬가지로 자료형이 <code>int</code>형인지 <code>double</code>형인지 모르기 때문이다. </p>
<p>그리고 숫자를 비교하는 부분을 간단하게 함수로 나타내주었다. 
두 숫자를 비교했을 때 같은 숫자가 나오면 0을 출력하도록 함수를 만들 것이다. </p>
<p>이제 <strong>int_compare</strong> 함수와 <strong>double_compare</strong> 함수를 설명하겠다. 
원래는 자료형을 모르기 때문에 byte를 하나하나 비교했다면, int_compare 함수와 double_compare 함수를 사용하게 되면서 <strong>int형인지 double형인지 함수는 알게 된다.</strong> 
그렇기 때문에 int_compare 함수에서 <code>void*</code>로 받은 매개변수를 <code>int*</code>로 <strong>형변환</strong>하여 숫자를 바로 비교한다. </p>
<p>double_compare 함수는 다르다. 위에서 설명했던 문제점 때문에 우리는 <strong>아주 작은 오차는 무시</strong>하고 비교하도록 코드를 짜야 한다. 
마찬가지로 먼저 <code>void*</code>로 받은 매개변수를 <code>double*</code>로 형변환하고, <strong>두 숫자의 차의 절댓값</strong>을 계산한다. 이 차이가 자신이 설정한 아주 작은 오차보다 더 작게 나온다면 같은 숫자라고 판단하는 것이다. </p>
<blockquote>
<h4 id="main-코드-int형-데이터-찾기">main 코드 (int형 데이터 찾기)</h4>
</blockquote>
<pre><code>int main() {
    void* ptr = malloc(sizeof(int) * 100);    // 동적할당 (int형 배열)
    int* a = ptr;    // int형 배열
    for (int i = 0; i &lt; 100; i++) {        // 배열 초기화
        a[i] = (int)(sqrt(i) * sqrt(i) + 0.5);
    }
    for (int i = 0; i &lt; 100; i++) {        // 배열 출력
        printf(&quot;%d\n&quot;, *(a+i));
    }
    int key = 24;    // 찾는 값
    int ans = linear_search(a, 100, sizeof(int), &amp;key, int_compare);
    printf(&quot;%d&quot;, ans);
    return 0;
}</code></pre><h4 id="main-코드-double형-데이터-찾기-1">main 코드 (double형 데이터 찾기)</h4>
<pre><code>int main() {
    void* ptr = malloc(sizeof(double) * 100);    // 동적할당 (double형 배열)
    double* a = ptr;    // double형 배열
    for (int i = 0; i &lt; 100; i++) {        // 배열 초기화
        a[i] = sqrt(i) * sqrt(i);
    }
    for (int i = 0; i &lt; 100; i++) {        // 배열 출력
        printf(&quot;%f\n&quot;, *(a+i));
    }
    double key = 24;    // 찾는 값
    int ans = linear_search(a, 100, sizeof(double), &amp;key, double_compare);
    printf(&quot;%d&quot;, ans);
    return 0;
}</code></pre><p>만약 <code>int</code>형 데이터를 찾으려면 배열 초기화를 바꾸는 것이 좋다. 
<code>double</code>형에서의 문제점을 찾기 위해 일부러 루트를 씌우고, 제곱을 했기 때문에 <code>int</code>형으로 바꾸기 위해 번거롭게 반올림을 해주었다. 
<code>double</code>형에서의 문제점을 초점으로 한 코드이기 때문에 <strong><code>double</code>형 코드를 중점</strong>으로 보길 바란다.</p>
<p>배열 초기화를 잘 하면 <code>int</code>형이든 <code>double</code>형이든 원하는 값의 인덱스를 잘 찾아낼 것이다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Python] 자동 로그인, 구매 등 매크로 만들기 (selenium 사용)]]></title>
            <link>https://velog.io/@fluid_silver/Python-%EC%9E%90%EB%8F%99-%EB%A1%9C%EA%B7%B8%EC%9D%B8-%EA%B5%AC%EB%A7%A4-%EB%93%B1-%EB%A7%A4%ED%81%AC%EB%A1%9C-%EB%A7%8C%EB%93%A4%EA%B8%B0-selenium-%EC%82%AC%EC%9A%A9</link>
            <guid>https://velog.io/@fluid_silver/Python-%EC%9E%90%EB%8F%99-%EB%A1%9C%EA%B7%B8%EC%9D%B8-%EA%B5%AC%EB%A7%A4-%EB%93%B1-%EB%A7%A4%ED%81%AC%EB%A1%9C-%EB%A7%8C%EB%93%A4%EA%B8%B0-selenium-%EC%82%AC%EC%9A%A9</guid>
            <pubDate>Thu, 20 Jan 2022 14:26:32 GMT</pubDate>
            <description><![CDATA[<h2 id="웹-사이트-열기-chrome">웹 사이트 열기 (chrome)</h2>
<p>먼저 chromedriver-autoinstaller, selenium을 다운받고 코드를 실행해야 합니다. </p>
<pre><code class="language-python"># 파일명 : main.py

import chromedriver_autoinstaller

chromedriver_autoinstaller.install()

from sutils import get_selenium_driver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

driver = get_selenium_driver(False)
driver.get(&#39;웹 사이트 주소 입력&#39;)</code></pre>
<p>프로젝트 안에서 새 Python File을 열고 다음 코드를 입력합니다.</p>
<pre><code class="language-python"># 파일명 : sutils.py

from selenium import webdriver

def get_selenium_driver(headless=True):
    chrome_options = webdriver.ChromeOptions()
    if headless:
        chrome_options.add_argument(&#39;--headless&#39;)

    chrome_options.add_argument(&#39;--no-sandbox&#39;)
    chrome_options.add_argument(&#39;lang=en&#39;)
    chrome_options.add_argument(&#39;--disable-dev-shm-usage&#39;)
    chrome_options.add_argument(&quot;disable-gpu&quot;)
    chrome_options.add_argument(&quot;disable-infobars&quot;)
    chrome_options.add_argument(&quot;--disable-extensions&quot;)

    driver = webdriver.Chrome(options=chrome_options)
    driver.set_window_size(1920, 1080)
    return driver</code></pre>
<p><strong>만약 위 코드를 사용하고 자동 로그인(아래에 코드 있음)을 시도했을 때 로그인이 막힌다면, 다음 코드를 main.py에 입력합니다. (이 코드를 사용할 때는 sutils.py가 필요없습니다.)</strong>
다음 코드를 입력해도 로그인이 막힌다면, 저도 잘 모릅니다 ..</p>
<pre><code class="language-python">import chromedriver_autoinstaller

chromedriver_autoinstaller.install()

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

from selenium.webdriver.chrome.options import Options
import subprocess

def get_original_chrome_driver():
    subprocess.Popen(r&#39;C:\Program Files\Google\Chrome\Application\chrome.exe --remote-debugging-port=9222 --user-data-dir=&quot;C:\chrometemp&quot;&#39;)  
                       # chrome이 있는 위치를 적어줍니다.

    option = Options()
    option.add_experimental_option(&quot;debuggerAddress&quot;, &quot;127.0.0.1:9222&quot;)

    chrome_ver = chromedriver_autoinstaller.get_chrome_version().split(&#39;.&#39;)[0]
    try:
        driver = webdriver.Chrome(f&#39;./{chrome_ver}/chromedriver.exe&#39;, options=option)
    except:
        chromedriver_autoinstaller.install(True)
        driver = webdriver.Chrome(f&#39;./{chrome_ver}/chromedriver.exe&#39;, options=option)
    driver.implicitly_wait(10)
    driver.set_window_size(1920, 1080)
    return driver

driver = get_original_chrome_driver()
driver.get(&#39;웹 사이트 주소 입력&#39;)</code></pre>
<p>(이제부터 나오는 코드들은 main.py에 입력합니다.)</p>
<h2 id="자동-로그인">자동 로그인</h2>
<p>먼저 예시를 보여드리겠습니다.
저는 나이키 로그인을 시도해봤습니다. </p>
<pre><code class="language-python">id = &#39;아이디 입력&#39;
pw = &#39;비밀번호 입력&#39;

try:
    WebDriverWait(driver, 3).until(EC.presence_of_element_located((By.CLASS_NAME, &#39;login&#39;)))
    elem_btn = driver.find_element(By.CLASS_NAME, &#39;login&#39;)
    elem_btn.click() # 버튼 클릭

    WebDriverWait(driver, 3).until(EC.presence_of_element_located((By.ID, &#39;j_username&#39;)))
    elem_id = driver.find_element(By.ID, &#39;j_username&#39;)
    elem_id.send_keys(id) # id 입력

    WebDriverWait(driver, 3).until(EC.presence_of_element_located((By.ID, &#39;j_password&#39;)))
    elem_pw = driver.find_element(By.ID, &#39;j_password&#39;)
    elem_pw.send_keys(pw)

    login_btn = driver.find_element(By.XPATH, &#39;//button[@class=&quot;button large width-max&quot;]&#39;)
    login_btn.click()
except: # 이미 로그인이 되어있는 경우
    pass </code></pre>
<p>위 코드를 보면 </p>
<pre><code class="language-python">WebDriverWait(driver, 3).until(EC.presence_of_element_located((By.CLASS_NAME, &#39;login&#39;)))
elem_btn = driver.find_element(By.CLASS_NAME, &#39;login&#39;)
elem_btn.click()</code></pre>
<p>이러한 형태의 코드가 계속 반복됩니다. 
<code>WebDriverWait(driver, 3)</code> : 창이 뜰 때까지 최대 3초 기다린다는 뜻
<code>By.CLASS_NAME, &#39;login&#39;</code> , <code>By.XPATH, &#39;//button[@class=&quot;button large width-max&quot;]&#39;</code> , ...
: 웹 사이트에서 F12를 누른 후 클릭하고 싶은, 또는 입력하고 싶은 곳을 찾아 코드를 작성합니다. 위 코드는 나이키 로그인에 맞는 코드이므로 다른 사이트에 적용하려면 수정해야합니다. </p>
<h2 id="자동-구매">자동 구매</h2>
<p>구매도 똑같습니다. </p>
<pre><code class="language-python">WebDriverWait(driver, 3).until(EC.presence_of_element_located((By.CLASS_NAME, &#39;login&#39;)))
elem_btn = driver.find_element(By.CLASS_NAME, &#39;login&#39;)
elem_btn.click()</code></pre>
<p>이 코드를 수정하여 사용합니다. </p>
<h3 id="마지막에-아래의-코드를-작성한-후-실행합니다">마지막에 아래의 코드를 작성한 후, 실행합니다.</h3>
<pre><code class="language-python">import time

time.sleep(600) # 600초 동안 창이 꺼지지 않음

driver.close()</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Python] 소수 구하기, 약수 찾기 알고리즘]]></title>
            <link>https://velog.io/@fluid_silver/Python-%EC%86%8C%EC%88%98-%EA%B5%AC%ED%95%98%EA%B8%B0-%EC%95%BD%EC%88%98-%EC%B0%BE%EA%B8%B0-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98</link>
            <guid>https://velog.io/@fluid_silver/Python-%EC%86%8C%EC%88%98-%EA%B5%AC%ED%95%98%EA%B8%B0-%EC%95%BD%EC%88%98-%EC%B0%BE%EA%B8%B0-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98</guid>
            <pubDate>Tue, 18 Jan 2022 13:47:34 GMT</pubDate>
            <description><![CDATA[<h2 id="약수-찾기">약수 찾기</h2>
<p>소수를 구하려면 약수를 찾는 방법부터 알아야 한다. 
소수의 정의는 <strong>1과 자기 자신을 약수로 가지는 수</strong>, 즉 <strong>약수가 2개인 수</strong>이기 때문이다.</p>
<pre><code class="language-python">def is_divisor(n: int, m: int):
    return n % m == 0</code></pre>
<p>n을 m으로 나누었을 때 나머지가 0이면 m이 n의 약수이고, 0이 아니면 m이 n의 약수가 아니다. </p>
<h2 id="소수-구하기">소수 구하기</h2>
<p>다양한 방법을 소개할 것인데, 아래로 갈 수록 더 간단하고 빠른 코드이다. 
간단하고 빠른 코드로 바꾸는 과정을 설명한다.</p>
<blockquote>
<h3 id="첫-번째-방법">첫 번째 방법</h3>
</blockquote>
<pre><code class="language-python">def is_prime(num: int):
    count = 0  # 약수의 개수
    for i in range(1, num + 1):  # 1 ~ num 사이의 약수 개수 구하기
        if is_divisor(num, i):
            count = count + 1
    return count == 2  # 약수의 개수 2개 == 소수</code></pre>
<p>이 방법은 구하는 시간이 오래 걸린다.
N이 소수인지 알기 위해서 for문을 N번 반복해야 하기 때문이다. (시간복잡도가 N이다.)</p>
<p>구하는 시간을 줄여보자.
첫 번째 방법에서 약수를 찾기 위해 1부터 num까지 하나하나 검사해보았는데, 
1부터 <strong>$$\sqrt{num}$$</strong> 까지만 검사해도 된다.</p>
<p>(증명 방법 배웠는데 기억이 안나서 찾으면 올림 ..)</p>
<p>예시를 들어 설명하자면, 
16의 약수는 1, 2, 4, 8, 16이다. 16 = 1 x 16 = 2 x 8 = 4 x 4 이므로 
1, 2, 4만 구해도 $$\sqrt{16}$$ (= 4) 보다 큰 약수인 8, 16은 자동으로 구해진다. 
1과 16, 2와 8, 4와 4는 각각 한 세트로 생각한다.</p>
<p>이렇게 하면 <strong>약수가 1개인 수</strong>가 소수이다.
숫자 자기 자신은 검사하지 않기 때문에 약수가 1만 나온다.</p>
<blockquote>
<h3 id="두-번째-방법">두 번째 방법</h3>
</blockquote>
<pre><code class="language-python">def is_prime_up_to_root(num: int):
    if num == 1:  # 1은 소수 아님
        return False
    count = 0
    i = 1
    while i * i &lt;= num:  # 1 ~ 루트 num 사이의 약수 개수 구하기
                         # 실수가 나오지 않도록 양변을 제곱하여 식을 변형함
        if is_divisor(num, i):
            count = count + 1
        i = i + 1
    return count == 1</code></pre>
<p>구하는 시간을 더 줄여보자.
처음부터 <strong>2의 배수를 제외</strong>하는 것이다. 
그렇다면 구하는 시간이 절반으로 줄어든다. </p>
<blockquote>
<h3 id="세-번째-방법">세 번째 방법</h3>
</blockquote>
<pre><code class="language-python">def is_prime_up_to_root_out_mult2(num: int):
    if num == 1:
        return False
    if num % 2 == 0:  # 2의 배수 제외
        return num == 2  # 2만 소수
    count = 0
    i = 1
    while i * i &lt;= num:
        if is_divisor(num, i):
            count = count + 1
        i = i + 1
    return count == 1</code></pre>
<p>3의 배수도 생략하면 시간이 더더 줄어든다. </p>
<p>코드를 더 간단하게 정리해보자.</p>
<blockquote>
<h3 id="네-번째-방법">네 번째 방법</h3>
</blockquote>
<pre><code class="language-python">def is_prime_up_to_root_out_mult2_2(num: int):
    if num % 2 == 0: 
        return num == 2 
    if num % 3 == 0:
        return num == 3
    i = 5  # 1은 나머지가 무조건 0이기 때문에 제외, 2, 3, 4는 위에서 판별
    while i * i &lt;= num:
        if num % i == 0:  # num이 i를 약수로 가짐
            return False
        i = i + 2  # i가 짝수일 때 num이 i를 약수로 가지면 num은 짝수라서 이미 판별
    return num != 1  # 1은 소수 아님</code></pre>
<p>이 방법은 약수를 찾는 함수가 필요 없다. 
또한 1을 제외한 다른 약수가 구해지면 num을 바로 합성수라고 판단하고 
<code>return False</code>가 실행되어 구하는 시간이 줄어든다.</p>
<p>이 외에도 코드를 더 간단하고 빠르게 만드는 방법이 존재할 수 있다. </p>
<h2 id="소수-출력">소수 출력</h2>
<p>소수 구하는 함수를 실행시키는 방법이다. </p>
<pre><code class="language-python">number = 1
while True:  # 계속 반복함
    if is_prime(number):
        print(number)
    number = number + 1</code></pre>
]]></description>
        </item>
    </channel>
</rss>