자바의 정석 기초(4) 연습문제: 조건문 반복문 편

NtoZ·2023년 2월 23일
0

Java

목록 보기
12/23
post-thumbnail

4-2.

  • 문제: 1부터 20까지의 정수 중에서 2 또는 3의 배수가 아닌 수의 총합을 구하라.
  • 내 풀이:
		//1부터 20까지의 정수 중에서 2 또는 3의 배수가 아닌 수의 총합을 구하시오.
		int i = 0;	//i는 증가하는 정수
		int sum = 0;//sum은 i의 값 중 2또는 3의 배수가 아닐때 누계한 값
		
		for(i=1;i<=20;i++) {//2의 배수이면서 3의배수인 값의 반대는 2의 배수 또는 3의 배수가 아닐 때이다.
			if(!(i%2==0&&i%3==0)) {
				sum +=i;
				System.out.printf("현재 %d를 더했고, 누계는 %d입니다.%n",i,sum);
			}
		}
<결과>
현재 1를 더했고, 누계는 1입니다.
현재 2를 더했고, 누계는 3입니다.
현재 3를 더했고, 누계는 6입니다.
현재 4를 더했고, 누계는 10입니다.
현재 5를 더했고, 누계는 15입니다.
현재 7를 더했고, 누계는 22입니다.
현재 8를 더했고, 누계는 30입니다.
현재 9를 더했고, 누계는 39입니다.
현재 10를 더했고, 누계는 49입니다.
현재 11를 더했고, 누계는 60입니다.
현재 13를 더했고, 누계는 73입니다.
현재 14를 더했고, 누계는 87입니다.
현재 15를 더했고, 누계는 102입니다.
현재 16를 더했고, 누계는 118입니다.
현재 17를 더했고, 누계는 135입니다.
현재 19를 더했고, 누계는 154입니다.
현재 20를 더했고, 누계는 174입니다.

<모범풀이>
		//1부터 20까지의 정수 중에서 2 또는 3의 배수가 아닌 수의 총합을 구하시오.
		int i = 0;	//i는 증가하는 정수
		int sum = 0;//sum은 i의 값 중 2또는 3의 배수가 아닐때 누계한 값
		
		for(i=1;i<=20;i++) {
			//2의 배수이면서 3의배수인 값의 반대는 2의 배수 또는 3의 배수가 아닐 때이다.
			if(!(i%2==0||i%3==0)) {
				sum +=i;
				System.out.printf("현재 %d를 더했고, 누계는 %d입니다.%n",i,sum);
			}
		}
<모범결과>
현재 1를 더했고, 누계는 1입니다.
현재 5를 더했고, 누계는 6입니다.
현재 7를 더했고, 누계는 13입니다.
현재 11를 더했고, 누계는 24입니다.
현재 13를 더했고, 누계는 37입니다.
현재 17를 더했고, 누계는 54입니다.
현재 19를 더했고, 누계는 73입니다.
  • ❌ 틀린 부분 :
    - '2 또는 3의 배수가 아닌 수'를 !(i%2==0&&i%3==0)라고 정의한 것이 잘못되었다. !(i%2==0&&i%3==0)i%2!=0||i%3!=0이다. 즉, '2의 배수가 아니거나 3의 배수가 아닌 수'를 뜻하는 것이다.
    - '2 또는 3의 배수가 아닌 수'는 !(i%2==0||i%3==0)로 정의 되어야 한다. 즉, i%2!=0&&i%3!=0이 되어야 하는 것이다.
  • ✔️반성할 점 :
    - 쉽게 생각하고 섣부르게 접근하지 말자! 특히 조건식을 뜻풀이를 주의깊게 생각하자!

4-3. 1+(1+2)+(1+2+3)+(1+2+3+4)+...+(1+2+3+...+10)의 결과값?

  • 🤔풀이 접근 : 점진적으로 더하는 자릿수가 늘어난다. 그렇다면 1)복합 대입 연산자를 사용하거나, 2)중첩 for문 중에서도 조건식이 점진적으로 변화하는 식을 세워보면 되겠군.
  • 풀이 과정:
<나의 풀이>
		int sum = 0;	//총 합계
        //i는 1부터 10까지 다음 수행문 반복
		for(int i=1; i<=10; i++) {
        	//j는 1부터 i까지 다음 수행문 반복
			for(int j=1; j<=i; j++) {
            	//j의 값이 계속해서 sum에 누계하여 저장
				sum +=j;
				System.out.printf("+%d=%d ",j,sum);
			}
			System.out.println();
		}
<나의 결과>
+1=1 
+1=2 +2=4 
+1=5 +2=7 +3=10 
+1=11 +2=13 +3=16 +4=20 
+1=21 +2=23 +3=26 +4=30 +5=35 
+1=36 +2=38 +3=41 +4=45 +5=50 +6=56 
+1=57 +2=59 +3=62 +4=66 +5=71 +6=77 +7=84 
+1=85 +2=87 +3=90 +4=94 +5=99 +6=105 +7=112 +8=120 
+1=121 +2=123 +3=126 +4=130 +5=135 +6=141 +7=148 +8=156 +9=165 
+1=166 +2=168 +3=171 +4=175 +5=180 +6=186 +7=193 +8=201 +9=210 +10=220 

<모범 풀이>
		int sum = 0;
		int totalSum = 0;
		for(int i=1; i <=10; i++) {
		sum += i;
		totalSum += sum;
		}
		System.out.println("totalSum="+totalSum);

<모범 결과>
totalSum=220		
  • 아쉬운 점 : 너무 과거 풀이에 의존하여 확증편향을 가지고 있었다.
  • 잘한 점 : 접근 방식 자체는 옳았고, 중첩 for문을 이용해 정답을 이끌어냈다.
  • 💡 배울만한 아이디어: 누계 변수를 2개 만들어서 하나(sum)는 for문 변수의 증가를 누계 저장하고, 다른 하나(totalSum)는 그 변수를 또 누계하여 저장하면 결국 점진적 누계 증가이다.

💡4-4. 1+(-2)+3+(-4)+...과 같은 식으로 계속 더해나갔을 때, 몇까지 더해야 총합이 100이상?

  • 문제 접근 : 당연히 식의 반복에서 반복문을 생각해보아야 한다. for문 또는 while문, do while문 등이 가능하다. 홀수와 짝수로 접근을 나눠 생각할 수 있다. 양의 홀수(odd)와 음의 짝수(even)가 누계되는 변수sum가 존재해야 한다. 그러나 1세트로 접근해서는 안된다. 누계된 변수에 양의 정수를 더했을 때 100이 되는 순간, 그 때의 양의 정수를 찾아야 한다. 즉, 이 때의 조건식은 sum<100으로 , sum>=100이 되는 순간 false가 되어 반복문을 끝내야 한다.
  • 문제 접근2 : 반복되는 식의 일반화가 중요하다는 것을 깨달았다. int i가 조건식이고 2씩 증가한다고 가정하자. i가 1일 때 첫 번째 홀수는 i, 첫 번째 짝수는 -(i+1), i가 3일 때 홀수는 i, 짝수는 -(i+1)이므로 규칙을 발견할 수 있다. 조건식이 끝났을 때(sum이 100이상일 때) i값을 확인해주면 되는 것이다. ➡️ ❗ 문제 발견, i의 값이 2씩 증가하면 i번 째를 확인했을 때 그것이 몇 번째 홀수 인지 또한 계산해야 하는 번거로움이 발생한다. ⬇️
  • 문제 접근3: 위의 풀이를 생각해봤을 때, i가 2씩 증가하는 것이 아니라 1씩 증가하는 경우도 생각해보면 어떨까 싶다. i가 1씩 증가한다면 홀수번째에는 +기호를 붙여 odd에 대입하고, 짝수번째에는 -기호를 붙어 even에 대입하는 것이 더 간결하고 좋은 식이지 않은가?
  • 문제 풀이 :
int odd = 0;
			int even = 0;
			int sum = 0;
		
			//
			for(int i=1; sum<100; i++) {
				//짝수인 경우
				if(i%2==0) {
					even = -i;
					sum += even;
					System.out.printf("+(%d) = %d%n", even, sum);
				} 
				//홀수인 경우
				else {
					odd = i;
					sum += odd;
					System.out.printf("+%d = %d%n", odd, sum);
				}
			}
			// for문을 벗어나면 현재 i번째 값과 sum값을 출력한다.
			// i번째 값은 for문 안에서만 통용되나보다. odd값이 곧 i이므로 i 대신 odd를 넣어주자
			System.out.printf("현재 %d째, 총 합계는 %d", odd, sum);
<결과>
...(전략)
+197 = 99
+(-198) = -99
+199 = 100
현재 199, 총 합계는 100
  • 잘한 점 : 처음부터 효율적인 답을 생각해내지는 못했지만, 차근차근 스텝을 밟아가며 오류를 수정했다. (🤪이제 처음인데 이 정도면 잘하는거지~)
  • 못한 점 : 미리 생길 오류를 인식하고 있었음에도(i를 홀짝 세트로 묶으면 안된다는 것) 이를 시도해 보다 복잡한 수식을 만들 뻔 했다.
  • 남궁 성 강사님 모범답안
	int sum = 0; // 총합을 저장할 변수
    int s = 1; // 값의 부호를 바꿔주는데 사용할 변수
    int num = 0;
    // true . 조건식의 값이 이므로 무한반복문이 된다
    for(int i=1;true; i++, s=-s) { // s 1, -1, 1, -1... 매 반복마다 의 값은
    num = s * i; // i (s) . 와 부호 를 곱해서 더할 값을 구한다
    sum += num;
    if(sum >=100) // 100 . 총합이 보다 같거나 크면 반복문을 빠져 나간다
    break;
    }
    System.out.println("num="+num);
    System.out.println("sum="+sum);
  • 💡추후 적용해볼 아이디어 : 부호 변수를 지정하여 반복문에 s=-s로 수행문이 한 번 실행될 때마다 s곱i로 num의 부호를 바꿔 준 것.
  • 나와는 다르게 무한 반복문 안에서 if문으로 끝내는 계산을 하셨다.

💡4-5. for문을 while문으로 바꿔 별피라미드 만들기

<예제>
		for (int i = 0; i <= 10; i++) {
			for (int j = 0; j <= i; j++)
			System.out.print("*");
			System.out.println();
			}
<예제 결과>
*
**
***
****
*****
******
*******
********
*********
**********
***********
  • 풀이 접근 : for 중첩문을 while문으로 바꿀 때 당연히 while 문을 두 번 사용하거나 while과 for문을 혼용하여 사용해야 할 것이다. while을 두 번 사용하기로 하였다.
  • 내 풀이
		int i=0;
		int j=0;
		// i는 0에서 10이 될 때까지 (11번)
		while(i<=10) {
			// j가 i 이하일 때 그만큼 "*"를 찍는 것을 반복한다. 
			while(j<=i) {
				System.out.print("*");
				j++; // j값을 증가시키지 않으면 *찍는게 끝나지 않는다. (처음엔 생략했었음)
			}
			i++; // i값만큼 *을 찍고나면 i를 1 증가시켜야 한다.
			// 별 피라미드 계층을 만드려면 엔터를 작동시켜야 한다. 
			System.out.println();
		}
<결과>
*
*
*
*
*
*
*
*
*
*
*	
  • 풀이과정 중 발생했던 오류: i가 1씩 증가하는 동시에 j도 1씩 증가해버리면 의미없다는 사실을 미처 인지하지 못했다. i와 j가 동시에 1씩 증가한다면 *이 단 한 번씩만 출력될 뿐이다. j가 증가되는 시점 자체는 i값 만큼 *이 프린트 된 다음이라는 것을 명심해야 한다.
  • 💡아이디어 : i와 j가 동시에 증가했던 이유는 while문에는 for문과 달리 초기화문이 없기 때문이다! ➡️ 직접 초기화 해줘야 한다.
  • ✔️ 왜 실수했지? : for문을 생각할 때 초기화문은 가장 처음 실행되고난 이후 의미없어 진다고 생각했다. 그러나 중첩 for문과 같은 경우에는 내부 for문에서 항상 초기화문이 동작하기에 내부 for문을 들어가기 직전에 반드시 변수를 초기화하는 과정을 거쳐야 한다.
  • ✔️ 누계는 차이에서부터 발생하는 것! 무엇과 무엇의 차이가 *을 찍는지 다시 한번 생각해보자!
  • 문제풀이:
		int i=0;
		int j=0;
		// i는 0에서 10이 될 때까지 (11번)
		while(i<=10) {
			// j가 i 이하일 때 그만큼 "*"를 찍는 것을 반복한다. 
			while(j<=i) {
				System.out.print("*");
				j++; // j값을 증가시키지 않으면 *찍는게 끝나지 않는다. (처음엔 생략했었음)
			}
			j=0;	//💡 while과 for문이 다른점!! while은 초기화문이 없어 직접 초기화해줘야함! 
            		// (내부 while문일 경우)
			i++; // i값만큼 *을 찍고나면 i를 1 증가시켜야 한다.
			// 별 피라미드 계층을 만드려면 엔터를 작동시켜야 한다. 
			System.out.println();
		}
 <결과>
*
**
***
****
*****
******
*******
********
*********
**********
***********
  • 모범 답안 :
	int i=0;
    while( i<=10) {
        int j=0;
          while(j<=i) {
          System.out.print("*");
          j++;
          }
        System.out.println();
        i++;
    }

4-6. 두 개의 주사위를 던졌을 때, 눈의 합이 6이 되는 모든 경우의 수를 출력하는 프로그램을 작성하시오.

  • 문제 접근 : 임의의 수 반환은 Math.random()을 이용하는 문제라고 생각한다. 정수의 경우 (int)로 Math.random()을 씌워주고, 요구하는 범위에 따라 적절히 값 조정을 해주면 된다.
  • 문제 접근2 : 하지만 임의의 수 반환이 아닌 것 같다. a는 1~6, b는 1~6이 나오는 두 주사위의 합을 조건식(a+b=6)인 경우에 한해서 a, b를 출력하라는 문제이다. 따라서 반복문(for 또는 while)을 사용한다.
<내 풀이>
		int a; // 주사위 A의 수
		int b; // 주사위 B의 수
		
		for (a=1; a<=6; a++) {
			for (b=1; b<=6; b++) {
				if (a+b==6) {
					System.out.printf("A(%d)+B(%d)=%d%n", a, b, a+b);
				}
			}
		}
<결과>
A(1)+B(5)=6
A(2)+B(4)=6
A(3)+B(3)=6
A(4)+B(2)=6
A(5)+B(1)=6

<모범 답안>
  for(int i=1;i<=6;i++)
    for(int j=1;j<=6;j++)
      if(i+j==6)
      System.out.println(i+"+"+j+"="+(i+j));

<결과>
1+5=6
2+4=6
3+3=6
4+2=6
5+1=6
  • 잘한 점 : 문제의 맥을 정확히 짚어 정확한 답 도출
  • 아쉬운 점 : int a, b는 불필요했다. 반복문 속 조건문 변수를 이용하면 더 간략한 코드를 작성할 수 있다.

💡4-7. 숫자로 이루어진 문자열str이 있을 때, 각 자리의 합을 더한 결과를 출력하는 코드

  • 예시) 문자열이 "12345"라면, '1+2+3+4+5'의 결과인 15가 출력되어야 함.
  • 풀이 접근 : 문자열 str을 int타입으로 바꾸는 방식을 활용해야 할 것 같다. 방법은 Integer.parseInt("")메소드를 활용하는 것이다. 그렇게 반환된 정수를 10으로 나누면 일의 자리가 하나씩 잘린다. 10나머지 연산자(%)를 활용하면 그 잘리는 일의 자리 숫자를 확인할 수 있다. 그것을 int 변수에 저장하여 누계하면 된다.
  • 풀이 :
		String str = "12345";
		int i = Integer.parseInt(str); // str을 정수형으로 바꿔 변수 i에 담아준다.
		
		int a = 0;		//잘리는 일의자리 수 
		int sum = 0;	//잘리는 일의자리 수들의 총계
		int tmp = i;	//계속 잘린 수, 최초에는 i로 시작해야하므로 i로 초기화
		
		//잘리는 수가 0이하가 되면 false로 반복문 종료
		while(tmp>0) {
			a = tmp%10; //잘리는 일의자리 수 담기
			System.out.printf("+%d ",a);	//현재 일의자리 수
			tmp = tmp/10;	//일의자리 수 버리기 (tmp /= 10)이어도 된다.
			sum += a;	//sum은 잘린 일의자리 수들의 누계이다.
		}
		System.out.println();
		System.out.printf("총계=%d",sum);
<결과>
+5 +4 +3 +2 +1 
총계=15
  • 만약 Integer.parseInt("")메소드를 이용할 수 없다는 조건이 있다면?
  • 문제 자체에 조건이 주어져 있었다!
    - Hint) String 클래스의 charAt() 메소드 이용
class Exercise4_7 {
  public static void main(String[] args) {
    String str = "12345";
    int sum = 0;
    
    for (int i = 0; i < str.length(); i++) {
    /*
    (1) 알맞은 코드를 넣어 완성하시오.
    */
    }
    System.out.println("sum=" + sum);
  }
}
  • 다시 풀이
	 	String str = "12345";
		    int sum = 0;
		    
		    for (int i = 0; i < str.length(); i++) {
		    	// 임시 변수 tmp에 str의 i번째 문자-'0'로 숫자로 만든 값을 저장한다.
		    	int tmp = str.charAt(i)-'0';
		    	// sum에 tmp의 값을 누계한다.
		    	sum += tmp;
		    }
		    System.out.println("sum=" + sum);
		  }
<모범 답안>
class Exercise4_7 {
  public static void main(String[] args) {
    String str = "12345";
    int sum = 0;
    for(int i=0; i < str.length(); i++) {
    sum += str.charAt(i) - '0';
    }
    System.out.println("sum="+sum);
  }
}
<결과>
sum=15
  • 💡 아이디어 : str.length()를 조건식으로 이용할 수 있다!
  • 잘한 점 : 다양한 방식으로 정답을 이끌어 냈다.
  • 아쉬운 점 : int tmp는 불필요한 변수였다. sum은 반복문 안에서 초기화 하지 않으므로, sum += str.charAt(i) - '0'를 활용하는 것이 더 간결하다.

4-8. Math.random()을 이용해서 1부터 6 사이의 임의의 정수를 변수 value에 저장하는 코드를 작성

<예제>

      int value = ( /* (1) */ );
      System.out.println("value:"+value);

<풀이>
	   int value = (int) (Math.random()*6)+1;
		System.out.println("value:"+value);
}
  • Math API, 그 중에서도 random()의 개념이 잡혀있는지 물어보는 문제라고 생각한다. random()은 0.0이상 1미만의 실수를 반환한다. 정수로 반환 받기 위해서 해당 수식을 요구하는 범위에 따라 적절하게 곱하고 더하는 것이 필요하다. double타입이 아닌 int 타입으로 변환하게 되면 강제 형변환으로 값 손실이 발생하는 데, 이 점을 이용한다.
  • 풀이 과정은
  1. 각 변에 6을 곱하고,
    0.0 *6 <= Math.random() *6 < 1.0 *6
  2. (int) 형변환이 발생하도록 한 다음
    (int) 0.0 <= (int) (Math.random() *6) < (int) 6.0
  3. +1을 해준다.
    (int) 1.0 <= (int) (Math.random() *6)+1 < (int) 6.0 +1
  4. 완성
    1 <= (int) ((Math.random() *6)+1) < 7

4-9. int 타입의 변수 num이 있을 때, 각 자리의 합을 더한 결과를 출력하는 코드.

  • 만일 변수 num의 값이 12345라면, '1+2+3+4+5'의 결과인 15를 출력.(단, 문자열 변환없이 숫자로만 처리한다.)
  • 풀이 접근 : 해당 문제는 나머지 연산자(%)와 나누기 연산자(/)의 개념을 이해하는 문제이다. int 값 끼리의 계산에서 나누기 연산자(/)는 나머지를 버린다. 따라서 10진수이니 10으로 나누면 해당 숫자의 일의 자리 수가 버려진다. 나머지 연산자(%)는 특정 값으로 나눈 나머지를 반환하는 연산자이다. 10으로 나누면 해당 숫자의 일의 자리 수가 남겨진다.(int값 끼리 계산에서) 해당 숫자들을 저장하여 누계하는 것이 이 문제의 풀이이다.
  • 풀이
<문제>
	int num = 12345;
    int sum = 0;
    /*
    (2) 알맞은 코드를 넣어 완성하시오.
    */
    System.out.println("sum="+sum);
<풀이>
		int num = 12345;
	    int sum = 0;
	    /*
	    (2) 알맞은 코드를 넣어 완성하시오.
	    */
	    
	    // num이 0이 되기 전까지 반복. (num은 정수이므로 0이 최소값이다.)
	    for(int i=1; num>0; i++) {
	    	sum += num%10; //sum은 num의 일의자리 수들의 합
		    num /= 10; //num =num/10 ,num에서 일의자리 수는 계속 잘라 주어야 함.
	    }
	    
	    System.out.println("sum="+sum);
  • 잘한 점 : 불필요한 tmp 변수를 사용하지 않고 풀이했다.
  • 반성할 점: 가장 처음에 조건식을 제대로 세우지 못했다. num이 /10 반복작업하면 0이 나오는데 조건식을 num>=0이라 해버려서 무한반복문이 됐다.

4-10.숫자 맞히기 게임

  • 1과 100 사이의 값을 반복적으로 입력해서 컴퓨터가 생각한 값을 맞히면 게임이 끝난다.
<문제>
// 1~100사이의 임의의 값을 얻어서 answer에 저장한다.
		int answer = /* (1) */ ;
		int input = 0; // 사용자입력을 저장할 공간
		int count = 0; // 시도횟수를 세기위한 변수
		
		// 화면으로 부터 사용자입력을 받기 위해서 Scanner클래스 사용
		java.util.Scanner s = new java.util.Scanner(System.in);
		
		do {
		count++;
		System.out.print("1과 100사이의 값을 입력하세요 :");
		
		input = s.nextInt(); // 입력받은 값을 변수 input에 저장한다.
		/*
		(2) 알맞은 코드를 넣어 완성하시오.
		*/
		} while(true); // 무한반복문
  • 풀이 접근 : do ~ while 문은 조건식과 관계없이 최소 한 번은 수행문을 수행한다. while의 조건식이 true이므로 무한 반복문이다. 무한 반복문은 특정 조건을 만족했을 때 break;로 빠져나오도록 해야한다. 이 문제에서 특정 조건이란 사용자가 입력한 값 input과 1~100 사이의 임의의 정수 anwer이 같을 경우이다.(input==answer) 만약 값이 서로 다르다면 input과 answer를 비교하는 문구를 출력한다음 다시 반복문으로 돌아간다. continue문을 사용하면 이후 코드를 무시하고 반복문으로 돌아가지만 어차피 무한 반복문이므로 if조건문을 잘 사용한다면 continue를 사용할 일도 없을 것이다.
// 1~100사이의 임의의 값을 얻어서 answer에 저장한다.
		int answer = (int)(Math.random()*100) +1 ;
		int input = 0; // 사용자입력을 저장할 공간
		int count = 0; // 시도횟수를 세기위한 변수
		
		// 화면으로 부터 사용자입력을 받기 위해서 Scanner클래스 사용
		java.util.Scanner s = new java.util.Scanner(System.in);
		
		do {
		count++;
		System.out.print("1과 100사이의 값을 입력하세요 :");
		
		input = s.nextInt(); // 입력받은 값을 변수 input에 저장한다.

		if(input==answer) {
			System.out.println("축하합니다. 정답입니다.");
			System.out.printf("시도횟수는 %d번입니다.", count);
			break;	//무한 반복문 빠져나오기
		} else if(input>answer) {
			System.out.println("더 작은 수를 입력하세요.");
		} else	/*(input<answer)*/ {
			System.out.println("더 큰 수를 입력하세요.");
		}
		
		} while(true); // 무한반복문
 <결과>
1100사이의 값을 입력하세요 :50
더 큰 수를 입력하세요.
1100사이의 값을 입력하세요 :75
더 큰 수를 입력하세요.
1100사이의 값을 입력하세요 :85
더 큰 수를 입력하세요.
1100사이의 값을 입력하세요 :92
더 큰 수를 입력하세요.
1100사이의 값을 입력하세요 :96
더 큰 수를 입력하세요.
1100사이의 값을 입력하세요 :99
더 작은 수를 입력하세요.
1100사이의 값을 입력하세요 :97
축하합니다. 정답입니다.
시도횟수는 7번입니다.
  • 잘한 점: 풀이 접근부터 결론까지 정확했다.
  • 못한 점: 초기에 Math.random()메소드에 괄호를 잘못쳐서 answer의 값이 1만 나오는 오류가 있었다.

문제 출처:

자바의 정석 기초편 (남궁 성)

profile
9에서 0으로, 백엔드 개발블로그

0개의 댓글