728x90
반응형

운영체제의 역사는 흐름을 파악하기 위해서 중요하다. 또한 P2P클라우드 컴퓨팅 같은 용어들의 등장배경도 알아볼 수 있다.

 

1. 초창기 컴퓨터(1940) - '애니악' 2진법 사용의 배경이 되었다. 아직까지 운영체제는 없다.

 

2. 일괄 처리 시스템 (1950) 

IC(Integrated Circuit)이라고 논리회로를 아주 작게 구현하였다. 천공카드리더punch card reader라는 입력장치(OMR의 원조라고 보면 된다)와 라인프린터라는 출력장치를 가지고 있게 되었다. 모든 작업을 한꺼번에 처리해야 하고 실행 중간에 사용자가 데이터를 입력하거나 수정이 불가하다. 여기에 사용되는 운영체제의 방식이 일괄 처리 시스템 방식이다.

 

3. 대화형 시스템(1960)

키보드와 모니터가 등장하였다. 작업 중간에 사용자가 입력이 가능해서 중간 결과값도 출력 가능하다. 이것을 대화형 시스템이라고 한다. CPU집중작업(CPU bound job)은 프로그램 실행 도중 입출력이 불가능하다. (일괄 처리 시스템의 특징이다) 대화형 시스템은 입출력 집중 작업으로 I/O bound job이라고 한다.

 

4. 시분할 시스템(1960년대 후반)

다중 프로그래밍 기술이 개발되었다. 하나의 CPU로 여러 작업을 동시에 실행한다. 따라서 효율성이 올라간다. 다중 프로그래밍 기술은 CPU 사용시간을 잘게 쪼개어 여러 작업에 나누어주어 동시에 진행되는 것처럼 보이게 한다. 오늘날 대부분의 컴퓨터도 시분할 시스템을 사용한다. 그렇지만 시분할 시스템을 쓰지 못하는 경우도 있다. 이런 것들은 예외적으로 일정시간 안에 끝나지 않으면 심각한 문제가 생기는 것들이다. 원자력이나 미사일같은것들을 조절하는 프로그램은 실시간 시스템 방식을 사용한다.

*유닉스(C언어로 개발됨) : 멀티 프로그래밍과 다중 사용자를 지원하는 운영체제로 개발되었다.

 

5. 분산 시스템(1970년대 후반)

 이즈음 스티브 잡스가 애플II를 만들면서 개인용 컴퓨터 시대를 열었다. 개인용 컴퓨터의 운영체제는 대부분 애플의 매킨토시나 마이크로소프트의 MS-DOS를 사용하였다. 

애플 II

 이때 인터넷도 등장한다. 서로 호환되지 않는 LAN을 하나로 묶어 아르파넷ARPnet이 생성되고 컴퓨터 간의 네트워킹을 위한 TCP/IP 프로토콜도 정의된다. 아르파넷은 냉전시기에 개발되었는데 이후 대학교에서 학문 연구를 이유로 서로 컴퓨터를 연결하려는 시도가 늘어나자 아르파넷을 민간에 풀어버린다. 이것이 이후에 인터넷으로 진화했다.

 개인용 컴퓨터가 보급되면서 값이 싸고 크기가 작은 컴퓨터들을 하나로 묶어 시스템을 생성하였다. 이것을 분산 시스템 distributed system 이라고 한다.

 

 

 

 

 

6. 클라이언트/서버 시스템(1990~현재)

분산 시스템의 문제는 모든 컴퓨터가 동일한 지위를 갖는 것이였다. 클라이언트/서버 시스템은 웹 시스템 보급 후 알려졌는데 서버에 데이터를 요청하면 클라이언트가 정보를 받는 역할을 하는 것이다. 클라이언트/서버는 상대적인 개념으로 누구든지 서버와 클라이언트가 될 수있다. 

 

데몬

클라이언트/서버 시스템에서 서버는 멈추지 않고 계속 작동해야 한다. 이렇게 멈추지 않고 계속 작동하는 프로그램을 데몬demon이라고 한다. 웹 데몬(웹 서버) 역할을 하는 프로그램은 아파티 톰캣, IIS 등이 있다.

 

7. P2P 시스템

P2P 시스템은 클라이언트/서버 시스템에서 불편한 점을 해결하기 위해 시작되었다. 클라이언트/서버 시스템에서 너무 많은 클라이언트가 서버에 요청을 하면 서버가 과부화 수도 있다. 보통 게임을 많이 하는 사람들은 경험해본적이 있을것이다. (서버가 터졌다니, 서버가 과부화되었다니,,그런 말들)

P2P 시스템(peer to peer)은 서버를 거치지 않고 사용자와 사용자를 연결해 준다. 각각 서버와 클라이언트가 되는 것이다. 메인 서버의 부하가 적다는 장점이 있다. P2P시스템의 예는 메신저 프로그램이나 MP3, 파일 공유 같은 P2P 시스템이 있다. 또한 비트코인, 블록체인 같은 것은 거래 장부를 분산시켜서 안정성을 확보한다.

(P2P 시스템도 서버가 있을 수는 있다. 예를 들면, P2P 메신저 프로그램같은 것은 사용자 인증, 출석과 같은 기능은 서버에서 한다.)

 

파일 공유

 만약 기존에 MP3 P2P에서 전송하는 쪽에서 프로그램을 종료해버리면 어떻게 될까? 전송 중단이 되어 P2P 시스템에 치명적인 오류가 발생한다. 그래서 해결한 방안이 파일 공유이다. 파일 공유는 같은 파일을 가진 여러 사람으로부터 데이터를 나누어 받아서 속도를 향상할 수 있다.

 

8. 기타 컴퓨터 환경

1) 그리드 컴퓨팅 : 컴퓨팅 자원을 구매하여 사용하는 컴퓨팅 환경이다. 서로 다른 기종의 컴퓨터들을 하나로 묶어 가상의 대용량 고성능 컴퓨터를 구성하여 고도의 연산 작업 혹은 대용량 처리를 수행하는 것을 일컫는다. 보통 연구용이나 상업용으로 이용한다. 일반인은 웹 하드 서비스에서 경험을 할 수 있다.

ex) 웹 하드 서비스 : 웹 하드에서 파일을 내려받을려고 하면 서버에서 주는 것이 아니라 파일을 내려받은 컴퓨터로부터 몰래 데이터를 빼와서 전달한다. 이것을 '그리드 딜리버리' 라고 한다.

(그래서 파일노리, 큐다운 같은 웹하드를 다운받으면 컴퓨터 속도가 느려지는 것이다. 무료로 웹하드 이용권을 나누어주는 것도 서비스 가입자 몰래 그리드 딜리버리를 깔아서 서버의 부하를 줄이기 위함이다.)

 

SaaS

 그리드 컴퓨팅의 소프트웨어적 버전. 소프트웨어를 영구 구매할 필요 없어 사용자의 초기 비용이 줄어든다. 단, 서비스 성능이 인터넷 연결 속도에 좌우되므로 사용자가 빠른 네트워크 하드웨어에 투자해야 된다.

(ex: Google Docs, Microsoft Office 365, IDE)

 

2) 클라우드 컴퓨팅 : 언제 어디서나 응용 프로그램과 데이터를 자유롭게 사용하는 환경이다. 그리드 컴퓨팅+SaaS라고 생각하면 된다.

클라우드 컴퓨팅이라는 이름은 시스템(하드웨어 포함)이 구름에 가려진 것처럼 사용자에게 보이지 않아서 붙여진 것이다. 

 

ex) 회사 홈페이지를 제작하고 싶으면 클라우드 서버에 회사 홈페이지를 올려놓고 사용한 만큼 비용을 지불하는 식이다. 그렇지 않으면 직접 서버 관리와 많은 초기 자원이 필요한데 클라우드 컴퓨팅을 통해 절감할 수 있다. 

 

ex) 또 휴대전화를 바꿀때 클라우드 백업을 통해서 직접 옮기지 않고도 손쉽게 전화번호부, 사진 등을 전달할 수 있게 된다. 

 

3) 사물 인터넷 IOT : 사물에 인터넷이 연결된 것.

728x90
반응형

'CS > 운영체제' 카테고리의 다른 글

병렬처리  (0) 2020.10.26
인터럽트  (0) 2020.10.26
[운영체제] 컴퓨터 기본 구성  (0) 2020.10.13
[운영체제] 운영체제의 구조  (0) 2020.10.11
[운영체제] 운영체제란 무엇일까...  (0) 2020.10.07
728x90
반응형

운영체제의 소개

Operating System, 즉 OS 라고 일컫는 운영체제는 우리가 컴퓨터를 키면 제일 먼저 만나는 소프트웨어라고 할 수 있다.

운영체제는 소프트웨어인데 우리가 흔히 생각하는 소프트웨어와는 다르게 하드웨어와 밀접한 소프트웨어이다.

모든 컴퓨팅 시스템에서 OS는 필수적이다.

 

하드웨어 위에 OS를 설치한다면 하나의 컴퓨터 시스템이 된다. 사용자는 직접 하드웨어에 접근하지 않고 OS와 상호작용하여 컴퓨터를 사용하게 된다. 

 

예를 들면, 개인용 컴퓨터에서 쓰는 윈도우나 MacOS는 흔히 들어봐서 익숙할 것이다. 이것말고도 대형 컴퓨터에서 사용하는

유닉스 Unix, 리눅스 Linux 등 다양하게 존재한다.

또한 우리가 이용하는 스마트폰도 운영체제를 이용하는데 이를 '모바일 운영체제' 라고 한다. 애플의 iOS, 구글의 안드로이드 같은 것이 여기에 해당한다. 

뿐만아니라 MP3, 내비게이션, PMP 모두 운영체제가 사용된다. 이것들은 '임베디드 시스템' 이라고 불린다. 임베디드 시스템이 있으면 기계는 기능을 계속 향상할 수 있다. 

 

 운영체제의 필요성

그렇다면 이러한 운영체제가 필요한 이유가 무엇일까?

컴퓨터의 환경이 날이 갈수록 복잡하게 조성이되고 있다. 여기에는 그에 따른 규칙이 필요하다. 운영체제는 컴퓨터가 필요한 규칙들을 시행시켜주는 것이라고 생각하면 될 것 같다. 그렇다면 여기서 질문이 생길 수 있다.

   

"운영체제가 없으면 기계는 아예 작동을 안할까?"

->노노.. 그럼 기계가 생겼을때부터 운영체제도 같이 만들었다는 말인데 이건 아니다. 최초의 컴퓨터 애니악은 운영체제 없이도 작동하였다. (참고로 컴퓨터라 불리는 것은 프로그래밍이 가능해진 시점부터이다.)

 

"운영체제의 유무에 따른 차이는 어떤 것이 있는가?"

-> 운영체제가 없다면 그 기계를 설계한 대로 밖에 사용할 수 없게 된다. MP3 노래 다운도 어렵다. 운영체제가 있다면 기능을 추가하고 성능을 변경할 수 있다.

 

"OS는 성능 향상에만 필요한가?"

-> OS는 성능 향상 뿐만 아니라 자원관리(resource management)라는 중요한 역할을 하기도 한다. (사실 제일 중요한 기능이다. )

많은 응용프로그램(채팅, 웹브라우저, 멜론 등)은 하나밖에 없는 하드웨어(마우스, 키보드)를 독점하고 싶어한다. 또 다른 응용프로그램을 파괴하고 싶어할 수도 있다. 이럴때 운영체제는 이러한 자원을 관리하는 중재자가 되어준다.

 

"자원관리는 그럼 어떻게 하는 것인가?"

-> 컴퓨터 자원 Computer resource 에는 CPU, 메모리, 디스크, 입출력장치, 하드디스크, 마우스, 사운드 카드, 그래픽 카드, 네트워크 카드, 터치 패드 같은 것들을 이야기한다.

 

운영체제는 사용자가 직접 자원에 접근하는 것을 막음으로써 자원을 보호하고 관리한다. 예를 들어 사용자가 하드디스크에 데이터를 저장하려는 경우 특정 위치에 저장이 불가하다. 우리가 저장을 할 때에도 어디에 저장되는지 모른다.(로컬디스크 C 같은 것은 하드 디스크의 첫번째 파티션을 의미할 뿐이다.) 알아서 적당한 위치에 저장이 되는 것이다. 그렇게 하지 않으면 악의적인 사용자가 데이터를 지우거나 덮어쓰는 일이 발생할수도 있기 때문이다. 

 

"사용자는 숨어 있는 자원을 그럼 어떻게 이용하는가?"

-> 적당한 장소에 저장하고 운영체제가 제공하는 사용자 인터페이스와 하드웨어 인터페이스를 이용하여 자원에 접근한다. 그걸 통해서 데이터를 꺼내거나 수정이 가능하다. 사용자가 컴퓨터를 사용할 수 있게 해주고 결과를 알려주는 것을 인터페이스interface라고 한다.

 

[펌웨어]

운영체제는 컴퓨터 하드웨어를 조정하고 관리하기 위해 존재한다. 그런데 운영체제는 하드웨어를 조절하고 관리하는 역할을 하므로 하드웨어 도움없이는 작동이 어렵다. 그래서 OS를 소프트웨어와 하드웨어의 결합 형태인 펌웨어 라고 부르기도 한다.

 

[인터페이스]

인터페이스는 자동차 핸들 및 계기판과 같다. 자동차는 엔진과 바퀴를 움직이지만 운전자는 핸들로 조종하고 계기판을 통해 주행 상황을 인지한다. 인터페이스는 현재 컴퓨터의 상황을 쉽게 인지하게 도와주는 것이다.

 

운영체제의 역할

 1. 자원관리 : 자원을 응용프로그램에게 나누어주고 배분, 또 사용이 끝나면 회수도 한다.

 2. 자원보호 : 응용프로그램을 조절하여 자원을 보호해준다.

 3. 하드웨어 인터페이스 제공 : 다양한 하드웨어를 일관된 방법으로 사용할 수 있도록 지원한다. 예를 들어 애플 마우스를 샀는데 삼성 컴퓨터에서는 사용할 수 없는 하드웨어가 되어 버리면 골치아파진다. 운영체제는 드라이버만 컴퓨터에 설치하면 다양한 부품을 편리하게 사용할 수 있도록 도와준다.

 4. 사용자 인터페이스 제공 : GUI(그래픽 사용자 인터페이스)제공해준다.

 

백신 유틸리티

 유틸리티란 컴퓨터 소프트웨어의 하나로, 컴퓨터의 동작에 필수적이지는 않지만 컴퓨터를 이용하는 주 목적에 대한 부차적인 일부 특정 작업을 수행하는 소프트웨어이다. 바이러스 검사, 디스크 조각 모음, 압축 프로그램 같이 운영체제의 작업을 보조하는 소프트웨어를 백신 유틸리티라고 한다. 

 

플러그 앤 플레이

 하드웨어의 종류와 관계없이 꽂으면 바로 실행되는 것을 의미한다.

 

 

 

 

참고한 도서

http://www.yes24.com/Product/Goods/62054527

 

쉽게 배우는 운영체제

명쾌한 비유와 사례로 쉽게 배우는운영체제의 구조와 원리컴퓨터 관련 학과 학생을 대상으로 운영체제의 구조와 원리를 설명한 책이다. 주요 개념은 일상생활의 사례를 통해 쉽게 이해하고, 개

www.yes24.com

 

728x90
반응형

'CS > 운영체제' 카테고리의 다른 글

병렬처리  (0) 2020.10.26
인터럽트  (0) 2020.10.26
[운영체제] 컴퓨터 기본 구성  (0) 2020.10.13
[운영체제] 운영체제의 구조  (0) 2020.10.11
[운영체제] 운영체제의 역사  (0) 2020.10.09
728x90
반응형

 그리디Greedy 알고리즘은 단순하고 강력한 알고리즘이다.('탐욕법'이라고 불린다.) 그리디 알고리즘은 '매 선택에서 지금 이 순간 당장 최적인 답을 선택하여 적합한 결과를 도출하는 알고리즘'을 의미한다. 미래를 생각하지 않고 각 단계에서 최선의 선택을 하는 것이다. 

 

 하지만 모든 경우에 그리디 알고리즘이 적용되지는 않는다. 그 순간에는 최선일지 몰라도 종합적으로는 그 방법이 최선이 아닐 경우가 있기 때문에 그리디 알고리즘을 모든 경우에 사용가능한 것은 아니다.

 

그리디 알고리즘을 설명한다면 마시멜로 실험에 비유할 수 있다. 마시멜로 실험은 지금 선택하면 1개의 마시멜로를 받고, 1분 기다렸다 선택하면 2개의 마시멜로를 받는 실험이다. 그리디 알고리즘을 사용하면 항상 마시멜로를 1개밖에 받지 못한다. 지금 당장 최선의 선택은 마시멜로 1개를 받는 거지만, 결과적으로는 1분 기다렸다가 2개 받는 게 최선이기 때문이다.  하지만 그리디 알고리즘은 최적해를 보장해주지 못한다.

 

 그리디 알고리즘은 유형이 다양해서 단순 암기를 통해서 잘 풀 수는 없다.(물론 다익스트라 알고리즘과 같은 유형은 암기가 필요하다)그리디 알고리즘은 미리 외우고 있지 않아도 풀 수 있는 가능성이 있다. 또한 기준에 따라 좋은 것을 선택하는 알고리즘이므로 문제에 기준이 제시될 가능성이 높다. '가장 큰 순서대로' '가장 작은 순서대로' 와 같은 기준을 알게 모르게 제시해 준다.


그리디(Greedy) 알고리즘의 대표적인 예시로서는 거스름돈 문제가 있다.

 

[백준] 거스름돈

https://www.acmicpc.net/problem/5585

 

5585번: 거스름돈

타로는 자주 JOI잡화점에서 물건을 산다. JOI잡화점에는 잔돈으로 500엔, 100엔, 50엔, 10엔, 5엔, 1엔이 충분히 있고, 언제나 거스름돈 개수가 가장 적게 잔돈을 준다. 타로가 JOI잡화점에서 물건을 사�

www.acmicpc.net

문제

타로는 자주 JOI잡화점에서 물건을 산다. JOI잡화점에는 잔돈으로 500엔, 100엔, 50엔, 10엔, 5엔, 1엔이 충분히 있고, 언제나 거스름돈 개수가 가장 적게 잔돈을 준다. 타로가 JOI잡화점에서 물건을 사고 카운터에서 1000엔 지폐를 한장 냈을 때, 받을 잔돈에 포함된 잔돈의 개수를 구하는 프로그램을 작성하시오.

예를 들어 입력된 예1의 경우에는 아래 그림에서 처럼 4개를 출력해야 한다.

입력

입력은 한줄로 이루어져있고, 타로가 지불할 돈(1 이상 1000미만의 정수) 1개가 쓰여져있다.

출력

제출할 출력 파일은 1행으로만 되어 있다. 잔돈에 포함된 매수를 출력하시오.

예제 입력 1

380

예제 출력 1

4

출처

Olympiad > 일본정보올림피아드 > 일본정보올림피아드 예선 > JOI 2008 예선 1번

 

[풀이 과정]

문제에서 '거스름돈 개수가 가장 적게 잔돈을 준다' 라는 조건이 제시되어있다. 입력에서는 620엔을 받아야된다. 620엔에서 일단 제일 큰 단위의 거스름돈부터 차례대로 줄 수 있으면 주는 해결방안을 찾을 수 있다. 

#include <iostream>
using namespace std;

int main()
{
    int input;						//입력값
    cin>>input;							
    int temp = 1000-input;				//동전으로 계산해주어야될 값
    int coin[6] = {500,100,50,10,5,1};  		//동전의 종류
    int result =0;					//거스름돈의 개수
    for(int i=0;i<6;i++)
    {
        result += temp/coin[i];
        temp %= coin[i];
    }
    cout<<result;
}

result에는 temp를 coin[i]로 나눈 몫을 저장해서 더해주고 temp는 coin[i]로 나눈 나머지를 저장해준다. 

 

 그리디 알고리즘은 input값이 얼마인지는 시간복잡도에 영향을 주지 않는다. 위의 코드를 보았을때 시간복잡도에 영향을 주는 것은 코인의 개수이다. 

 이처럼 그리디 알고리즘은 그 순간의 최적의 해를 고려해준다. 그러나 여기서는 조건에서 coin들이 서로의 배수관계라서 그리디 알고리즘이 적용되었다. 만약 500,100,50,10,5,1 이러한 조합이 아닌 40이나 30같은 서로의 배수관계가 아닌 조합이 있다면 그리디 알고리즘을 적용할 수 없다. 그럴 경우는 예외를 다시 고려해주어야 된다. 최소한의 아이디어를 떠올리고 이것이 정당한지 검토할 수 있어야 답을 도출할 수 있다.

 

728x90
반응형

'알고리즘 > 알고리즘 이론' 카테고리의 다른 글

정렬(Sort)  (0) 2021.04.12
재귀 함수  (0) 2021.04.02
브루트 포스 (Brute Force)  (0) 2021.03.18
다이나믹 프로그래밍  (0) 2021.03.08
알고리즘 평가  (0) 2021.03.05
728x90
반응형

https://programmers.co.kr/learn/courses/30/lessons/12926

 

코딩테스트 연습 - 시저 암호

어떤 문장의 각 알파벳을 일정한 거리만큼 밀어서 다른 알파벳으로 바꾸는 암호화 방식을 시저 암호라고 합니다. 예를 들어 AB는 1만큼 밀면 BC가 되고, 3만큼 밀면 DE가 됩니다. z는 1만큼 밀면 a가

programmers.co.kr

[문제 설명]

 

어떤 문장의 각 알파벳을 일정한 거리만큼 밀어서 다른 알파벳으로 바꾸는 암호화 방식을 시저 암호라고 합니다. 예를 들어 AB는 1만큼 밀면 BC가 되고, 3만큼 밀면 DE가 됩니다. z는 1만큼 밀면 a가 됩니다. 문자열 s와 거리 n을 입력받아 s를 n만큼 민 암호문을 만드는 함수, solution을 완성해 보세요.

 

[제한 조건]

  • 공백은 아무리 밀어도 공백입니다.
  • s는 알파벳 소문자, 대문자, 공백으로만 이루어져 있습니다.
  • s의 길이는 8000이하입니다.
  • n은 1 이상, 25이하인 자연수입니다.

입출력 예

s n result
"AB" 1 "BC"
"z" 1 "a"
"a B z" 4 "e F d"

 

[풀이 과정] : [풀이 1] -> [풀이 2]

 

[풀이 1] : 테스트 6,7,8,10,12 오류

 

먼저 string s에서 하나씩 체크해 준다. 만약 s에서 빈칸이면 빈칸을 반환해 준다. 빈칸이 아닐 경우, string 타입을 int로 바꾸어서 n을 더해준다. 

 

#include <string>
#include <vector>

using namespace std;

string solution(string s, int n) {
    string answer;
    for(int i=0;i<s.length();i++){
        if(s[i]==' ') answer+=' ';
        else{
            int temp=s[i]-0;
            temp+=n;
            if(temp>90&&temp<97) {temp= temp-26;}
            else if(temp>122) {temp = temp-26;}
            char ch = temp;
            answer += ch;
        }
    }
    return answer;
}

1. string을 int로 바꾸는 방법은 여러 가지가 있지만, 여기서는 s[i]-0을 int 변수에 대입했다.

2. temp변수에 n을 더하고 n을 더한 temp 값이 알파벳 소문자인 a~z 와 대문자인 A~Z를 초과했을 경우 다시 돌아갈 수 있게 차이인 26을 빼서 temp 값에 다시 저장하였다. (26은 아스키 값에서 z-a+1이다.)

3. temp값을 char형의 ch변수에 저장하고 answer에 ch를 저장하였다.

 

//아스키 코드 표 참고//

 

[풀이 2] 

[풀이 1]은 n의 개수가 너무 커져서 알파벳 소문자들과 대문자들의 차이를 정확히 구별하기 힘들다고 생각했다.

예를 들면 input 값이 z일때 n이 20이면 아스키 코드 값은 110이 된다. 그러나 소문자로 변환해 주어야 되는데 해당하는 조건이 없으므로 output값이 오류가 날 수 있을 것이라고 생각했다. 그래서 [풀이 2]는 이러한 케이스 들을 다시 처리해주었다.

<내가 직접 더해준 케이스들>

s n result
"AB" 1 "BC"
"z" 1 "a"
"Z" 10 "J"
"a B z" 4 "e F d"
"aBZ" 20 "uVT"

s[i]를 int로 바꾸어서 저장한 temp값을 소문자 대문자로 조건을 먼저 분류한 뒤, [풀이 1]의 방법을 사용하였다.

#include <string>
#include <vector>

using namespace std;

string solution(string s, int n) {
    string answer = "";
    for(int i=0;i<s.length();i++){
        if(s[i]==' ') answer+=' ';
        else{
            int temp = s[i]-0;
            if(temp>=65&&temp<=90){
                temp+=n;
                if(temp>90) temp-=26;
            }
            else if(temp>=97&&temp<=122){
                temp+=n;
                if(temp>122) temp-=26;
            }
            char ch = temp;
            answer+=ch;
        }
    }
    return answer;
}

 

728x90
반응형
728x90
반응형

https://programmers.co.kr/learn/courses/30/lessons/12921

 

코딩테스트 연습 - 소수 찾기

1부터 입력받은 숫자 n 사이에 있는 소수의 개수를 반환하는 함수, solution을 만들어 보세요. 소수는 1과 자기 자신으로만 나누어지는 수를 의미합니다. (1은 소수가 아닙니다.) 제한 조건 n은 2이상

programmers.co.kr

[문제 설명]

1부터 입력받은 숫자 n 사이에 있는 소수의 개수를 반환하는 함수, solution을 만들어 보세요.

소수는 1과 자기 자신으로만 나누어지는 수를 의미합니다.
(1은 소수가 아닙니다.)

[제한 조건]

  • n은 2이상 1000000이하의 자연수입니다.

입출력 예

n result
10 4
5 3

입출력 예 설명

입출력 예 #1
1부터 10 사이의 소수는 [2,3,5,7] 4개가 존재하므로 4를 반환

입출력 예 #2
1부터 5 사이의 소수는 [2,3,5] 3개가 존재하므로 3를 반환

 

[문제 개요]

 소수는 자신과 1이외의 정수로 나누어떨어지지 않는 정수이다. 예를 들어 소수인 13은 2,3,...,12 가운데 어떤 정수로도 나누어 떨어지지 않는다. 그러므로 어떤 정수 n에 대하여 아래의 조건을 만족하면 소수임을 알 수 있다. 

2부터 n-1까지의 어떤 정수로도 나누어떨어지지 않는다.

만약 나누어떨어지는 정수가 하나 이상 존재하면 그 수는 합성수(composite number)이다. 

 

[풀이 과정]

알고리즘 개선 과정 : [풀이 1] -> [풀이 2] -> [풀이 3],[풀이 4]

 

[풀이 1]

첫번째 풀이는 제일 처음에 풀었던 방식인데 테스트 12부터 시간 초과가 나고 효율성에서 0점을 받았다.. 

 

최대공약수 함수 gcd를 먼저 만들어 놓는다. 판별하고 싶은 수(n) 전까지 모두 gcd에서 return 1이 나오면 된다고 생각해서 풀었던 방식이다. temp 변수를 0으로 초기화 해두어서 gcd가 1이 나올경우 temp++해준다. 만약 temp가 n-1이 된다면 이 수를 '소수' 라고 판단해주는것이다. (할때부터 시간 초과가 날 것 같았고, 그냥 머릿속에 떠오른 대로 푼 방식이다)

여기서 시간 복잡도는 O(N) 이다. 

#include <string>
#include <vector>

using namespace std;
int gcd(int a, int b){
    int c;
    while(b!=0){
        c=a%b;
        a=b;
        b=c;
    }
    return a;
}
int solution(int n) {
    int answer = 0;
    for(int i=2;i<=n;i++){
        int temp=0;
        for(int j=1;j<i;j++){
            if(gcd(i,j)==1){
                temp++;
            }else break;
        }
        if(temp==(i-1)) answer++;
    }
    return answer;
}

비슷한 경우 : 1000이하의 소수를 나열하는 프로그램.

#include <iostream>
using namespaces std;

int main(void){
    for(int n=2;n<=1000;n++){
    	for(int i=2;i<n;i++){
        	if(n%i==0)//나누어떨어지면 소수가 아니다
            	break;//더 이상의 반복은 불필요
        }
        if(n==i)
        	cout<<n<<endl;
    }
    return 0;
}

for문의 반복이 종료된 시점에서 변수의 값은 아래와 같다.


n이 합성수(소수가 아닌) 경우 : n보다 작은 값 (for문이 중단됨)

[풀이 2] : 테스트 15까지 통과했지만 효율성 0점..

위의 상자에서 힌트를 얻었다. n이 2또는 3으로 나누어 떨어지지 않으면 2X2인 4 또는 2X3인 6으로도 나누어떨어지지 않는다. 그러므로 이 프로그램은 불필요한 나눗셈을 실행하고 있음을 알 수 있다. 즉, 정수 n이 소수인지의 여부는 아래 조건을 만족하는지 조사하면 된다.

2부터 n-1까지의 어떤 소수로도 나누어떨어지지 않는다.

예를 들어, 7이 소수인지는 7보다 작은 소수 2,3,5로 나눗셈을 실행하면 충분하다. 이 아이디어를 이용하여 시간을 줄여보겠다.

#include <string>
#include <vector>

using namespace std;

int solution(int n) {
    int answer = 1;
    vector<int> prime;// 소수를 저장하는 배열
    prime.push_back(2);// 2는 소수 -> answer=1에서 시작
    for(int i=3;i<=n;i+=2){// 홀수만을 대상으로 한다
        int j;
        for(j=0;j<prime.size();j++){// 이미 구한 소수 배열의 크기까지 반복문
            if(i%prime[j]==0)// 나누어 떨어지면 소수가 아니다
                break;
        }
        if(j==prime.size()) {// 마지막까지 나누어떨어지지 않으면
            prime.push_back(i);// 배열에 저장한다.
            answer++;// 소수 개수는 증가시켜줌
        }
    }
    return answer;
}

소수를 구하는 과정에서 그때까지 구한 소수를 배열 prime의 요소로 저장한다. 이 때, n이 소수인지를 판단할 때 쌓아 놓은 소수로 나눗셈을 한다. 먼저 2는 소수이므로 prime에 저장한다. 3이상의 소수는 이중 for문으로 구한다. 바깥쪽 for문은 n의 값을 2씩 증가하여(짝수는 2의 배수이므로 해줄 필요 x) 3,5,7,9...,999로 홀수 값만을 생성한다.

 

[풀이 3] : 효율성까지 통과

100을 두 숫자의 곱셈으로 나타내면 어떻게 될까 -> ① 2x50 ② 4X25 ③ 5X20 ④ 10X10 ⑤ 20X5 ⑥ 25X4 ⑦ 50X2

여기서 ① ~ ④까지의 나눗셈만 수행하면 된다.이것은 넓이가 100인 직사각형의 가로,세로 길이와 같다.

예를 들어, 2X50과 50X2 는 가로, 세로가 다르지만 같은 직사각형으로 볼 수 있다. 100은 10X10을 중심으로 대칭 형태를 이루고 있다. 이는 소수임을 판단할때, 정사각형의 한 변의 길이까지만 소수로 나눗셈을 시도하면 된다는 뜻이다. ( 나머지는 대칭이라고 보면 된다) 이 과정에서 한 번도 나누어떨어지지 않으면 소수라고 판단할 수 있게 되는 것이다. 이러한 성질을 이용하여 제곱근을 한 변으로 하는 이후의 직사각형에 대한 계산량을 줄이는 것이 핵심이다. 

 즉, 어떤 정수 n은 다음 조건을 만족하면 소수라고 판단할 수 있다.

n이 소수일 조건은 n의 제곱근 이하의 어떤 소수로도 나누어떨어지지 않는다.

[풀이2]에서 prime[i]를 n의 제곱근 이하의 수들만 판단하면 된다는 것이다.

 예를 들어 첫 번째 경우에서 10이하의 소수를 구할려면 10의 제곱근은 3.xxx 이므로 3이하의 소수로 나누어 떨어지는지 판단하면 된다. 

#include <string>
#include <vector>

using namespace std;

int solution(int n) {
    vector<int> prime;// 소수를 저장하는 배열
    prime.push_back(2);// 2는 소수
    for(int i=3;i<=n;i+=2){// 홀수만을 대상으로 한다
        bool flag=true;//나누어 떨어지는지 확인할 변수
        for(int j=0;prime[j]*prime[j]<=i;j++){// prime[j]의 제곱이 i이하인가 판단
            if(i%prime[j]==0){// 나누어 떨어지면 소수가 아니다
                flag=false;
                break;
            }
        }
        if(flag) {// 마지막까지 나누어떨어지지 않으면
            prime.push_back(i);// 배열에 저장한다.
        }
    }
    return prime.size();
}

 

[풀이 4]

4번째 풀이는 알고리즘에 '에라토스테네스의 체' 라는 효율적인 방법으로 풀었다. 에라토스테네스의 체는 2부터 소수를 구하고자 하는 구간의 모든 수를 나열하여, 자기 자신을 제외한 배수들을 모두 지운다. 예를 들어 2부터 시작할 때, 2는 자기 자신을 제외한 2의 배수를 모두 지운다. 그 다음, 남아있는 수 가운데 3은 소수이므로 자기 자신을 제외한 3의 배수를 모두 지운다. 앞서 [풀이 3]에서 제곱근 만큼만 하면 나머지는 대칭이므로 위의 과정을 구하려는 n의 제곱근 이하의 자연수만큼만 반복하면 구하는 구간의 모든 소수가 남는다. 

#include <string>
#include <vector>
using namespace std;

int solution(int n)
{
    bool flag[100000];
    int answer = 0;
    for(int i=2;i<= n;i++){
    	flag[i] = true;
    }
    
	for(int i=2;i*i<=n;i++)
	{
		if (flag[i]){
        	for (int j=i*i;j<=n;j+=i){
            	flag[j] = false;
            }
        }
    }
    for(int i=2;i<=n;i++)
    {
    	if(flag[i]) answer++;
    }
    return answer;
}

 

728x90
반응형

+ Recent posts