[알고리즘]통계학

package org.example.통계학;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.text.MessageFormat;
import java.util.*;

/* *
 *
 *
 *
 *
 *
 *
 * N 개의 수
 *  N 은 홀수
 * 1<= N <= 500000
 * 입력되는 정수 => -4000 ~ 4000
 *  */
public class Main {
    public static void main(String[] args) {
        try (BufferedReader br = new BufferedReader(new InputStreamReader(System.in))) {
            int N = Integer.parseInt(br.readLine());
            
            if (!(N % 2 == 1)) {
                String errorMessage = MessageFormat.format("N =>{0} 는 홀수여야 합니다.", N);
                throw new IllegalArgumentException(errorMessage);
            }
            
            if (!(1 <= N && N <= 500_000)) {
                String errorMessage = MessageFormat.format("N =>{0} 는 1 이상 500,000 이하여야 합니다.", N);
                throw new IllegalArgumentException(errorMessage);
            }
            
            List<Number> numbers = new ArrayList<>();
            for (int i = 0; i < N; i++) {
                int inputNumber = Integer.parseInt(br.readLine());
                Number number = new Number(inputNumber);
                numbers.add(number);
            }
            
            // 산술 평균 구하기
            numbers.stream()
                   .mapToInt(Number::getValue)
                   .average()
                   .ifPresent(i -> System.out.println(Math.round(i)));
            
            // 중앙값 구하기
            numbers.stream()
                   .sorted()
                   .skip(N / 2)
                   .findFirst()
                   .ifPresent(number -> System.out.println(number.getValue()));
            
            // 최빈값
            HashMap<Number, Integer> numberCountMap = new HashMap<>();
            for (Number number : numbers) {
                int count = numberCountMap.getOrDefault(number, 0);
                numberCountMap.put(number, count + 1);
            }
            
            int maxCount = 0;
            List<Number> modeNumbers = new ArrayList<>();
            for (Map.Entry<Number, Integer> entry : numberCountMap.entrySet()) {
                if (entry.getValue() == maxCount) {
                    modeNumbers.add(entry.getKey());
                    continue;
                }
                if (entry.getValue() > maxCount) {
                    modeNumbers.clear();
                    maxCount = entry.getValue();
                    modeNumbers.add(entry.getKey());
                }
            }
            if (modeNumbers.size() > 1) {
                Collections.sort(modeNumbers);
                System.out.println(modeNumbers.get(1)
                                              .getValue());
            } else {
                System.out.println(modeNumbers.get(0)
                                              .getValue());
            }
            
            // 범위 ( 최댓값과 최솟값의 차이)
            int max = numbers.stream()
                             .mapToInt(Number::getValue)
                             .max()
                             .orElseThrow();
            
            int min = numbers.stream()
                             .mapToInt(Number::getValue)
                             .min()
                             .orElseThrow();
            
            System.out.println(max - min);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    
    public static class Number implements Comparable<Number> {
        private final int value;
        
        public Number(int value) {
            if (!(-4_000 <= value && value <= 4_000)) {
                String errorMessage = MessageFormat.format("value =>{0} 는 -4000 이상 4000 이하여야 합니다.", value);
                throw new IllegalArgumentException(errorMessage);
            }
            this.value = value;
        }
        
        public int getValue() {
            return value;
        }
        
        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (!(o instanceof Number)) return false;
            
            Number number = (Number) o;
            
            return value == number.value;
        }
        
        @Override
        public int compareTo(Number o) {
            return Integer.compare(this.value, o.value);
        }
        
        @Override
        public int hashCode() {
            return value;
        }
        
        @Override
        public String toString() {
            return "Number{" +
                "value=" + value +
                '}';
        }
    }
}
  • 일단 클래스로 뽑아낸다고 하더라도 유틸성 클래스가 될거같아서 메인문에서 전부 수행했다.

Stream 사용

  • 개요
    • Java 8 부터 도입되었습니다.
    • 함수형 프로그래밍의 접근 방식을 사용하고 있습니다.
    • 주로 컬렉션에 대한 연산을 비동기적이거나 병렬적으로 처리하는 데 유용하게 쓰인다고 합니다.
  • 생성 방법
    • Arrays.stream(array) ← 배열에 대한 스트림 생성
    • Collection.stream() ← 컬렉션에 대한 스트림 생성
    • 등등이 있는데 대표적으로 이 2가지가 많이 사용됩니다.
  • 중간 연산법
    • 스트림을 다른 스트림으로 변환하는 연산법입니다.
    • lazy 하게 연산이 수행됩니다.
      • 종료 연산이 호출될 때까지는 실제로 수행되지 않는 연산이라는 의미입니다!
    • 예시
      • filter(predicate) : 해당 조건에 맞는 요소만 선택할때 사용
      • map(function) : 각 요소를 다른 형태로 변환할 때 사용
      • sorted() : 스트림의 요소를 정렬
      • distinct() : 중복되는 요소를 제거합니다.
      • skip(n) : 처음 n 개의 요소를 무시합니다.
  • 종료 연산
    • 스트림을 최종적으로 소비하여 결과를 생성합니다!
    • 예시
      • collect() : 결과를 컬렉션으로 변환시 사용합니다.
      • forEach(consumer) : 각 요소에 대해 작업을 수행할 수 있습니다 consumer 에 로직을 주입할 수 있습니다.
      • reduce() : 스트림의 요소를 하나의 값으로 축약할 수 있습니다.
      • average() , max() , min() , sum() : 산술 연산입니다.
      • findFirst() , findAny() : 조건을 만족하는 첫 번째, 임의 요소 반환

산술평균의 경우

numbers.stream()
       .mapToInt(Number::getValue)
       .average()
       .ifPresent(i -> System.out.println(Math.round(i)));
  1. numbers.stream(): 리스트 numbers로부터 스트림 생성
  1. mapToInt(Number::getValue): Number 객체의 getValue 메서드를 호출하여 int로 변환
  1. average(): 평균 계산
  1. ifPresent(): 평균 값이 존재하면 출력

중앙값 구하기

numbers.stream()
       .sorted()
       .skip(N / 2)
       .findFirst()
       .ifPresent(number -> System.out.println(number.getValue()));
  1. sorted(): 숫자를 정렬
  1. skip(N / 2): 처음 N / 2개의 요소를 무시
  1. findFirst(): 남은 요소 중 첫 번째 요소를 찾음
  1. ifPresent(): 값이 존재하면 출력

HashMap 에서 EntrySet이란..

  • entrySet() 의 원리
    • entrySet() 메서드는 HashMap 내부 데이터에 접근하여 , 키-값 쌍을 Set 형태로 반환합니다.

    Set() 형태로 반환하는 걸까? (HashSet 을 예시로 설명)

    • 일단 Set 인터페이스는 중복값을 허용하지 않는다는 특징이 있습니다.
    • 그래서 contains ( 해당 값을 가지고 있는지 체크 ) … 등의 수행을 하는 경우
      • 내가 찾고자 하는 인자의 객체나 값을 ⇒ 해시 값으로 변환
      • 그 해시 값은 Set 자료구조 키값이 되겠죠
      • 그 키값에 value 가 있는지 확인한다면 ⇒ 찾을 객체의 존재여부를 쉽게 파악할 수 있습니다.
      • 해당 행위는 어떠한 반복을 수행하지 않기에

      상수시간이 가능한 장점이 있어서 Set 으로 HashMap 을 순회하게 되는 것입니다.


Uploaded by N2T

'자바 > 알고리즘' 카테고리의 다른 글

[알고리즘] 바이러스  (0) 2023.08.28
[알고리즘]수 찾기  (0) 2023.08.27
[알고리즘]문자열 집합  (0) 2023.08.25
[알고리즘]스택 수열  (0) 2023.08.23
[알고리즘]소수 구하기  (0) 2023.08.19