문제 및 이론 정리/기타

[Java] 별(*) 피라미드 출력 문제 코드 작성 (231230)

hail2y 2023. 12. 31. 05:43

벨로그에서 95% 쓴 글이 임시저장도 안 되고 출간도 안 된 채로 순식간에 날아갔다. 정신 잃고 쓰러질 뻔 했지만 친구가 세뇌하듯 위로해줬다...고맙다 덕분에 정신승리하며 다시 쓴다..! 내가 학기 중에 필기 노트 다 날라간 것도 견뎌냈는데... 아자아자 파이팅!

파이썬이랑 c 공부할 때도 피라미드 문제는 항상 어려웠는데 이제는 더 이상 어려움 느끼면 안 될 것 같아서 각 잡고 다 다뤄보기로 다짐했다! 이렇게 앞으로 나아가는 거겠지- 실제로 어제 코드들 쳐 보면서 하나하나 되니 자신감도 생기고 더 공부하고 싶단 생각이 들었다. 이제 잡담 그만하고 바로 들어가자.

 

(240702) 아래는 반복문을 이용해 구현했지만 시간복잡도를 더 줄이는 방법으로 조건문으로도 엮어서 풀 수 있다. 하루코딩 님 유튜브 영상을 보면 확인해 볼 수 있다! 

 

1. 왼쪽 정렬한 피라미드 모양

   public class Pyramid {
        public static void main(String[] args) {
            for(int i=1; i<=5; i++) {
                for(int j=1; j<=i; j++) {
                    System.out.print("*");
                }
                System.out.println();
            }
        }
    }

 

중첩 for문 처음 배울 땐 어려웠지만 지금은 그래도 익숙해져서 큰 어려움은 없다. 초기값 설정할 때 나중에 헷갈릴까봐 1로 설정해 뒀다.

 

과거의 나에게:

println()은 출력 후 다음 라인으로 넘어간다. 라인을 넘기지 않고 출력하려면 print()를 사용한다. 

ex) System.out.print("*");

 

2. 오른쪽 정렬한 피라미드 모양

    public class Pyramid {
        public static void main(String[] args) {
            for(int i=1; i<=5; i++) { // 1,2,3,4,5
                // 공백 출력
                for(int j=1; j<=5-i; j++) { // 4,3,2,1,0
                    System.out.print(" ");
                }
                // 별 출력
                for(int j=1; j<=i; j++) {
                    System.out.print("*");
                }
                System.out.println();
            }
        }
    }

 

 

이 문제를 풀기 위해서는 공백 출력과 별 출력, 두 부분으로 나누어 생각해볼 수 있어야 한다. 항상 공백 출력 부분이 어려웠는데 그림 그려가 보면서, 그리고 전체 중에 몇 칸이 공백으로 들어올 지 생각해 보면서 한결 수월해 졌던 것 같다. 그래도 가운데 정렬 피라미드보단 쉬운 편...

 

2.5 홀수 개의 별들이 출력되는 모양

  public class Pyramid {
        public static void main(String[] args) {
            for(int i=1; i<=5; i++) { // 1,2,3,4,5
                for(int j=1; j<=(2*i-1); j++) {
                    System.out.print("*");
                }
                System.out.println();
            }
        }
    }

 

가운데 정렬한 피라미드에 들어가기 전에 홀수 개의 별을 출력하는 모양을 보자. 이것도 솔직히 처음에 순탄히 풀리지는 않았다. 빠릿빠릿하게 생각하지 않은 채 증감식에만 매몰돼서(변명) 거기만 보다가 답을 확인했으니... 그리고 홀수 개를 찍으라길래 2*j+1만 생각했다. 그러다 다른 사람 풀이에서 뒤늦게  2*j-1를 발견하고 아차 싶었다.

 

홀수 표현에는 2*j+1, 2*j-1 두 가지 방식이 있는데 어떤 거를 쓸 지는 직접 값을 대입해 보면서 의도한 모양대로 맞게 나오는지 확인해 봐야한다. 여긴 그것만 조심하면 잘 풀 수 있다! 

 

3. 가운데 정렬한 피라미드 모양 

   public class Pyramid {
        public static void main(String[] args) {
            for(int i=1; i<=5; i++) { // 1,2,3,4,5
                // 공백 출력
                for(int j=1; j<=5-i; j++) {
                    System.out.print(" ");
                }
                // 별 출력
                for(int j=1; j<=(2*i-1); j++) {
                    System.out.print("*");
                }
                System.out.println();
            }
        }
    }

 

 

완-전 이해 못 했을 땐 공백을 어떻게 넣어야 할 지 전혀 몰랐었다. 사실 공백 처리를 해야 한다는 것도 몰랐을 것 같다. 이번에도 공백을 어떻게 다뤄야 하는지 기억이 안 나서 찾아보다가 공백을 어떤 문자로 대치해 푸는 꿀팁을 발견했다.

(참고: https://javanewbie.tistory.com/5 )

 

공백에 대한 이해는 했지만 이것을 어떻게 5-i로 하는지는 직접 그림을 그려가 보면서 이해할 수 있었다. 가운데 별을 기준으로 5칸 중 4 > 3 > 2 > 1 > 0 순으로 줄어드니 높이(5) 중 i를 빼 보는 걸 통해 일반화했다. 처음에는 전체 너비(9) 중 별을 기준으로 대칭이 되니까 왼쪽 공백+별+오른쪽 공백을 다 신경쓰는 줄 알았다. 근데 공백은 그냥 왼쪽만 신경쓰고, 너비가 아닌 이미 제시된 힌트(높이 5)를 가지고 잘 변형하면 되는 문제였다. 어제만 해도 구체적인 수학적 증명이 이게  맞는지 헷갈렸는데 지금은 이렇게 도출하는 게 맞다란 생각이 든다.

 

그리고 코드를 써 가면서 깨달은 건데 오른쪽 정렬한 모양, 홀수 개 출력된 모양이 합쳐진 코드였다..! 진짜 방금 안 사실 

 

~ 그리고 뒤집어보자 ~

1. 왼쪽 정렬한 피라미드를 위아래로 뒤집은 모양

 

(1) 증감 연산자 i-- 사용

    public class Pyramid {
        public static void main(String[] args) {
            for(int i=5; i>=1; i--) { // 5,4,3,2,1
                for(int j=5; j>5-i; j--) { // 5,4,3,2,1
                    System.out.print("*");
                }
                System.out.println();
            }
        }
     }
    public class Pyramid {
        public static void main(String[] args) {
            for(int i=5; i>=1; i--) { // 5,4,3,2,1
                for(int j=1; j<=i; j++) { // 5,4,3,2,1
                    System.out.print("*");
                }
                System.out.println();
            }
        }
    }

 

원래 알고 있던 피라미드 모양이 반대가 되니 자연스럽게 증감연산자도 반대로 설정하게 되더라.. 전체 for 루프는 어차피 몇 줄을 출력할 거냐를 결정하겠지만.. 물론 i 값에 따라 내포된 for 루프의 조건문을 신경써 줘야겠지! 

어쨌든 j--도 같은 맥락에서 해 줬지만 다른 분의 코드에서 저걸 순방향으로 설정해 준 걸 보고 이렇게도 표현할 수 있구나를 느꼈다. 

 

(참고: https://kyhyuk.tistory.com/40)

 

(ii) 증감 연산자 i++ 사용

    public class Pyramid {
        public static void main(String[] args) {
            for(int i=1; i<=5; i++) { // 1,2,3,4,5
                for(int j=5; j>=i; j--) { // 5,4,3,2,1
                    System.out.print("*");
                }
                System.out.println();
            }
        }
    }
    public class Pyramid {
        public static void main(String[] args) {
            for(int i=1; i<=5; i++) { // 1,2,3,4,5
                for(int j=i; j<=5; j++) { // 5,4,3,2,1
                    System.out.print("*");
                }
                System.out.println();
            }
        }
    }

 

 

 

 

다른 문제도 마찬가지겠지만 이 문제를 통해 코드가 진짜 다양하게 나올 수 있구나를 느꼈다. 전체 for 루프의 증감조건을 i++로 걸어도 내부 for 루프에서 j--, j++ 둘 중 어떤 걸 취할지에 따라 2개가 더 나오더라 뭐 출력 결과만 같게 나오면 숫자를 요리조리 변형시켜도 되겠지만 논리적으로 뭐가 이해하기 좋은지를 고려해 작성하는 것이 좋을 거 같다. 이건 쉬운 문제니까 이해가 빠르게 되어도 복잡한 문제에서는 어떤 식으로 코드를 작성하면 좋을지, 이 중에서 뭐가 가장 가독성 좋은 코드인지가 궁금해진다.

 

(초기 값을 0, 1 중 어떤 값으로 설정할 지에 따라 또 다양하게 나온다. )

 

2. 오른쪽으로 정렬한 피라미드를 위아래로 뒤집은 모양

 

    public class Pyramid { // 초기 값 0, 대소관계 등호 없이 유지
        public static void main(String[] args) {
            for(int i=0; i<5; i++) { // 0,1,2,3,4
                // 공백 출력
                for(int j=0; j<i; j++) { // 0,1,2,3,4
                    System.out.print(" ");
                }
                // 별 출력
                for(int j=5; j>i; j--) { // 5,4,3,2,1
                    System.out.print("*");
                }
                System.out.println();
            }
        }
    }
    
    public class Pyramid { // 초기 값 1, 대소관계 경우에 따라 바뀜
        public static void main(String[] args) {
            for(int i=1; i<=5; i++) { // 1,2,3,4,5
                // 공백 출력
                for(int j=1; j<i; j++) { // 0,1,2,3,4
                    System.out.print(" ");
                }
                // 별 출력
                for(int j=5; j>=i; j--) { // 5,4,3,2,1
                    System.out.print("*");
                }
                System.out.println();
            }
        }
    }
    public class Pyramid {
        public static void main(String[] args) {
            for(int i=5; i>=1; i--) { // 5,4,3,2,1
                // 공백 출력
                for(int j=5; j>i; j--) {
                    System.out.print(" ");
                }
                // 별 출력
                for(int j=1; j<=i; j++) { // 5,4,3,2,1
                    System.out.print("*");
                }
                System.out.println();
            }
        }
    }

 

공백이 눈에 띄지 않으니까 앞에 하나씩 더 붙어 오른쪽으로 밀린 걸 늦게 알아차렸다. 이 작은 실수도 어쨌든 다른 출력결과를 만들어낸 거니까 좀 더 주의해야겠다! 앞에 것도 다시 다 확인해야 되잖아;; 아유

 

어쨌든 첫 번째 적은 코드는 초기 값을 0으로 할 거냐, 1로 할 거냐에 따라 비교연산자가 다르게 붙더라 어떤 게 더 좋을까를 생각해 봤는데 도긴개긴이겠지만 난 1로 하는 게 이해하기에 더 편한 느낌이 든다. 

 

3. 가운데 정렬한 피라미드 위아래로 뒤집은 모양

    public class Pyramid {
        public static void main(String[] args) {
            for(int i=5; i>=1; i--) { // 5,4,3,2,1
                // 공백 출력
                for(int j=1; j<=5-i; j++) { // 0,1,2,3,4
                    System.out.print(" ");
                }
                // 별 출력
                for(int j=1; j<=(2*i-1); j++) { // 9,7,5,3,1
                    System.out.print("*");
                }
                System.out.println();
            }
        }
    }

 

이것도 여러 경우가 나오겠지만 별 출력 부분의 조건식에 (2*i-1)을 넣어주려면 이 조합이 베스트인 것 같다. 지금 조금 지친 감이 없지 않은데 아까 참고 표시해 놓은 다른 분의 글에서도 이렇게 작성하신 걸 보면 맞을지도..?ㅎ 

 

어제 다 날라간 덕분에 오늘 하루 치 공부를 날렸는데 굴하지 않고 다 작성해 놔서 뿌듯하기 그지없다 정말

자바를 아직 제대로 경험한 것 같진 않지만 이 문제를 자바로 작성했다는 점도 뿌듯 포인트다! 

나중에 기억이 날라가면 다시 와서 참고해야지 이거 정리하면서 그래도 논리적 훈련을 많이 한 것 같아 유익했다 굿!

 

--끝--