✅ 학습 키워드

1. Promise, resolve, reject
2. state: pending, fulfilled, rejected
2. producer, consumer
3. then, catch, finally
4. Asynchronous, Await
5. Promise API - Promise.all, Promise.race

Promise란?

Promise는 객체다. 객체란 키-값 쌍으로 이루어진 데이터 저장 공간을 의미한다.

그러나 Promise는 특별한 목적을 위해 설계된 객체다. 그 특별한 목적은 '비동기 작업'이다.

 

Promise는 비동기 작업이 끝나면 등록한 콜백 함수를 실행시켜 줄 것을 '약속'한다.

 

그리고 그 콜백 함수를 통해 생성된 데이터는 Promise 가 제공하는 특별한 메서드(.then(), .catch(), .finally())를 통해 소비할 수 있다.

 

Promise는 상태를 가진다.

Promise는 상태(state)를 가진다. 작업을 진행 중인지 (pending) 작업을 성공했는지 (fulfilled) 실패했는지 (rejected)를 나타낸다. ( 이는 객체 내부의 캡슐화된 값이며 속성이 아니다.)

 

Promise 객체 생성과 resolve, reject 함수

Promise는 생성자 함수인 new를 통해 비동기 작업의 로직을 초기화한다. 프로미스 생성자는 콜백 함수를 인자로 받고, 이 콜백 함수는 resolve와 reject 라는 콜백 함수를 인자로 받는다.

이때 Promise를 생성할 때 넣는 콜백 함수는 Producer 역할을 하며, 해당 비동기 작업이 끝난 뒤 사용하는 then, catch, finally 메소드는 Consumer 역할을 한다.

Producer과 Consumer의 차이

Producer : 데이터 제공
Consumer : 데이터 소비

 

 

비동기 작업의 성공과 실패, 그리고 해당 작업을 통해 만들어진 데이터를 어떻게 소비하는지 조금 더 자세히 살펴보자.

 

생성자 함수의 콜백 함수로 수행할 비동기 작업이 성공적으로 완료되면 resolve 함수를 통해 값을 외부로 전달한다. 만약 실패한다면 reject 함수와 Error 객체를 통해 에러 값을 전달한다. 

 

❗️반대로 말하면 비동기 작업이 완료되는 시점의 정보를 외부로 전달하고 싶다면, Promise 객체를 사용하면 된다는 것!

 

또한 resolve 와 reject 를 사용해서 비동기 작업이 끝났음을 명시해주지 않으면 계속 pending 상태에 머무른다.

 

Producer에서 제공하는 데이터는 Counsumers 함수인 then(비동기 작업 성공 시, resolve를 통해)과 catch(비동기 작업 실패 시, reject를 통해)로 데이터를 전달받아 사용할 수 있다. finally 함수는 비동기 작업이 성공적으로 완료되든 그렇지 않든, 무조건 한 번 실행된다.

 

catch를 통한 에러 핸들링은 다음 블로그 포스팅을 참고하자 => 프로미스 에러 핸들링 보러 가기

 

Promise 에러 핸들링

 

yuni-dev.tistory.com

 

Async

Promise 체이닝은 콜백 지옥을 만들어서 코드를 복잡하게 만든다. 그래서 Promise를 쓰는 대신 함수 앞에 async 키워드를 붙여주면 자동으로 함수의 코드 블럭이 Promise로 바뀐다.

Await

await 키워드를 사용하면 자동으로 비동기 작업이 완료될때까지 기다린 후 결과를 return 한다.

✅ async await 과 promise then 차이

function delay(time) {
  return new Promise(resolve => setTimeout(resolve, time));
}

async function haveLunch() {
	await delay(3000);
    return '냠냠'
}

function haveLunch() {
	delay(3000)
    .then('냠냠');
}

 

Promise API - Promise.all, Promise.race

Promise.all : 두 개의 비동기 작업이 동시에 일어나도 상관 없는 경우 사용하는 메서드

Promise.race : 여러개의 비동기 작업 중 가장 먼저 완료되는 값만 반환

function examplePromiseAll() {
	return Promise.all(([비동기1(), 비동기2()]).then( 이후 수행 작업);
}

 


✅ 핵심 키워드

1. 프로퍼티, 객체
2. 객체지향, 캡슐화, 상속, 추상화, 다형성
3. 프로퍼티 어트리뷰트, 내부 슬롯, 내부 메서드

프로퍼티와 객체

객체는 일종의 서랍장이다. 그리고 프로퍼티는 서랍 안에 있는 데이터다.
프로퍼티는 점 표기법이나 대괄호 표기법이 있다. (. / [])

 

프로퍼티 어트리뷰트

프로퍼티 어트리뷰트를 이해하기 위해 먼저 내부 슬롯과 내부 메서드를 알아야 한다. 그리고 내부 슬롯과 내부 메서드를 이해하기 위해서는 객체 지향 개념을 이해해야 한다.

객체 지향 (OOP)

C++ 이나 Java에서 등장하는 객체 지향 개념은 클래스를 사용해 객체를 만들고, 이 객체가 하나의 모듈로써 동작하게 하는 것이다. 그렇게 함으로써 대규모 프로그램도 작은 부품을 조립하듯 효율적으로 프로그래밍 할 수 있게 되었다.

객체 지향의 특징으로 네 가지가 있다.

  1. 캡슐화
    객체 내부에서만 쓰이는 데이터를 구분하여, 외부에서 이에 접근하지 못하도록 캡슐화 함.
  2. 추상화
    객체에서 공통된 부분을 찾아 상속으로 객체를 재활용 하기 위해서, 상위 개념에 이름을 붙이는 것을 추상화라고 한다.
  3. 상속
    부모 클래스의 특징을 자식 클래스가 싱속받음으로써 불필요하게 작성해야 하는 코드를 줄일 수 있다.
  4. 다형성
    한 객체는 여러 객체로 구성될 수 있음을 의미함.

Prototype-based programing

자바스크립트는 정확히 말하면 객체 기반 언어이다. class를 사용하지 않고 함수형 프로그래밍에 객체 지향의 핵심 개념인 상속, 추상화, 다형성, 캡슐화를 구현했다.

내부 슬롯과 내부 메서드

내부 슬롯: 객체가 내부적으로 데이터를 저장하는 공간
내부 메서드: 객체가 내부적으로 동작하는 방식을 정의한 함수

ECMAScript 사양에서는 [[]] 이중 대괄호로 내부 슬롯과 내부 메서드를 정의한다. 이는 외부로 공개된 객체의 프로퍼티는 아니지만, 간접적으로 접근할 수 있는 수단이 있다. 이것은 마치 class 내부에서만 사용하는 변수와 메서드를 정의하는 것과 비슷하다.

그래서 프로퍼티 어트리뷰트란?

프로퍼티 어트리뷰트는 각 프로퍼티가 내부적으로 가지고 있는 속성 정보이다. 자바스크립트 엔진은 프로퍼티를 생성할 때 프로퍼티의 상태를 나타내는 프로퍼티 어트리뷰트를 기본값으로 자동 정의한다.

 

프로퍼티 상태란 1. 프로퍼티의 값 2. 값의 갱신 가능 여부 3. 열거 가능 여부 4. 재정의 가능 여부를 말한다. 그리고 이런 프로퍼티 상태 값은 외부에서 접근할 수 없는 자바스크립트 엔진이 관리하는 내부 슬롯이다. 해당 값에 접근하려면 Object.defineProperty() 같은 특정한 메서드를 사용해야 한다.

데이터 프로퍼티 (내부 슬롯)

[[Value]] : 프로퍼티 키를 통해 프로퍼티 값에 접근하면 반환되는 값
[[Writable]] : 프로퍼티 값의 변경 가능 여부를 나타내며 불리언 값을 가짐
[[Enumerable]] : 프로퍼티의 열거 가능 여부를 나타내며 불리언 값을 가짐
[[Configurable]] : 프로퍼티의 재정의 가능 여부를 나타내며 불리언 값을 가짐

 

만약 데이터 프로퍼티 writable 값이 false로 설정되어 있으면 프로퍼티 값을 변경할 수 없다.

const user = { name: "gildong" };

Object.defineProperty(user, "name", {
  writable: false,
  enumerable: false,
  configurable: false
});

user.name = 'uu';

console.log(user.name); // "gildong"
접근자 프로퍼티

[[Get]] : 접근자 프로퍼티를 통해 데이터 프로퍼티의 값을 읽을 때 호출되는 접근자 함수
[[Set]] : 접근자 프로퍼티를 통해 데이터 프로퍼티의 값을 저장할 때 호출되는 접근자 함수

const person = { name: "gildong" };
console.log(person.name); // 내부적으로 [[Get]]이 호출됨
person.name = "Minji";    // 내부적으로 [[Set]]이 호출됨


✅ 핵심 키워드

1. 연산자, 피연산자

2. 옵셔널 체이닝 연산자, null 병합 연산자, 삼항 조건 연산자

3. 산술 연산자, 문자열 연결 연산자, 할당 연산자, 삼항 연산자, 비교 연산자, 논리 연산자, 그룹 연산자, 쉼표 연산자, typeof 연산자, 지수 연산자

4. 암묵적 타입 변환/타입 강제 변환

연산자란?

연산자란 피연산자를 연산해 새로운 값을 만드는 역할을 한다. (피연산자는 값으로 평가되는 표현식이여야 한다.)

JS가 제공하는 다양한 연산자

1.  산술 연산자

  • 이항 산술 연산자
    항이 두 개를 갖는 연산자로, 피연산자의 값을 변경하는 부수효과가 없고 언제나 새로운 값을 만든다.
    + , -, *, /, % 연산자가 있다.
  • 단항 산술 연산자
    ++, --, +, - 연산자가 있다.
    이 중에서 증가/감소(++/--) 연산자는 피연산자의 값을 변경하는 부수 효과가 있다. 또한 위치에 의미가 있다.
  var x = 1;

  var result = x++; // 선할당 후증가
  console.log(x); // 2
  console.log(result); // 1

  var result = ++x; // 선증가 후할당
  console.log(x); // 3
  console.log(result); // 3

 

숫자 타입이 아닌 피연산자에 + 단항 연산자를 사용하면 피연산자를 숫자 타입으로 변환하여 반환한다.

var x = '1'

console.log(+x); // 1
console.log(x); // "1", 부수효과 없음

2.  문자열 연결 연산자

+ 연산자는 피연산자 중 하나 이상이 문자열인 경우 문자열 연결 연산자로 동작한다. (문자로 취급하고 연결하여 하나의 문자열을 반환)
이때 피연산자 중 하나 이상 문자라면, 자바스크립트 엔진은 나머지 피연산자의 타입을 암묵적으로 변환한다.

암묵적 타입 변환/타입 강제 변환

    1 + true // 2, true는 1로 타입 변환  
    1 + false // 1, false는 0으로 타입 변환  
    1 + null // 1, null은 0으로 타입 변환  
    1 + undefined // NaN, undefined는 숫자로 타입 변환되지 않음

3.  할당 연산자

=, +=, -=, *=, /=, %= 이 있고, 부수효과가 있다.
이러한 할당문은 값으로 평가되는 표현식인 문으로서 할당된 값으로 평가된다. 따라서 연쇄 할당이 가능하다.

4.  비교 연산자

  • 동등/일치 비교 연산자
    ==, ===, !=(부동등 비교), !==(불일치 비교)

동등 비교(==) 연산자는 좌항과 우항의 피연산자를 비교할 때 먼저 암묵적 타입 변환을 통해 타입을 일치시킨 후 같은 값인지 비교한다. 하지만 이러한 동등 비교 연산자는 예측하기 어려운 결과를 만들기 때문에 대신 일치 비교(===) 연산자를 사용한다.

 

일치 비교 연산자는 좌항과 우항의 피연산자가 타입도 같고 값도 같은 경우에 한하여 true를 반환한다.

 

여기서 주의할 것은 NaN이다. NaN은 자신과 일치하지 않는 유일한 값이다. 따라서 숫자가 NaN인지 조사하려면 빌트인 함수 Number.isNaN을 사용한다.

숫자 0도 주의해야 한다. 양의 0과 음의 0을 비교하면 true를 반환한다.

// 주의해야 할 점
NaN === NaN // false
Number.isNaN(NaN) // true
Object.is(NaN, NaN) // true

0 === -0 // true
Object.is(0, -0) // false
  • 대소 관계 비교 연산자
    대소 관계 비교 연산자는 피연산자의 크기를 비교하여 불리언 값을 반환한다. >, <, >=, <= 가 있다.

5.  삼항 조건 연산자

삼항 조건 연산자는 조건식의 평가 결과에 따라 반환할 값을 결정한다. 삼항 조건 연산자는 다음과 같이 사용한다. 또한 삼항 조건 연산자 표현식은 갓으로 평가할 수 있는 표현식인 문이다.

조건식 ? 조건식이 true일 때 반환할 값 : 조건식이 false일 때 반환할 값

6.  논리 연산자

||(논리합), &&(논리곱), !(부정) 가 있다.

// 드 모르간 법칙
!(x || y) === (!x && !y)
!(x && y)=== (!x || !y)

7.  쉼표 연산자

쉼표(,) 연산자는 왼쪽 피연산자부터 차례대로 피연산자를 평가하고 마지막 피연산자의 평가가 끝나면 마지막 피연산자의 평가 결과를 반환한다.

8.  그룹 연산자

소괄호로 피연산자를 감싸는 그룹 연산자는 자신의 피연산자인 표현식을 가장 먼저 평가한다. 이렇게 그룹 연산자를 사용해 연산자의 우선순위를 조절할 수 있다.

9.  typeof 연산자

typeof 연산자는 7가지 문자열 "string", "number", "boolean", "undefined", "symbol", "object", "function" 중 하나를 반환한다.

// 주의
typeof null // "object"

 

typeof 연산자로 null 값을 연산해 보면 "null" 이 아닌 "object"를 반환하는데, 이것은 버그지만 수정되지 않고 있다. 따라서 null 값을 확인할 때는 일치 연산자(===)를 사용하자.

10.  지수 연산자

/** 는 좌항의 피연산자를 밑으로, 우항의 피연산자를 지수로 거듭 제곱하여 숫자 값을 반환한다.

11.  옵셔널 체이닝 연산자 (?.)

?. 는 좌항의 피연산자가 null 또는 undefined 경우 undefined를 반환하고, 그렇지 않으면 우항의 프로퍼티 참조를 이어간다.
옵셔널 체이닝 연산자가 도입되기 이전에는 논리 연산자 &&를 사용한 단축 평가를 통해 null 또는 undefined인지 확인했다.

&& 과 ?. 차이점
&& : Falsy 값이면 좌항 피연산자를 반환함
?. : Falsy 값이어도 null 또는 undefined가 아니면 우항의 프로퍼티 참조를 이어감

 

❗️옵셔널 체이닝 연산자를 남용하지 말 것

?. 옵셔널 체이닝 연산자는 존재하지 않아도 되는 대상에만 사용해야 한다!!

이미지가 있으면 띄워주는 로직을 생각해보자

{ imgs?.map(img => console.log(img) }

만약 imgs가 반드시 존재해야 하는 값이라면, 이것은 에러이므로 에러를 터트려줘야 한다. 
반드시 존재해야 하는 변수를 옵셔널 체이닝 연산자로 연결하면, 해당 구문은 오류를 반환하지 않는다. 
이것은 디버깅을 어렵게 만든다.

12.  null 병합 연산자 (??)

null 병합 연산자 ??는 좌항의 피연산자가 null 또는 undefined인 경우 우항의 피연산자를 반환하고, 그렇지 않으면 좌항의 피연산자를 반환한다.

var foo = null ?? 'default string';
console.log(foo); // 'default string'

 

null 병합 연산자는 이렇게 변수에 기본값을 설정할 때 유용하다

가상 메모리

  • 가상메모리는 왜 등장했을까?
  • time sharing 때문에 등장함. 시분할을 통해 여러 프로그램이 함께 실행되기 시작하면서, 메모리를 보호해야 할 필요가 생김. 그래서 가상 메모리 개념이 등장함.

 

  • 가상 메모리는 왜 사용할까?
  • 가상 메모리를 사용하면 여러 프로그램이 한정된 물리 메모리를 공유함으로써 실제 물리 메모리보다 더 큰 메모리 공간을 사용할 수 있게 되었다. 이로 인해 메모리 효율성이 높아지고, 여러 프로그램을 동시에 실행할 수 있다.

 

  • 가상 메모리가 물리 메모리와 어떻게 연결되는가?
  • 페이지 테이블을 통해 연결된다. 가상 메모리는 페이지라는 고정된 크기의 블록으로 나뉘는데, 페이지 테이블이 이를 물리메모리의 페이지 프레임과 매핑한다. CPU에서 메모리를 접근하면, 페이지 테이블을 통해 물리 메모리로 변환하며, 만약 매핑된 물리 메모리가 부족하면 일부 페이지가 디스크로 이동하고 다시 불러오는 Swap in/out 방식으로 관리된다.

 

  • Page fault 란?
  • CPU가 특정 페이지에 접근하려고 하는데, 그 페이지가 물리 메모리에 존재하지 않을 때 발생하는 예외 상황이다. 이 경우 page fault handler를 호출하여 운영체제는 디스크에서 해당 페이지를 물리 메모리로 불러온다.

 

  • page replacement 란?
  • 물리 메모리에 빈 공간이 없을 때, 더 이상 필요하지 않거나 자주 사용되지 않는 페이지를 디스크에 스왑 아웃 하고 새 페이지를 스왑 인하는 과정이다. FIFO, LRU, LFU 등이 있다.

 

  • Thrashing이란?사용하는 프로세스가 많아질 때 어느 한계점 까지는 CPU이용율이 증가하다가 한계점 이상부터는 CPU 이용율이 떨어지게 되는데 이 때 사용량이 떨어지는 이유 중 하나가 스레싱 때문이다.
  • 디스크의 입출력이 많은 경우 성능 저하가 발생하는데, 스와핑이나 페이지 폴트가 많이 발생하면 디스크 속도에 의존하게 되면서 성능이 크게 저하된다.

'CS (Computer Science)' 카테고리의 다른 글

Tree  (0) 2024.08.05

JS 런타임 환경이 뭘까?

 

JS 실행 환경이란?

JS를 실행시키기 위한 전체적인 환경을 의미한다. JS는 그 자체로 독립적으로 동작하는 언어가 아니라, 실행될 환경에서 다양한 기능을 추가로 제공받는다. 이렇게 JS가 실행되는 곳을 호스트 환경이라고 한다. 그리고 그런 자바스크립트 호스트 환경에는 브라우저, node.js, Deno 등이 있다. 특히 가장 유명한 node js는 크롬의 v8엔진을 가져와서 만든 자바스크립트 런타임 환경이다. 모든 JS 실행 환경에는 JS를 해석하고 실행할 수 있는 JS 엔진을 내장하고 있다. 하지만 브라우저와 Node.js의 차이가 있다.

브라우저와 Node.js의 차이점

브라우저는 웹페이지를 화면에 렌더링 하는 것이 주된 목적이고, Node.js는 브라우저 외부에서 자바스크립트 실행 환경을 제공하는 것이 주된 목적이다. 따라서 모두 자바스크립트 코어인 ECMAScript(JS 핵심 문법)을 가지고 있지만, 이외에 추가로 제공하는 기능은 호환되지 않는다.

Node.js: ECMAScript + Node.js 고유의 API

브라우저: ECMAScript + DOM API (DOM, Canvas, fetch, SVG, Web Storage, Web Worker ..)

 

요약❗️자바스크립트는 어디서 실행되느냐에 따라서 사용할수 있는 기능이 다르다.

왜 JS는 브라우저라는 제한된 환경에서 실행되었는가?

JS가 생긴 목적은 기존의 정적 콘텐츠로 구성되어 있던 웹페이지에 동적인 기능을 추가하기 위해 개발되었다. 당시의 JS 사용 목적은 브라우저에서 JS가 실행되도록 하는 것이었으므로, 다음과 같은 이유로 브라우저 상에서만 JS가 실행되게 하였다.

  1. JS는 웹 개발을 위한 언어이다.
    JS는 웹페이지를 동적으로 조작하기 위해 만들어진, 브라우저를 위한 언어이다.
  2. 서버와 클라이언트가 명확히 역할 분담이 되어야 한다.
    서버는 데이터 처리를 맡고, 클라이언트는 데이터를 화면에 표시한다. JS는 그러한 클라이언트의 역할을 돕기 위해 개발되었다.
  3. 보안
    JS가 파일 시스템에 접근할 수 있다면, 브라우저에서 다운받은 파일에 악성코드가 심겨져서 사용자의 정보를 탈취할 수 있을 것이다.
  4. 플랫폼 독립성
    JS는 브라우저에서 동작하기 때문에 운영체제나 하드웨어에 상관없이 웹페이지에서 동일한 동작을 보장할 수 있다.

 

백준 3273 - 두 수의 합

 

해결 방법

1️⃣ 파이썬의 특수 구문을 활용하면 문제를 간단히 해결할 수 있다. (for - in) x - ai 가 수열에 포함되는지 확인하면 된다.

 

2️⃣ 내가 작성하는 문법이 파이썬이 아님을 생각했을 때 다른 방식으로 풀 수 있다. 먼저 주어진 수열에서 max 값을 뽑은 다음, 그 크기만큼의 새로운 리스트를 만든다. 그 뒤, 리스트의 인덱스를 활용해서 입력받은 수열의 존재여부를 기록한다. 그리고  x - ai가 그 리스트에서 1 (존재) 인지 아닌지 체크한다.

어려웠던 점

✅ 배열의 인덱스에 따른 예외 처리를 생각하지 못했음.

새로 알게 된 점

✅ set과 list의 차이

접근 방법

1️⃣ 처음에는 set 대신 list를 사용했는데, 시간초과가 났고, list를 set으로 바꾸니까 해결되었다. 그리고 // 연산자 대신 /을 한 뒤 형변환을 했는데, //로 바꾸고 시간이 약간 줄었다.

N = int(input())
arr = set(map(int, input().split()))
X = int(input())

count = 0

for i in arr:
  if X-i in arr:
    count += 1

print(count//2)

 

2️⃣ 예외 처리를 고려하지 않아 런타임에러(IndexError)가 발생했었다.

N = int(input())
arr = list(map(int, input().split()))
X = int(input())

arr1 = [0] * (max(arr) + 1)
for i in arr:
  arr1[i] = 1

count = 0

for i in arr:
  remain = X - i
  if remain < 0 or remain >= len(arr1): continue
  if remain != i and arr1[remain] == 1:
    arr1[i] = 0
    count += 1

print(count)

 

2️⃣ 보다 1️⃣ 번이 훨씬 효율적이었다. 

'Algorithm' 카테고리의 다른 글

[BOJ] 1475 방 번호 - 파이썬  (0) 2024.08.20
[BOJ] 2577 숫자의 개수 - 파이썬  (0) 2024.08.19
[바킹독] 배열  (0) 2024.08.17
[BOJ] 10808 알파벳 - 파이썬  (0) 2024.08.06
[BOJ] 1629 곱셈 - 파이썬  (0) 2024.08.04

백준 1475 - 방 번호

해결 방법

0~9까지의 인덱스를 가지는 배열을 선언하고, 방 번호에 필요한 숫자들의 개수를 +1 하면 된다. 그런 다음, 가장 큰 값만큼 숫자 세트를 구매하면 된다.

어려웠던 점

✅ 9와 6에 대한 예외가 없었다면, 그냥 배열의 max 값을 뽑으면 되었을텐데, 9와 6이 서로 대체 가능하다는 점을 고려하는 게 까다로웠다. 

새로 알게 된 점

올림은 +1 한 다음 나머지를 버리면 되는구나를 알았다.

접근 방법

1️⃣ 방 번호를 가장 작은 자리수부터 탐색하며 0~9까지 인덱스를 가지는 배열의 값을 +1 하려고 했다. 그 이후 max 값에 6, 9 인덱스가 포함되어 있을 경우 예외 처리를 하려고 했다. 하지만, "max 값 확인 -> 6, 9 포함 -> 6, 9 예외처리 -> 다시 max 값 확인" 이 과정이 너무 번거로웠다.

 

2️⃣ max 값을 뽑기 전에 예외 처리를 하기로 했다. 그러면 max 값을 가지는 모든 인덱스를 확인해서 그 인덱스에6, 9가 포함되는지 확인하지 않아도 되니 1번 보다 효율적이었다. 먼저 6, 9가 공통으로 가지는 부분은 1set로 사야하는 부분이다. 그리고 그 둘의 차이는 절반만 사도 된다. 단, 그 둘의 차이가 홀수라면 한 세트를 추가적으로 구매해야 한다.

# 모든 수를 순회하면서 0~9까지 숫자가 몇 개인지 체크. 나중에 6,9를 예외처리해서 몇 개의 플라스틱 세트를 사야 하는지 확인하기
import math

N = int(input())

answer = [0]*10

while(N > 0):
  answer[int(N%10)] += 1
  N = int(N/10)

if answer[6] > answer[9]:
  answer[6] = answer[9] + math.ceil((float(answer[6] - answer[9])) / 2)
elif answer[6] < answer[9]:
  answer[9] = answer[6] + math.ceil((float(answer[9] - answer[6])) / 2)

print(int(max(answer)))

 

3️⃣ math를 사용하지 않고 올림 처리를 했다. 또한, 6, 9 인덱스를 뺀 나머지에서 max를 구하고, 6, 9인덱스 값과 비교해 max를 뽑는 식으로 로직을 수정하여 코드 길이를 단축시켰다.

N = int(input())

answer = [0]*10

while(N > 0):
  answer[int(N%10)] += 1
  N = int(N/10)

max_value = max([answer[i] for i in range(len(answer)) if (i != 6 and i != 9)])

print(int(max(max_value, (answer[6] + answer[9] + 1) / 2)))

'Algorithm' 카테고리의 다른 글

[BOJ] 3273 두 수의 합 - 파이썬  (0) 2024.08.21
[BOJ] 2577 숫자의 개수 - 파이썬  (0) 2024.08.19
[바킹독] 배열  (0) 2024.08.17
[BOJ] 10808 알파벳 - 파이썬  (0) 2024.08.06
[BOJ] 1629 곱셈 - 파이썬  (0) 2024.08.04

 

import math

N = int(input())

answer = [0]*10

while(N > 0):
  answer[int(N%10)] += 1
  N = int(N/10)

if answer[6] > answer[9]:
  answer[6] = answer[9] + math.ceil((float(answer[6] - answer[9])) / 2)
elif answer[6] < answer[9]:
  answer[9] = answer[6] + math.ceil((float(answer[9] - answer[6])) / 2)

print(int(max(answer)))

백준 2577 - 숫자의 개수

접근 방법

1️⃣ 세 수의 곱셈 결과를 배열로 변환하고, 0~9 배열의 인덱스를 활용해 각 숫자에 해당하는 배열의 값을 +1 함.

A = int(input())
B = int(input())
C = int(input())

result = A*B*C

multiple = [int(digit) for digit in str(result)]

answer = [0]*10

for i in multiple:
  if i == 0:
    answer[0] += 1
  elif i == 1:
    answer[1] += 1  
  elif i == 2:
    answer[2] += 1  
  elif i == 3:
    answer[3] += 1  
  elif i == 4:
    answer[4] += 1  
  elif i == 5:
    answer[5] += 1  
  elif i == 6:
    answer[6] += 1  
  elif i == 7:
    answer[7] += 1  
  elif i == 8:
    answer[8] += 1  
  elif i == 9:
    answer[9] += 1

for i in answer:
  print(i)

 

2️⃣ if elif문이 너무 길어서 코드 길이를 단축하고자 함

A = int(input())
B = int(input())
C = int(input())

result = A*B*C

multiple = [int(digit) for digit in str(result)]

answer = [0]*10

for i in multiple:
  answer[i] += 1
  
for i in answer:
  print(i)

 

3️⃣ while문을 사용해서도 풀 수 있을 것 같았음. 그러면 세 수의 곱을 배열로 저장하지 않아도 됨. 백준 결과 세 번째 방식이 가장 효과가 좋았음.

A = int(input())
B = int(input())
C = int(input())

result = A*B*C

answer = [0]*10

while(result>0) :
  answer[int(result%10)] += 1
  result = result/10
  print(result)

for i in answer:
  print(i)

 

'Algorithm' 카테고리의 다른 글

[BOJ] 3273 두 수의 합 - 파이썬  (0) 2024.08.21
[BOJ] 1475 방 번호 - 파이썬  (0) 2024.08.20
[바킹독] 배열  (0) 2024.08.17
[BOJ] 10808 알파벳 - 파이썬  (0) 2024.08.06
[BOJ] 1629 곱셈 - 파이썬  (0) 2024.08.04

❗️바킹독 강의 정리

배열이란?

배열은 메모리 상에 원소를 연속하게 배치한 자료구조

 

배열의 성질

  1. O(1)에 k번째 원소를 확인/변경 가능
  2. 추가적으로 소모되는 메모리의 양이 거의 없음
  3. cache hit rate가 높음
  4. 메모리 상에 연속한 구간을 잡아야 해서 할당에 제약이 걸림

원소의 삽입/삭제는 O(N) 시간이 걸림.


배열과 리스트의 차이는?

배열과 리스트의 차이를 이해하기 위해서는, 메모리 사용법이 다르다는 것을 알아야 한다.

배열은 연속된 메모리 공간에 원소를 넣는 것이고, 리스트는 연속되지 않은 메모리 공간을 사용한다.

이 차이에서 배열과 리스트의 내부 동작 방식이 완전히 달라진다.

파이썬에서 배열은?

❗️파이썬에서 배열은 리스트와 다르다는 것을 인지하자.

 

배열은 같은 자료형의 데이터들이 하나의 변수로 정의된 것이다. 그런데 파이썬에서는 서로 다른 자료형들이 하나의 변수로 정의할 수 있도록 만들어놓았다. 그것이 바로 '리스트'이다.

 

우리가 흔히 쓰는 list = [] 이런 형식의 파이썬 코드는 '리스트'이다.

 

배열은, numpy같은 외부 라이브러리를 사용해야 한다.

파이썬에서 배열의 삽입/삭제는?

앞서 파이썬에서 배열은 numpy같은 라이브러리를 사용한다고 했다. 따라서 파이썬에서 원소의 삽입 삭제는 insert 함수와 delete 함수를 통해 새로운 배열을 생성할 수 있다.

 

동작 방식은, 복사의 방식을 사용한다. 즉, 새로운 크기의 배열을 만든 후 원래 배열 요소를 복사한다.


파이썬 리스트로 배열 insert 구현하기

배열의 동작 방식을 이해하기 위해 파이썬으로 insert를 구현해보자. 나는 새로운 리스트를 하나 만들고, 거기에 원본 배열을 복사했다. 

def insert(idx, num, arr, len) :
  copy_arr = []

  insert_idx = 0

  for i in range(0, len + 1):
    if i != idx:
      copy_arr.append(arr[insert_idx])
      insert_idx += 1
    elif i == idx:
      copy_arr.append(num)

  return copy_arr

'Algorithm' 카테고리의 다른 글

[BOJ] 3273 두 수의 합 - 파이썬  (0) 2024.08.21
[BOJ] 1475 방 번호 - 파이썬  (0) 2024.08.20
[BOJ] 2577 숫자의 개수 - 파이썬  (0) 2024.08.19
[BOJ] 10808 알파벳 - 파이썬  (0) 2024.08.06
[BOJ] 1629 곱셈 - 파이썬  (0) 2024.08.04

+ Recent posts