SHUSTORY

[ 백준 ] 1008번 : A/B _ JAVA 본문

코딩테스트/JAVA

[ 백준 ] 1008번 : A/B _ JAVA

어서오시우 2023. 11. 25. 00:38
728x90
문제

  • 주의 사항
    • 입력이 공백 단위로 이루어진다.
    • 출력값의 절대오차 또는 상대오차 범위
    • 문제 조건에서 오차의 범위가 10^(-9)이기 때문에 double 형을 써야 한다.
      • 오차 범위가 문제의 조건으로 등장하면 7자리 이하의 정밀도가 필요한 경우에는 float를 사용하고,
        7자리 이상의 정밀도가 필요할 경우 double을 사용한다.
      • 정밀도란 해당 자릿수까지 오차 없이 저장되는 범위를 의미한다.
      • 참고로 오차 범위가 따로 주어지지 않을 경우 연산속도 및 메모리 절약을 위해 float를 사용하는 것이 바람직

 

풀이 방법
[Java] 백준 1008번 : A/B (입출력과 사칙연산) — 조각보 (tistory.com) 참고
  • Scanner 사용
    • Scanner는 리소스를 많이 사용하여 성능 부하를 초래하기 때문에 속도가 느린 편으로, 입출력이 많은 코드에서는 지양하는 것이 좋다.
  • BufferedReader 사용
    • BufferedReader는 Buffer에 있는 IO 클래스로 데이터를 버퍼에 저장해두었다가 한번에 입력받는 방식으로 동작해, 데이터를 하나씩 입력받는 것이 아닌 한번에 입력하므로 속도가 빨라 입출력이 많은 코드에서 지향한다.
    • BufferedWriter를 사용하지 않는다.
      • BufferedWriter / BufferedReader 는 String의 형태로만 입출력이 가능하기 때문에 다른 자료형의 값을 출력하려면 다시 String으로 형변환 해주어야 한다. 두 번 파싱이 들어가는 것보다 System.out.println()을 쓰는 것이 속도면에서 더 효율적이기 때문에 System.out.println()을 사용한다.

  • 위 풀이가 BufferedReader를 사용한 코드이고, 아래 풀이가 Scanner를 사용한 풀이인데,
    코드 길이가 2배 가량 되는데도 동작 시간은 반절이나 줄어든 것을 볼 수 있다. 신기해!!

 

내 풀이
코드 리뷰
import java.util.Scanner;

public class Main {
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
int A = sc.nextInt();
int B = sc.nextInt();

double C = (double)A/(double)B;
System.out.println(C);
}
}
  • 나는 Scanner를 사용해서 코드를 작성했는데, 코드가 정상적으로 실행은 됐으나.. 다른 사람들이 푼 풀이방법을 보니까 내 코드가 너무 밥..으로 보여서.. 충격 받았다..
  • Scanner 객체 사용 후 close() 메소드를 사용해서 입력버퍼 종료시켜주어야 한다.
    • 백준이나 코딩테스트에서 하는 프로그램은 작은 단일 프로그램이라 해당 프로그램이 끝나면 OS가 자원회수를 다 해주기 때문에 굳이 해줄 필요는 없지만, 파일에 접근할 때는 close() 를 해주어야 하기 때문에 습관을 들여놓는 것이 좋다.
  • Scanner의 next() 메서드는 기본적으로 공백을 기준으로 값을 구분하여 입력받는다.
    • String 자료형을 읽어들이는 nextLine() 메서드는 공백이 아닌 엔터를 기준으로 값을 읽어 온다.
    • String 자료형을 읽어들이는 next() 메서드는 공백을 기준으로 값을 읽어 온다.
  • int형으로 입력을 받을 경우 소수점 이하를 버리고 계산하기 때문에 double형으로 입력을 받아서 계산한다.
    • 아니 근데 나는 두 정수 A와 B를 입력받으라길래.. 꾸역꾸역 int형으로 입력받은건데 다들 double형으로 입력 받더라고? .. 내가 잘못 이해한거니..ㅜ

 

풀이
내 풀이 수정 ( Scanner 사용 )
import java.util.Scanner;

public class Main {
public static void main(String[] args){
Scanner sc = new Scanner(System.in);

double a = sc.nextDouble();
double b = sc.nextDouble();
sc.close();

System.out.println(a/b);
}
}

 

풀이
BufferedReader 사용
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;

public class Main {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine()," ");

double A = Double.parseDouble(st.nextToken());
double B = Double.parseDouble(st.nextToken());

System.out.println(A/B);
br.close();
}
}
  • public static void main(String[] args) throws IOException {
    • 자바에서 예외(Exception)가 발생할 수 있는 코드를 작성할 때, 이 예외를 처리하는 방법에는
      예외를 직접 처리하기(try-catch) 혹은 예외를 호출자에게 던지기(throws) 방법이 있다.
    • 지금 코드에서 throws IOException을 선언하는 이유는 BuffredReader에서 readLine() 메서드를 호출할 때 발생할 수 있는 입출력 예외를 처리하기 위함이다.
  • BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    • BuffredReader는 효율적인 문자 입력을 제공하는 클래스로, 문자들을 버퍼에 읽어두었다가 한 줄씩 읽을 수 있게 해주므로 입출력 속도를 향상시킬 수 있다.
    • InputStreamReader는 바이트 기반의 입력 스트림을 문자 기반의 입력 스트림으로 변환하는 역할을 하여
      바이트 단위의 데이터를 문자 단위로 읽을 수 있게 도와준다.
      • BuffredReader가 문자 기반의 입력을 처리하기 때문에,
        System.in을 통해 사용자로부터 키보들 입력받아 바이트 스트림으로 들어오는 값을 문자로 변환해주어야 한다.
  • StringTokenizer st = new StringTokenizer(br.readLine()," ");
    • br.readLine()은 사용자로부터 한 줄을 읽어 오는 메서드로,
      사용자가 엔터 키를 입력할 때까지의 모든 문자열을 하나의 줄로 간주한다.
    • StringTokenizer은 문자열을 지정된 구분자를 기준으로 토큰을 나누어주는 클래스이다.
      여기서는 공백을 기준으로 나누어 StringTokenizer 객체 st에 저장한다.
  • double A = Double.parseDouble(st.nextToken());
    double B = Double.parseDouble(st.nextToken());
    • Double.parseDouble()은 문자열을 double 형으로 변환하는 메서드이다.
    • st.nextToken() 을 통해 StringTokenizer에서 다음 토근을 가져온다.

 

풀이
BufferedReader 사용 ( split을 사용하여 공백 기준으로 문자열 나누기 )
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class Main {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String st = br.readLine();
String[] tokens = st.split(" ");

double A = Double.parseDouble(tokens[0]);
double B = Double.parseDouble(tokens[1]);

System.out.println(A/B);
br.close();
}
}
  • String[] tokens = st.split(" ");
    • st.split(" ")은 문자열을 공백을 기준으로 나누어준다.
      공백을 기준으로 나누어진 값을 배열에 저장한다.
Scanner vs BufferedReader
  • 대부분 코딩테스트에서는 제한시간을 주기 때문에, BufferedReader를 사용하는 것이 유리하다.
  • 코드를 작성하는 것이 BufferedReader가 더 복잡함에도 BufferedReader가 더 빠르다.
    • BufferedReader 객체를 생성하고 그 안에 InputStreamReader 객체를 생성하고, readLine이라는 함수로 받으면 한 줄씩 String 형태로 받게 되므로 StringTokenizer를 생성해서 받아줘야 하고, 받은 값을 int형으로 파싱까지 해주어야 한다.
  • BufferedReader
    • Scanner와 유사
    • Enter만 경계로 인식하고 String 형만 받는다.
  • BufferedWriter
    • System.out.println과 유사