Hash란?


해쉬는 임의의 크기를 가진 데이터를 고정된 데이터의 크기로 변환시키는 것을 말한다. 

이를 이용하면 입력하고자 하는 데이터의 값을 특정한 배열의 위치나 인덱스로 이용해 저장하거나 찾을 수 있다.

해쉬를 이용하면 즉시 저장하거나 찾고자 하는 위치를 참조할 수 있으므로 더욱 빠른 속도로 처리할 수 있다.

 

 

1. Direct Addressing Table


Direct Addressing Table은 key-value쌍의 데이터를 배열에 저장할 때, key값을 직접적으로 배열의 인덱스로 사용하는 방법이다.

 

예를 들어 키 값이 400인 데이터가 있다면, 이는 배열의 인덱스가 400인 위치에 키 값을 저장하고 포인터로 데이터를 연결한다.

 

똑같은 키 값이 존재하지 않는다고 가정하면

삽입 시에는 각 키마다 자신의 공간이 존재하므로 그 위치에 저장을 하면 되고, 

삭제 시에는 해당 키의 위치에 NULL값을 넣어주면 되고,

탐색 시에는 해당 키의 위치를 찾아가서 참조하면 된다.

 

찾고자 하는 데이터의 key만 알고 있으면 즉시 위치를 찾는 것이 가능하므로 탐색, 저장, 삭제, 갱신은 모두 선형시간 O(1)로 매우 빠른 속도로 처리가 가능하다.

 

다만 key값의 최대 크기만큼 배열이 할당되기 때문에, 크기는 매우 큰데 저장하고자 하는 데이터가 적다면 공간을 많이 낭비할 수 있다는 단점이 있다.

 

 

2. Hash Table


Hash Table은 key-value쌍의 데이터에서 key값을 테이블에 저장할 때, key값을 함수를 이용해 계산을 수행 한 후, 그 ㅇ결과값을 배열의 인덱스로 사용하여 저장하는 방식이다. key값을 계산하는 함수는 해쉬함수(Hash Function)라고 부르며, 해쉬함수는 입력으로 key를 받아, 0부터 배열의 크기-1 사이의 값을 출력한다. 해쉬에 대한 첫 정의대로 임의의 숫자를 배열의 크기만큼으로 변환 시킨것이다. 이 경우 k값이 h(k)로 해쉬되었다고 하며, h(k)는 k의 해쉬값이라고 한다.

 

Hash Table은 Direct Addressing Table에 비해 공간 낭비가 매우 적은데, 이는 테이블의 크기가 key값에 좌우되는 것이 아니고, h(k)만큼의 공간에 저장되기 때문이다.

 

 

2.1 충돌(Collusion)


Hash Table은 충돌이 일어날 수 있다는 큰 문제점이 있다.

충돌이란 서로 다른 key값이 동일한 h(k)값을 가져 동일한 slot에 저장되는 경우를 말한다.

예를 들어, h(k1)=h(k2) 인 경우를 들 수 있다. Direct Addressing Table에서는 이를 방지하기 위해 모든 key값이 다르다고 전제하지만, 해쉬 테이블에서는 key값이 달라도 해쉬의 결과가 같을 수 있기 때문에 이를 방지하기 위한 방법이 필요하다. 하지만 해쉬 함수를 짜더라도 충돌을 '완전히' 방지한다는 것을 보장하기 힘드므로, 충돌을 방지하기 위한 방법으로 충돌을 어느정도 허용하되 이를 최소화 하는 방법을 사용하기도 한다.

 

 

2.1.1 Chaining 방법 - 충돌을 허용하되 최소화 하는 방법

충돌을 허용하지만 이를 최소화하기 위한 방법중 하나는 chaining 방식이다. 

chaining이란 데이터들을 포인터를 이용해 체인 형태로 엮어 나가는 것을 말한다.

Hash Table에서 동일한 해쉬값이 출력되 충돌이 일어나면, 그 위치에 있던 데이터에 key값을 포인터 뒤이어 연결한다.

따라서 최초로 h(k)위치에 저장된 데이터를 시작으로 그 이후의 h(k)값이 출력되면 연결 리스트의 형태를 취한다.

그렇기 때문에 최초의 위치를 탐색하는 해쉬 과정을 제외하고, 모든 탐색, 삽입, 삭제 과정은 연결리스트와 유사한 방식으로 진행한다.

 

chaining방법에서의 수행시간은 삽입 시에는 해쉬값을 이용해 slot에 저장하면 되므로 상수시간에 일어나고,

삭제는 연결리스트의 삭제와 동일하게 상수시간에 일어나고,

탐색은 연결리스트를 따라가기 때문에 리스트의 길이 만큼 발생하지만, 최악의 경우, 즉 모든 데이터의 해쉬값이 일치하여 한 인덱스에 저장됐을 경우엔 연결리스트의 탐색 시간과 동일한 선형시간을 가지게 된다.

 

2.1.2 적재율

최악의 경우는 극단적인 예로, 평균적인 경우엔 O(a+1)의 시간이 걸린다. a는 적재율을 뜻하며 적재율이란 현재 저장된 key 값 갯수 (K)를 전체 테이블의 갯수(N)로 나눈 값(K/N)이다. 즉 현재 저장된 데이터가 많으면 많아질수록 충돌 확률이 높아져 연결 리스트 탐색 확률도 증가하며, 적을수록 리스트 탐색 확률이 적어진다는 것이다.

 

2.1.3 Simple uniform hash

충돌을 최소화 하는 방법 중에 충돌이 적은 좋은 해쉬 함수를 만드는 방법도 있다.

좋은 해쉬 함수의 조건은 Simple uniform hash 함수를 만드는 것으로, 이 조건은 다음과 같다.

  1. 계산된 해쉬 값의 범위는 0부터 배열의 크기-1 사이이며, '동일환 확률'로 골고루 나타나야 한다.
  2. 각각의 해쉬값들은 서로 연관성을 가지지 않고 독립적으로 생성되야 한다.

첫번 째 조건을 충족하면 충돌이 일어날 확률이 적어질 것이며,

두번 째 조건은 해쉬값들이 서로 연관이 있을 경우 해쉬값이 등장하는 패턴이나 순서가 존재할 수 있고, 이는 반복적인 충돌을 일으킬 확률이 있기 때문이다.

 

2.1.4 division method

해쉬 함수는 정말 다양하지만 대표적인 해쉬 함수로는 division method가 있는데, modular 연산 방법을 이용하는 방법이다. 특정 key를 어떤 수로 나눈 나머지를 해쉬값으로 사용한다. 

예를 들어 m=100 이면 k mod m 은 0부터 99까지의 범위를 가진다. 이 범위의 m은 해쉬 테이블의 성능을 크게 좌우하는데, m의 크기는 보통 키의 수의 3배가 적당하다고 한다. (적재율이 30%쯤까지 충돌이 거의 일어나지 않는다고 한다.)

그리고 m으로 2^p 값을 사용하는 것엔 큰 주의를 요한다. m이 2^3이면, 2진수로 000010000이고, 4번째이하의 숫자만 해쉬 값에 영향을 끼치기 때문이다.

예를 들어 k1과 k2가 각각 10110100, 10120100이면 둘다 같은 해쉬값을 출력한다. 이를 방지하기 위해서 보통 2^p에 근접한 소수를 선택한다고 한다.

 

즉 가장 최적의 m의 크기는 키의 갯수의 3배이며 2의 지수승에 근접한 소수이다.

3. Open Addressing


Open Addressing은 key값을 테이블에 저장하는 Direct Addressing Table과는 다르게, 모든 데이터(key+데이터)를 테이블에 저장하는 방법이다.

장점으로는 데이터를 직접 모두 읽어오기 때문에 포인터를 쓸 일이 없어 포인터를 사용함으로서 발생 할 수 있는 오버헤드를 방지할 수 있다는 점이다.

포인터가 필요 없어 구현이 훨씬 용이해졌으며, 포인터 접근에 필요한 시간이 없기 때문에 큰 성능 향상이 있다.

 

3.1 Liner probing

포인터를 사용하지 않기 때문에, 앞서 설명한 Chaining방법은 사용할 수 없다. 따라서 다른 방법으로 충돌시에 대처해야 하는데 그 중 하나가 Liner probing이다. 

Liner probing은 충돌이 발생하면 바로 다음 인덱스에 데이터를 저장하는 방식이다. 다음으로 이동한 이후에도 충돌이 발생했다면 다시 다음으로 이동하여 저장한다.

즉 충돌이 일어나지 않을 때 까지 다음으로 이동하며 빈 공간을 찾으면 그 위치에 저장한다.

 

매 충돌시마다 한칸씩 이동하므로 해쉬함수는 다음의 형태를 취하게 된다.

h(k,i) = (k+i) mod m

 

Liner probing은 정말 구현이 용이하지만, primary clustering이라는 문제점을 가지고 있다.

primary clustering은 충돌이 나면, 뒤 슬롯에 데이터를 넣어 하나의 데이터 덩어리를 이루기 때문에, 데이터들이 특정 위치에만 밀집하는 현상을 말한다. 이 현상으로 slot이 많아지면 많아질수록 탐색 시간이 엄청 늘어나게 된다.

3.2  Quadratic probing

primary clustering을 방지하기 위해 hash함수를 다음과 같이 2차식의 형태로 만드는 것이다.

h(k,i) = (h'(k)+c1*i+c2*i^2) mod m

 

Liner probing과는 달리 i가 2차식의 형태를 취해, 한칸씩 이동하는 것이 아닌 c1*i+c2*i^2만큼 이동한다.

 

간단한 예로 해쉬 함수는 h(k,i)=(k+i^2) mod m의 형태일 때를 살펴보자.

m은 7이며, insert값이 76, 40, 48일때 76%7=6, 40%7=5, 48%7=6이 된다. 

3번째 insert값인 48은 충돌이 일어난다. 그래서 i를 하나 증가시켜 h(48,1) = (48+1^2) mod 7 = 0의 위치에 저장하였다. 여기선 충돌이 한번 일어난 경우지만, 만약 0에서도 충돌이 일어났다면 h(48,2) = h(48+2^2) mod 7 = 3의 위치에 저장되었을 것이다. 

 

하지만 Quadratic probing에도 secondary clustering이라는 단점이 있다. 이는 처음 시작 해쉬값이 같을 경우, 그 이후의 해쉬값들도 모두 동일한 값으로 계산되어 충돌이 반복적으로 일어나는 것을 말한다.

 

3.1.3 Double hashing

Quadratic probing의 secondary clustering을 해결하기 위해서 사용하는 방법이다.

원리는 간단한데 해쉬 함수를 2개로 구성하는 것이다. 해쉬 함수는 다음과 같은 형태를 가진다.

h1(k) = k mod m

h2(k) = k mod m2

h(k,i) = (h1(k) + i*h2(k)) mod m

 

 

권한은 왜 필요할까?


안드로이드에서 권한의 목적은 사용자의 개인정보를 보호하는 것입니다. Android앱은 사용자의 민감한 데이터와 특정 시스템 기능을 실행할 때 권한을 요청해야 합니다. 주로 사용자의 연락처나 SMS데이터에 접근해야할 때나 카메라 및 인터넷을 사용해야 할때 사용자에게 요청을 승인받아야 합니다.

 

Android 보안 아키텍처의 중심 디자인 포인트는 기본적으로 앱에 다른 앱, 운영체제 또는 사용자에게 부정적인 영향을 줄 수 있는 작업을 수행할 권한이 없다는 것입니다. 

  • 사용자의 개인 데이터를 읽거나 쓰기
  • 다른 앱의 파일을 읽거나 쓰기
  • 네트워크 액세스를 수행하고, 장치를 깨운 상태로 유지하는 것
  • 등등

이 글에서 알아볼 것은 다음과 같습니다.

  • 사용자에게 권한이 제공되는 방식
  • 설치 시간과 런타임 권한 요청의 차이점
  • 권한이 적용되는 방식
  • 권한 유형 및 그룹과 같은 Android 권한의 작동 방식

권한 승인받기


App은 Manifest 파일에 <uses-permission>태그를 포함하여 필요한 권한을 공개해야합니다.

예를 들어 SMS를 보내야할 때 아래와 같은 코드를 작성해야 합니다.

1
2
3
4
5
6
7
8
9
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="com.example.snazzyapp">
 
    <uses-permission android:name="android.permission.SEND_SMS"/>
 
    <application ...>
        ...
    </application>
</manifest>
cs

'Android' 카테고리의 다른 글

[안드로이드] 기본위젯과 드로어블  (0) 2019.11.16
[Kotlin] TextWatcher  (0) 2019.10.30
[Design] Material Design  (0) 2019.08.18
[Kotlin] Room Library  (0) 2019.08.17
[Kotlin] Permission 얻기  (0) 2019.08.12

django가 무엇인가?

장고는 파이썬에서 웹사이트를 만들 수 있도록 도와주는 도구입니다. 이런 도구를 프레임워크라고 부르기도 합니다. 그래서 장고를 파이썬의 웹 프레임워크라고 합니다. 장고로 웹사이트를 구현하면 가져다 쓸 수 있는 패키지가 많아서 편리합니다. 패키지가 많다는 건 많은 기능들을 내가 직접 구현할 필요 없이 가져다가 쓰기만 하면 됩니다. 예를 들어 내가 만든 웹페이지의 내용을 pdf로 다운로드하게 해주고 싶으면 웹페이지를 pdf로 전환하는 기능을 직접 만들 필요 없이 패키지를 가져다 쓰기만 하면 됩니다. 

 

장고 자체도 파이썬의 패키지입니다. 파이썬에서 패키지는 pip라는 걸 통해서 관리합니다. 파이썬 최신 버전이라면 pip를 사용할 수 있을 겁니다. pip 명령을 입력하여 아래와 같이 나온다면 잘 설치되어있는 겁니다.

pip에서 새로운 패키지를 설치하고 싶으면 pip install 명령어를 이용하면 됩니다. 장고를 설치하고 싶으면 pip install django를 입력하여 설치할 수 있습니다. 장고의 스펠링이 d로 시작한다는 걸 유의해주세요. 아래와 같이 명령어를 실행했다면 장고가 설치되었을 겁니다.

 

이제 장고 프로젝트를 만들어 보겠습니다.

프로젝트를 만들 때는 django-admin이라는 명령어를 사용합니다. 프로젝트를 만들기 전에 프로젝트를 만들 폴더로 이동해주세요. 저는/ home/hyun/python 폴더에 프로젝트를 만들어보겠습니다.

django-admin startproject mysite를 입력하여 mysite라는 장고 프로젝트를 만드는 명령어입니다.

실행후 폴더안을 살펴보면 mysite 폴더가 생긴것을 확인할 수 있고 mysite안을 보면 manage.py와 mysite라는 폴더가 생긴것을 확인할 수 있습니다. 프로젝트도 만들어졌으니 안을 들여다보기전에 서버를 실행해보겠습니다.

manage.py가 들어있는 폴더로 들어와 python manage.py runserver 로 서버를 실행할 수 있습니다.

(저는 python3.6 버전으로 진행하여 python3.6 manage.py runserver 로 서버를 실행했습니다)

서버를 실행하면 밑에서 2번째 줄에 Starting development server at 'http://127.0.0.1:8000/' 이란 문구를 볼 수 있습니다. 이제 웹 브라우저 주소창에 127.0.0.1:8000을 입력하면 장고서버가 잘 실행되는지 확인할 수 있습니다.

127.0.0.1은 특별한 IP로 자기 컴퓨터를 가리킵니다. 그래서 127.0.0.1:8000을 localhost:8000으로 바꿔도 똑같이 실행됩니다. 서버를 중단시키고 싶다면 Terminal창에서 Ctrl+C를 눌러주면 됩니다. 

 

지금까지는 mysite라는 프로젝트를 만들어봤습니다. 이제 장고에서 앱을 만들고 Hello World가 출력되도록 해보겠습니다. 장고 프로젝트는 앱이라는 것들로 구성됩니다. 앱은 웹사이트를 기능별로 분리해놓은 단위라고 생각하면 됩니다. 예를 들어 웹사이트에 게시판 과 투표 기능이 있다면 그 사이트는 게시판 앱과 투표 앱으로 구성할 수 있습니다. 하나의 사이트는 여러개의 앱으로 구성할 수 있습니다. 그럼 첫번째 앱을 만들어 보겠습니다.

python manage.py startapp elections는 elections라는 앱을 만들라는 명령어 입니다.
python3.6 manage.py startapp elections )

실행하면 elections라는 폴더가 만들어진걸 볼 수 있습니다. 프로젝트와 같은 이름의 폴더는 프로젝트의 기본적인 설정을 담고있다고 생각하세요. 방금 만든 elections 폴더로 이동해 views.py을 열어 Hello World를 출력하는 코드를 작성해보겠습니다.

elections/views.py

1
2
3
4
5
6
from django.shortcuts import render
from django.http import HttpResponse
 
# Create your views here.
def index(request):
    return HttpResponse("Hello world")

페이지 요청에 대해서 Hello world라고 HttpResponse를 날려주는 코드입니다. 이 코드가 실행되도록 만들려면 어떤 url을 통해서 index함수가 실행되는지 지정해줘야 합니다. 이 부분은 2단계로 나눠집니다.

 

elections App 안에 views 파일이 있고 그 안에 index가 있으니까 우선 사이트에 접속했을 때 elections App이 실행되는 조건을 지정해줘야 합니다. 그리고 elections App이 실행되면 어느 경우에 index함수가 실행되는지를 지정해줘야 합니다. 우선 elections App이 실행되는 조건을 지정해보도록 하겠습니다. 먼저 mysite/urls.py 를 열어줍니다.

urls.py안에 urlpatterns는 서버로 요청이 들어오면 누가 어떻게 처리할지 담당자를 지정하는 역할을 합니다.

앞 부분이 주소, 뒷 부분이 접속했을 때 누가 처리할 것인지를 의미합니다. 그런데 urlpatterns에 admin이 정의되어 있는데요. 서버를 실행하고 loalhost:8000/admin을 입력해보면 장고 어드민이 실행되는걸 볼 수 있습니다.

 

그럼 localhost:8000 접속을 했을 때 elections App이 실행되게 하려면 어떻게 해야할까요? 먼저 mysite/urls.py를 열어 elections.urls로 연결하라는 코드를 만들어줍니다.

mysite/urls.py

1
2
3
4
5
6
7
from django.contrib import admin
from django.urls import path
from django.conf.urls import url, include
urlpatterns = [
    path('', include('elections.urls')),
    path('admin/'admin.site.urls),
]
 

127.0.0.1:8000으로 접속하면 elections.urls에서 처리하라는 코드인데요. include는 앱에 대해서 접속을 처리해줄 때 반드시 적어줘야하는 부분입니다. 여기까지하면 elections라는 앱이 어떤 경우에 실행되는지까지 정의한겁니다. 원하는건 elections/views.py 안에 index함수가 실행되는겁니다. 이제 index함수가 실행되도록하기위해 elections폴더안에 urls.py 파일을 만들어 주도록합니다.

elections/urls.py

1
2
3
4
5
6
7
8
9
10
from django.urls import path
 
# .은 현재폴더란 의미고 여기서 views라는 모듈을 갖고오라는 의미
from . import views
 
urlpatterns = [
    # ''는 빈경로라는 의미, localhost:8000 뒤에 아무것도 안붙은 경우에 views.index가 실행되도록 만들라는 의미
    path(''views.index),
]
 
 

이제 서버를 실행시키고 접속하면 HelloWorld가 출력되는걸 볼 수 있습니다.

References

 

장고를 활용한 웹사이트 만들기 | 프로그래머스

이 강의는 최신 버전의 장고를 활용하고 있지 않아, 최근 사용되는 장고와 차이가 있습니다. 장고를 배우고 싶으신 분은 다른 튜토리얼 사이트를 이용하시길 추천합니다.

programmers.co.kr

 

+ Recent posts