Contents
숫자 스트림 활용(피타고라스 정리)
   Oct 2, 2022     4 min read

숫자 스트림 활용

피라고라스 수

  • 숫자스트림 + 스트림 연산 으로 피타고라스 수를 찾아보자.

피타고라스 수는 ?

Screen Shot 2022-10-02 at 11 35 08 PM

세 개의 정수 (a, b, c)가 있다. 예를 들어서 다시 피타고라스의 정리를 생각해보자.

(3, 4, 5)를 넣어보자.

Screen Shot 2022-10-02 at 11 35 14 PM

  • 9 + 16 = 25

수를 통해서 알아봤으니 그림을 통해서 이해해보자.

Untitled

세 수 표현하기

세수를 표현할 클래스를 정의하는 것보다는 세 요소를 갖는 int 배열을 사용하는 것이 좋다.

이유는 (3, 4, 5)를 new int[] {3, 4, 5}로 하게될 경우 인덱스로 접근하기 때문에 편리하기 때문이다.

좋은 필터링 조합

a * a + b * b을 자바 코드로 구현한다면 ? .. 제곱근으로 접근해보자.

Mtah.sqrt(a * a + b * b) % 1 == 0;

이때 부동 소숫점 수라면 x % 1.0이라는 자바 코드로 소숫점 이하 부분을 얻을 수 있다.

예를 들어 5.0이라는 수에 이 코드를 적용하면 소숫점 이하는 0이 된다.

위 코드를 filter 에서 활용이 가능하다.

filter(b -> Math.sqrt(a * a + b * b) % 1 == 0)

a라는 값이 주어지고 b는 스트림으로 제공된다고 가정한다면 filter로 a와 함께 피타고라스의 정리를 구성하는 모든 b를 필터링할 수 있다.

집합 생성

세번째 수를 찾아보자.

map을 이용해서 각 요소를 피타고라스 수로 변환해보자.

stream.filter(b -> Math.sqrt(a * a + b * b) % 1 == 0)
      .map(b -> new int[]{a, b, (int) Math.sqet(a * a + b * b)});

b값 생성

Stream.rnageClosed로 주어진 범위의 수를 만들 수 있다. 이것을 활용해보자.

1 ~ 100까지 b값을 생성할 수 있다.

IntStream.rangeClosed(1, 100)
         .filter(b -> Math.sqrt(a * a + b * b) % 1 == 0)
         .boxed()
         .map(b -> new int[]{a, b, (int)Math.sqrt(a * a + b * b)});

filter 연산 다음에 rangeClosed가 반환한 IntStream을 boxed를 이용해서 Stream로 복원했다. `map`은 스트림의 각 요소를 int 배열로 변환하기 때문이다. IntStream의 map 메소드는 스트림의 각 요소로 int가 반환될 것을 기대하지만 원하는 연산이 아니다.

개체값 스트림을 반환하는 IntStreammapToObj 메서드를 이용해서 이 코드를 재구현할 수 있다.

a값 생성

마지막으로 a값을 생성하는 코드를 추가하면 끝

결과적으로 피타고라스 수를 생성하는 스트림을 완성된다.

b와 비슷한 방법으로 a의 값을 생성해준다.

➡️ 최종

Stream<int[]> pythagoreanTriples =
  IntStream.rangeClosed(1, 100)
           .boxed()
           .fiatMap( a -> IntStream.rangeClosed(a, 100)
                                   .filter(b -> Math.sqrt(a * a + b * b) % 1 == 0)
                                   .mapToObj(new int[]{a, b, (int)Math.sqrt(a * a + b * b)})
  );

flatMap의 역할은 ?

먼저 a에 사용할 1부터 100까지의 숫자를 만들었다. 그리고 주어진 a를 이용해서 세 수의 스트림을 만든다. 스트림 a의 값을 매핑하면 스트림의 스트림이 만들어질 것이다.

따라서 flatMap 메서드는 생성된 각각의 스트림을 하나의 평준화된 스트림으로 만들어준다.

결과적으로 세 수로 이루어진 스트림을 얻을 수 있다. 또한 b의 범위가 a에서 100으로 바뀐점도 유의하자.

  • b를 1부터 시작하면 중복된 세수(ex (3,4,5)와 (4,3,5)가 생성될 수 있기 때문에 a부터 100까지로 범위를 바꿨다.

코드 실행

pythagoreanTriples.limit(5)
                  .forEach(t -> System.out.println(t[0[ + ", " + t[1] + ", " + t[2]));

➡️결과

3, 4 ,5
5, 12, 13
6, 8, 10
7, 24, 25
8, 15, 17

코드 개선의 부분

제곱근을 두 번 사용한다. (a * a, b * b, aa + bb)형식을 만족하는 세 수를 만든 다음에 우리가 원하는 조건에 맞는 결과만 필터링하는 것이 더 최적화된 방법이다.

개선된 코드

Stream<double[]> pythagoreanTriples2 =
  IntStream.rangeClosed(1, 100).boxed()
  .flatMap(a -> IntStream.rnageClosed(a, 100)
  .mapToObj(b -> new double[]{a, b, Math.sqrt(a * a + b * b)})//<--만들어진 세 수
  .filter(t -> t[2] % 1 == 0)); // <-- 세 수의 세 번째 요소는 반드시 정수여야 한다.