[Do it! 알고리즘]01. 기본 알고리즘

업데이트:
21 분 소요

출처 : Do it! 자료구조와 함께 배우는 알고리즘


01-1 알고리즘이란?

알고리즘이란? 문제를 해결하기 위한 것으로, 명확하게 정의되고 순서가 있는 유한 개의 규칙으로 이루어진 집합

  • 아무리 명확하게 알고리즘을 정의해도 변수의 값에 따라 결과가 맞기도 하고 틀리기도 한다면 올바른 알고리즘이라 할 수 없다.

(1) 세 값의 최댓값

세 값의 최댓값 구하기

  • 3개의 정수 가운데 ‘최댓값’을 구하는 프로그램을 작성한다. 변수 a, b, c에 들어가는 값은 키보드에서 입력한 값이고, 그 3개의 값 중 최댓값을 변수 max로 찾는다.
package chapter01;

import java.util.Scanner;

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

    System.out.println("세 정수 중 최댓값 구하기");
    System.out.print("a의 값 : ");
    int a = stdIn.nextInt();
    System.out.print("b의 값 : ");
    int b = stdIn.nextInt();
    System.out.print("c의 값 : ");
    int c = stdIn.nextInt();

    int max = a;
    if (b > max) {
      max = b;
    }
    if (c > max) {
      max = c;
    }

    System.out.printf("최댓값 : %s \n", max);
  }
}
  • System.in : 키보드와 연결된 표준 입력 스트림(standard input stream)
  • stdIn : 키보드와 연결된 표준 입력 스트림(System.in)에서 문자나 숫자를 꺼내는 장치 역할
  • 실행 결과
세 정수 중 최댓값 구하기
a의 값 : 10
b의 값 : 20
c의 값 : 30
최댓값 : 30 
  • 변수 a, b, c의 최댓값을 max로 구하는 과정
    • maxa 값을 넣는다.
    • b 값이 max보다 크면 maxb 값을 넣는다
    • c 값이 max보다 크면 maxc 값을 넣는다.

순차적 구조 & 선택 구조

  • 세 문장이 아래로 나란히 있다면 이 문장은 순서대로 실행된다. 이렇게 여러 문장(프로세스)이 순차적으로 실행되는 구조를 순차적(concatenation) 구조라고 한다.
  • ()안에 있는 식의 평가 결과에 따라 프로그램의 실행 흐름을 변경하는 if문을 선택(selection) 구조라고 한다.

여러 값의 최댓값 구하기

  • 최댓값을 여러 번 반복해서 구하는 경우에는 메서드로 처리하면 편리하다.
package chapter01;

public class ex02 {

  // 3개의 정수 중 최댓값을 구한다.
  static int findMax(int a, int b, int c) {
    int max = a;
    if (b > max) max = b;
    if (c > max) max = c;
    return max;
  }

  public static void main(String[] args) {
    System.out.println("max3(3,2,1) = " + findMax(3,2,1));
    System.out.println("max3(3,2,2) = " + findMax(3,2,2));
    System.out.println("max3(3,1,2) = " + findMax(3,1,2));
    System.out.println("max3(3,2,3) = " + findMax(3,2,3));
    System.out.println("max3(2,1,3) = " + findMax(2,1,3));
    System.out.println("max3(3,3,2) = " + findMax(3,3,2));
    System.out.println("max3(3,3,3) = " + findMax(3,3,3));
    System.out.println("max3(2,2,3) = " + findMax(2,2,3));
    System.out.println("max3(2,3,1) = " + findMax(2,3,1));
    System.out.println("max3(2,3,2) = " + findMax(2,3,2));
    System.out.println("max3(1,3,2) = " + findMax(1,3,2));
    System.out.println("max3(2,3,3) = " + findMax(2,3,3));
    System.out.println("max3(1,2,3) = " + findMax(1,2,3));
  }
}
  • 실행 결과, 모든 조합에 대해 모두 3이 출력되어 최댓값이 바르게 구해진 것을 확인할 수 있다.
max3(3,2,1) = 3
max3(3,2,2) = 3
max3(3,1,2) = 3
max3(3,2,3) = 3
max3(2,1,3) = 3
max3(3,3,2) = 3
max3(3,3,3) = 3
max3(2,2,3) = 3
max3(2,3,1) = 3
max3(2,3,2) = 3
max3(1,3,2) = 3
max3(2,3,3) = 3
max3(1,2,3) = 3

매개변수

  • 메서드를 정의할 때 메서드에 전달되는 값을 저장하기 위해 변수(variable)을 선언하는데, 이를 매개변수(parameter) 또는 형식적 매개변수(formal parameter)라 한다.
  • 형식매개변수를 가인수라 하고 메서드를 호출할 때 사용하는 매개변수 값(value)을 실인수(actual argument)라고 한다.

연습문제

  • Q1) 네 값의 최댓값을 구하는 메서드를 작성하세요.
static int findMax(int a, int b, int c, int d) {
  int max = a;
  if (b > max) max = b;
  if (c > max) max = c;
  if (d > max) max = d;
  return max;
}
  • Q2) 세 값의 최솟값을 구하는 메서드를 작성하세요.
static int findMin(int a, int b, int c) {
  int min = a;
  if (b < min) min = b;
  if (c < min) min = c;
  return min;
}
  • Q3) 네 값의 최솟값을 구하는 메서드를 작성하세요.
static int findMin(int a, int b, int c, int d) {
  int min = a;
  if (b < min) min = b;
  if (c < min) min = c;
  if (d < min) max = d;
  return min;
}

세 값의 중앙값

  • 세 정수의 중앙값을 구하는 findMed 메서드를 정의한다.
package chapter01;

import java.util.Scanner;

public class ex03 {

  static int findMed(int a, int b, int c) {
    if (a >= b) {
      if (b >= c) {
        return b;
      } else if (a <= c) {
        return a;
      } else {
        return c;
      }
    } else if (a > c) {
      return a;
    } else if (b > c) {
      return c;
    } else {
      return b;
    }
  }

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

    System.out.println("세 정수의 중앙값 구하기");
    System.out.print("a의 값 : ");
    int a = stdIn.nextInt();
    System.out.print("b의 값 : ");
    int b = stdIn.nextInt();
    System.out.print("c의 값 : ");
    int c = stdIn.nextInt();

    System.out.println("중앙값 : " +findMed(a, b, c));
  }
}

연습문제

  • Q4) 세 값의 대소 관계 13종류의 모든 조합에 대해 중앙값을 구하여 출력하는 프로그램을 작성하세요.
package chapter01;

import java.util.Scanner;

public class quiz04 {

  static int findMed(int a, int b, int c) {
    if (a >= b) {
      if (b >= c) {
        return b;
      } else if (a <= c) {
        return a;
      } else {
        return c;
      }
    } else if (a > c) {
      return a;
    } else if (b > c) {
      return c;
    } else {
      return b;
    }
  }

	public static void main(String[] args) {
    System.out.println("median3(3,2,1) =" + findMed(3, 2, 1));
    System.out.println("median3(3,2,2) =" + findMed(3, 2, 2));
    System.out.println("median3(3,1,2) =" + findMed(3, 1, 2));
    System.out.println("median3(3,2,3) =" + findMed(3, 2, 3));
    System.out.println("median3(2,1,3) =" + findMed(2, 1, 3));
    System.out.println("median3(3,3,2) =" + findMed(3, 3, 2));
    System.out.println("median3(3,3,3) =" + findMed(3, 3, 3));
    System.out.println("median3(2,2,3) =" + findMed(2, 2, 3));
    System.out.println("median3(2,3,1) =" + findMed(2, 3, 1));
    System.out.println("median3(2,3,2) =" + findMed(2, 3, 2));
    System.out.println("median3(1,3,2) =" + findMed(1, 3, 2));
    System.out.println("median3(2,3,3) =" + findMed(2, 3, 3));
    System.out.println("median3(1,2,3) =" + findMed(1, 2, 3));
  }
}
  • Q5) 중앙값을 구하는 메서드는 다음과 같이 작성할 수도 있다. 그러나 기존에 작성했던 findMed 메서드에 비해 효율이 떨어지는데, 그 이유를 설명하세요.
static int findMed2(int a, int b, int c) {
  if ((b >= a && c <= a) || (b <= a && c >= a))
    return a;
  else if ((a > b && c < b) || (a < b && c > b))
    return b;
  return c;
 }

위의 findMed2 메서드의 경우, 중앙값을 구하기 위해 같은 연산을 중복해서 수행하기도 하고 기존 메서드보다 더 많은 연산을 수행해야 한다.

//	가장 처음의 if문의 판단
//	if ((b >= a && c<= a) || (b <= a && c >= a))
//	에 주목합니다. 
//  여기서 b >= a 및 b <= a의 판단을 뒤집은 판단(실질적으로 같은 판단)을 이어지는 else 이후의 
//	else if ((a > b && c < b) || (b <= a && c > b))
//	으로 수행합니다. 
//  결국 가장 처음의 if가 성립한 경우 2 번째의 if에서도 (실질적으로)같은 판단을 수행하므로 효율이 나빠집니다.

(2) 조건 판단과 분기

if - else if - else

  • 변수 no의 값이 양수인지, 음수인지, 0인지에 따라 한 가지가 실행된다. 두 가지가 동시에 실행되거나 하나도 실행되지 않거나 하는 경우는 없다. 이는 프로그램의 흐름이 세 가지로 분기하기 때문이다.
package chapter01;

import java.util.Scanner;

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

    System.out.println("정수를 입력하세요 : ");
    int no = stdIn.nextInt();

    if (no > 0)
      System.out.println("입력한 숫자는 양수입니다.");
    else if (no < 0)
      System.out.println("입력한 숫자는 음수입니다.");
    else
      System.out.println("입력한 숫자는 0입니다.");
  }
}

if - else if - else if

  • 프로그램의 흐름은 실제로 네 가지로 분기한다.
if (no == 1)
  System.out.println("입력한 숫자는 1입니다.");
else if (no == 2)
  System.out.println("입력한 숫자는 2입니다.");
else if (no == 3)
  System.out.println("입력한 숫자는 3입니다.");

// if - else if - else (프로그램의 흐름이 세 가지로 분기)
// 입력한 값이 1과 2가 아니면 "입력한 숫자는 3입니다."가 출력된다.
if (no == 1)
  System.out.println("입력한 숫자는 1입니다.");
else if (no == 2)
  System.out.println("입력한 숫자는 2입니다.");
else 
  System.out.println("입력한 숫자는 3입니다.");
  • 실제로는 마지막에 공백문이 실행된다.
if (no == 1)
  System.out.println("입력한 숫자는 1입니다.");
else if (no == 2)
  System.out.println("입력한 숫자는 2입니다.");
else if (no == 3)
  System.out.println("입력한 숫자는 3입니다.");
else
	; // 공백문 (실제로 아무것도 하지 않는 문장)

연산자와 피연산자

  • 연산자(operator) : +, - 등의 기호
  • 피연산자(operand) : 연산의 대상이 되는 것
  • 연산자는 피연산자의 수에 따라 3종류로 분류된다.
    • 단항 연산자 : 피연산자 1개. 예) a++
    • 2항 연산자 : 피연산자 2개. 예) a < b
    • 3항 연산자 : 피연산자 3개. 예) a ? b : c
  • 조건 연산자(conditional operator) ? : 는 자바에서 유일한 2항 연산자이다.
    • a ? b : catrue이면 b를 반환하고, false이면 c를 반환한다.

(3) 순서도의 기호

  • 데이터(data) : 데이터의 입력과 출력을 나타낸다.
  • 처리(process) : 여러 종류의 처리 기능을 수행한다. 즉, 정보의 값, 자료형, 위치를 바꾸도록 정의한 연산이나 연산집합의 실행 도는 연속적인 몇 가지 흐름 가운데 하나의 방향을 결정하는 연산집합이나 연산군의 실행을 나타낸다.
  • 미리 정의한 처리(predefined process) : 서브 루틴 및 모듈 등 다른 곳에서 이미 정의한 하나 이상의 연산 도는 명령어들로 이루어진 처리를 나타낸다.
  • 판단(decision) : 하나의 입구와 하나 이상을 선택할 수 있는 출구가 있고, 기호에서 정의한 조건을 평가하여 하나의 출구를 선택하는 판단 기능(스위치형 기능)을 나타낸다. 주로 예상되는 평가 결과의 경로를 선 가까이에 쓴다.
  • 루프 범위(loop limit) : 두 부분으로 구성되어 루프트 시작과 종료를 나타낸다. 기호의 두 부분에는 같은 이름(루프에 대한 임의의 이름)을 사용한다.
  • 선(line) : 제어의 흐름을 나타낸다. 흐름의 방향을 분명히 나타내고자 할 때 화살표를 붙인다. 선은 위에서 아래로 내려가는 것이 원칙이다.
  • 단말(terminator) : 외부 환경으로 나가거나 외부 환경에서 들어오는 것을 나타낸다. 예를 들어 프로그램 흐름의 시작과 종료를 나타낸다.

01-2 반복

(1) 1부터 n까지의 정수 합 구하기

  • 사용자에게 하나의 정수를 입력받아 1부터 그 정수까지의 합을 구하는 프로그램을 작성한다.
package chapter01;

import java.util.Scanner;

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

    System.out.println("1부터 n까지의 합 구하기");
    System.out.print("n의 값 : ");
    int n = stdIn.nextInt();

    int sum = 0;
    int i = 1;

		// 변수 i의 값이 n 이하인 동안
		// i의 값을 1씩 증가하면서 루프 본문을 n회 반복 실행
    while (i <= n) {
      sum += i;
      i++; // 최종 i의 값은 n이 아니라 n+1이다.
    }

    System.out.printf("1부터 %d까지의 합 : %d", n, sum);
  }
}
  • 실행 결과
1부터 n까지의 합 구하기
n의 값 : 10
1부터 10까지의 합 : 55

while문 반복

  • 어떤 조건이 성립하는 동안 처리를 방복하여 실행하는 것을 반복(repetition) 구조라 하며 일반적으로 루프(loop)라고 부른다.
  • while문은 실행전에 반복을 계속할지를 판단하는데, 이를 ‘사전 판단 반복 구조‘라고 부른다. 제어식의 평갓값이 0이 아니면 프로그램 명령문이 반복된다. 반복의 대상이 되는 ‘명령문’을 문법적으로는 루프 본문이라 한다.
while(제어식) 명령문

for문 반복

  • 하나의 변수를 사용하는 반복문은 for문을 사용하는 것이 좋다.
  • 초기화 부분은 for문을 실행학기 전에 한 번만 실행한다. 제어식을 평가한 값이 true이면 for문의 명령문을 반복한다. 명령문을 실행한 다음에는 업데이트 부분을 실행한다.
for(초기화 부분; 제어식; 업데이트 부분) 명령문
  • 1부터 n까지의 정수의 합을 for문으로 구하는 프로그램을 작성한다.
package chapter01;

import java.util.Scanner;

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

    System.out.println("1부터 n까지의 합 구하기");
    System.out.print("n의 값 : ");
    int n = stdIn.nextInt();

    int sum = 0;

    for (int i = 1; i <= n; i++) {
      sum += i;
    }

    System.out.printf("1부터 %d까지의 합 : %d", n, sum);
  }
}
  • 실행 결과
1부터 n까지의 합 구하기
n의 값 : 10
1부터 10까지의 합 : 55

for문 자세히 알아보기

  • for문의 초기화 부분, 제어식, 업데이트 부분은 생략이 가능하다. 세 부분 모두 생략이 가능하지만 세미콜론(;)은 생략하면 안된다.
  • for문의 초기화 부분에서 선언한 변수는 for문 안에서만 유효하다. 따라서 for문을 종료한 다음에도 변수를 사용하려면 for문 바깥 부분에서 변수를 선언해야 한다.
int i;
for (int 1; i <= n; i++) {
	sum += i;
}
  • 같은 함수의 하나 이상의 for문에서 같은 이름의 변수를 사용하려면 각 for문마다 변수를 선언하면 된다.
for (int i = 1; i <= 3; i++) {
	sum += i;
}

for (int i = 1; i <= 5; i++) {
	System.out.println(i);
}

연습문제

  • Q6) ex05while문이 종료될 때 변수 i의 값이 n+1이 됨을 확인하세요(변수 i값을 출력하도록 프로그램을 수정하세요)
package chapter01;

import java.util.Scanner;

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

    System.out.println("1부터 n까지의 합 구하기");
    System.out.print("n의 값 : ");
    int n = stdIn.nextInt();

    int sum = 0;
    int i = 1;

    while (i <= n) {
      sum += i;
      i++;
    }
    
		System.out.println("i = " + i);
    System.out.printf("1부터 %d까지의 합 : %d", n, sum);
  }
}
  • Q7) n7이면 '1 + 2 + 3 + 4+ 5+ 6+ 7 = 28'로 출력하는 프로그램을 작성하세요.
package chapter01;

import java.util.Scanner;

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

    System.out.println("1부터 n까지의 합 구하기");
    System.out.print("n의 값 : ");
    int n = stdIn.nextInt();

    int sum = 0;

    for (int i = 1; i <= n; i++) {
      sum += i;
      if (i < n) {
        System.out.print(i + " + ");
      } else {
        System.out.print(i + " = ");
      }
    }

    System.out.print(sum);
  }
}
  • Q8) 1부터 10까지의 합은 (1+10)*5와 같은 방법으로 구할 수 있습니다. 가우스의 덧셈이라는 방법을 이용하여 1부터 n까지의 정수 합을 구하는 프로그램을 작성하세요.
package chapter01;

import java.util.Scanner;

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

    System.out.println("1부터 n까지의 합 구하기");
    System.out.print("n의 값 : ");
    int n = stdIn.nextInt();

    int sum = (n + 1) * (n / 2) + (n % 2 == 1 ? (n + 1) / 2 : 0);

    System.out.printf("1부터 %d까지의 합 : %d", n, sum);
  }
}
  • Q9) 정수 a, b를 포함하여 그 사이의 모든 정수의 합을 구하여 반환하는 메서드를 작성하세요.
// 나의 풀이
static int sumof(int a, int b) {
  int sum = 0;

  if (a >= b) {
    for (int i = b; i <= a; i++) {
      sum += i;
    }
  } else {
    for (int i = a; i <= b; i++) {
      sum += i;
    }
  }
  return sum;
}

// 해설
static int sumof(int a, int b) {
  int min; // a, b의 작은 쪽의 값
  int max; // a, b의 큰 쪽의 값

	if (a < b) {
		min = a;
		max = b;
	} else {
		min = b;
		max = a;
	}

	int sum = 0; // 합
	for (int i = min; i <= max; i++)
		sum += i;
	return (sum);
}

(2) 양수만 입력하기

  • 1부터 n까지의 정수의 합을 구하는 프로그램을 작성하되, 사용자가 n의 값으로 0 이하의 값을 입력하면 다시 'n의 값 : '이라고 출력되며 사용자에게 다시 입력할 것을 요구하도록 한다.
package chapter01;

import java.util.Scanner;

public class ex06 {
  public static void main(String[] args) {
    Scanner stdIn = new Scanner(System.in);
    int n;

    System.out.println("1부터 n까지의 합 구하기");

    // n이 0보다 클 때까지 반복
    do {
      System.out.print("n의 값 : ");
      n = stdIn.nextInt();
    } while (n <= 0);

    int sum = 0;

    for (int i = 1; i <= n; i++) {
      sum += i;
    }

    System.out.printf("1부터 %d까지의 합 : %d", n, sum);
  }
}
  • 실행 결과
1부터 n까지의 합 구하기
n의 값 : -5
n의 값 : 0
n의 값 : 10
1부터 10까지의 합 : 55

do문

  • do문은 일단 루프 본문을 한 번 실행한 다음에 계속 반복할 것인지를 판단하는 사후 판단 반복문이다. while문과 마찬가지로 () 안의 제어식을 평가한 값이 0이 아니면 루프 본문의 명령문이 반복된다.
do문 while(제어식);
  • 위의 코드에서는 양수만 입력 받기 위해 do문으로 프로그램을 작성했다. 변수 n에 입력한 값이 0이하면 루프 본문이 반복 실행된다. 그러므로 do문이 종료될 때 n의 값은 반드시 양수여야 한다.

사전 판단 반복과 사후 판단 반복의 차이점

  • 사전 판단 반복문인 while문과 for문은 처음에 제어식을 평가한 결과가 0이면 루프 본문은 한 번도 실행되지 않는다.
  • 사후 판단 반복문인 do문은 루프 본문이 반드시 한 번은 실행된다.

연습 문제

  • Q10) 오른쪽과 같이 두 변수 a, b에 정수를 입력하고 b - a를 출력하는 프로그램을 작성하세요. 단, 변수 b에 입력한 값이 a 이하면변 변수 b의 값을 다시 입력하세요.
package chapter01;

import java.util.Scanner;

// 나의 풀이
public class quiz10 {
  public static void main(String[] args) {
    Scanner stdIn = new Scanner(System.in);
    int a, b;

    System.out.print("a의 값 : ");
    a = stdIn.nextInt();

    do {
      System.out.print("b의 값 : ");
      b = stdIn.nextInt();
    } while (b <= a);

    System.out.printf("b - a는 %d입니다.", b - a);
  }
}

// 해설
class Difference_01_10 {
	public static void main(String[] args) {
		Scanner stdIn = new Scanner(System.in);

		System.out.print("a의 값:");
		int a = stdIn.nextInt();

		int b=0;
		while (true) {
			System.out.print("b의 값:");
			b = stdIn.nextInt();
			if (b > a)
				break;
			System.out.println("a보다 큰 값을 입력하세요!");
		}

		System.out.println("b - a는 " + (b - a) + "입니다.");
	}
}
  • Q11) 양의 정수를 입력하고 자릿수를 출력하는 프로그램을 작성하세요. 예를 들어 135를 입력하면 ‘그 수는 3자리입니다.’라고 출력하고, 1314를 입력하면 ‘그 수는 4자리입니다.’라고 출력하면 됩니다.
package chapter01;

import java.util.Scanner;

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

    int n;
    do {
      System.out.print("양의 정수를 입력하세요 > ");
        n = stdIn.nextInt();
    } while (n <= 0);

    int result = 0;
    while (n > 0) {
      n /= 10;
      result++;
    }

    System.out.printf("입력한 수는 %d자리수 입니다.", result);
  }
}

(3) 구조적 프로그래밍

  • 하나의 입구와 하나의 출구를 가진 구성 요소만을 계층적으로 배치하여 프로그램을 구성하는 방법을 구조적 프로그래밍(structured programming)이라고 한다. 구조적 프로그래밍은 순처, 선택, 반복이라는 3종류의 제어 흐름을 사용한다.
  • 사용자가 입력할 수 있는 값을 ‘2자리 양수’로 제한하는 프로그램을 작성한다.
package chapter01;

import java.util.Scanner;

public class ex07 {
  public static void main(String[] args) {
    Scanner stdIn = new Scanner(System.in);
    int n;

    do {
      System.out.print("2자리의 정수를 입력하세요 > ");
        n = stdIn.nextInt();
    } while (n < 10 || n  > 99);
			// 입력한 값이 10보다 작거나 99보다 크면 루프 본문을 반복

    System.out.printf("입력한 수는 %d 입니다.", n);
  }
}
  • 실행 결과
2자리의 정수를 입력하세요 > 1
2자리의 정수를 입력하세요 > 100
2자리의 정수를 입력하세요 > 10
입력한 수는 10 입니다.

논리 연산자의 단축 평가

  • 논리 연산의 식 전체를 평가한 결과가 왼쪽 피연산자의 평가 결과만으로 정확해지는 경우 오른쪽 피연산자의 평가를 수행하지 않는데, 이를 단축 평가(short circuit evaluation)라고 한다.
    • || 연산자의 왼쪽 피연산자를 평가한 값이 true이면 오른쪽 피연산자는 평가하지 않는다.
    • && 연산자의 경우 왼쪽 피연산자를 평가한 값이 false이면 오른쪽 피연산자는 평가하지 않는다.
    • 예) ex07에서 사용자가 입력한 값이 1인 경우 n < 10의 평갓값은 true이므로 오른쪽 n > 99를 평가하지 않아도 제어식 n < 10 || n > 99의 값이 true가 된다.

드모르간 법칙

  • ‘각 조건을 부정하고 논리곱을 논리합으로, 논리합을 논리곱으로 바꾸고 다시 전체를 부정하면 원래의 조건과 같다’라는 법칙을 드모르간 법칙(De Morgan’s laws)이라고 한다.
x && y와 !(!x || !yy)는 같다.
x || y와 !(!x && !y)는 같다.
  • 예) ex07에서 사용자가 입력한 수가 2자리 수인지 검사하는 제어식 (n < 10 || n > 99)는 반복을 계속하는 ‘계속 조건‘인 반면, !(n >= 10 || n <= 99)는 반복을 종료하는 ‘종료 조건‘의 부정이다.

(4) 다중 루프

  • 반복 안에서 다시 반복을 할 수 있다. 반복 루프가 중첩되는 수준에 따라 ‘이중 루프, 삼중 루프’라고 한다.

곱셈표

  • 곱셈표를 출력하는 프로그램을 작성한다.
package chapter01;

public class ex08 {
  public static void main(String[] args) {
    System.out.println("---------- 곱셈표 ----------");

    for (int i = 1; i <= 9; i++) {
      for (int j = 1; j <= 9; j++) {
        System.out.printf("%3d", i * j);
      }
      System.out.println();
    }

  }
}
  • 실행 결과
---------- 곱셈표 ----------
  1  2  3  4  5  6  7  8  9
  2  4  6  8 10 12 14 16 18
  3  6  9 12 15 18 21 24 27
  4  8 12 16 20 24 28 32 36
  5 10 15 20 25 30 35 40 45
  6 12 18 24 30 36 42 48 54
  7 14 21 28 35 42 49 56 63
  8 16 24 32 40 48 56 64 72
  9 18 27 36 45 54 63 72 81
  • 바깥쪽의 for문(행 루프)은 세로 방향에 대한 반복이다. 변수 i의 값을 1부터 9까지 증가시키며, 각각의 반복은 표의 ‘1행, 2행, … 9행’에 해당한다.
  • 안쪽의 for문(열 루프)은 각 행의 가로 방향에 대한 반복이다. 각각의 행에서 변수 j의 값을 1부터 9까지 증가시킨다.
  • 변수 i의 값을 1부터 9까지 증가시키는 ‘행 루프’는 9회 반복되고, 각각의 반복으로 변수 j의 값을 1부터 9까지 증가시키는’열 루프’가 9회 반복된다.
  • 열 루프 종료 후 줄바꿈 문자의 출력은 1부터 9까지 출력을 완료한 행에서 줄을 바꾸어 다음 행을 출력하기 위해 사용한다.
  • 이중 루프는 아래와 같이 처리된다.
i가 1일 때 : j를 1→9로 증가시키면서 1 * j를 출력한다. 그리고 줄 바꿈한다.
i가 2일 때 : j를 1→9로 증가시키면서 2 * j를 출력한다. 그리고 줄 바꿈한다.
i가 3일 때 : j를 1→9로 증가시키면서 3 * j를 출력한다. 그리고 줄 바꿈한다.

...

i가 9일 때 : j를 1→9로 증가시키면서 9 * j를 출력한다. 그리고 줄 바꿈한다.

연습 문제

  • Q12) 위쪽과 왼쪽에 곱하는 수가 있는 곱셈표를 출력하는 프로그램을 작성하세요. 구분선은 기호(|), 마이너스 기호(-), 플러스 기호(+)를 사용하세요.
package chapter01;

public class quiz12 {
  public static void main(String[] args) {

    System.out.print("  |");
    for (int i = 1; i <= 9; i++) {
      System.out.printf("%3d", i);
    }
    System.out.println();
    System.out.println("--+----------------------------");

    for (int i = 1; i <= 9; i++) {
      System.out.printf("%2d|", i);
      for (int j = 1; j <= 9; j++) {
        System.out.printf("%3d", i * j);
      }
      System.out.println();
    }

  }
}
  • Q13) 곱셈이 아니라 덧셈을 출력하는 프로그램을 작성하세요. Q12와 같이 표의 위쪽과 왼쪽에 더하는 수를 출력하세요.
package chapter01;

public class quiz13 {
  public static void main(String[] args) {

    System.out.print("  |");
    for (int i = 1; i <= 9; i++) {
      System.out.printf("%3d", i);
    }
    System.out.println();
    System.out.println("--+----------------------------");

    for (int i = 1; i <= 9; i++) {
      System.out.printf("%2d|", i);
      for (int j = 1; j <= 9; j++) {
        System.out.printf("%3d", i + j);
      }
      System.out.println();
    }

  }
}
  • Q14) 입력한 수를 한 변으로 하는 정사각형을 * 기호로 출력하는 프로그램을 작성하세요.
package chapter01;

import java.util.Scanner;

public class quiz14 {
  public static void main(String[] args) {
    Scanner stdIn = new Scanner(System.in);
    System.out.println("정사각형 출력");
    System.out.print("단 수 : ");
    int n = stdIn.nextInt();

    for (int i = 1; i <= n; i++) {
      for (int j = 1; j <= n; j++) {
        System.out.print("*");
      }
      System.out.println();
    }

  }
}

(5) 직각 이등변 삼각형 출력

  • 이중 루프를 응용하면 기호를 늘어놓아 삼각형이나 사각형 모양으로 출력할 수있다.
  • 사용자가 양의 정수를 입력하면 왼쪽 아래가 직각인 이등변 삼각형을 출력하는 프로그램을 작성한다.
package chapter01;

import java.util.Scanner;

public class ex09 {
  public static void main(String[] args) {
    Scanner stdIn = new Scanner(System.in);
    int n;

    System.out.println("왼쪽 아래가 직각인 이등변 삼각형 출력");

    do {
      System.out.print("몇 단 삼각형입니까? ");
      n = stdIn.nextInt();
    } while(n <= 0);

    for (int i = 1; i <= n; i++) {
      for (int j = 1; j <= i; j++) {
        System.out.print("*");
      }
      System.out.println();
    }

  }
}
  • 실행 결과
왼쪽 아래가 직각인 이등변 삼각형 출력
몇 단 삼각형입니까? 5
*
**
***
****
*****
  • 바깥쪽 for문(행 루프)은 변수 i의 값을 1부터 n, 즉 5까지 증가시킨다. 이것은 삼각형의 각 행에 대응하는 세로 방향 반복이다. 안쪽 for문(열 루프)은 변수 i의 값을 1부터 i까지 증가시키면서 출력한다.
  • 이중 루프는 아래와 같이 처리된다.
i가 1일 때 : j를 1→1로 증가시키면서 *를 출력한다. 그리고 줄 바꿈한다. (*)
i가 2일 때 : j를 1→2로 증가시키면서 *를 출력한다. 그리고 줄 바꿈한다. (**)
i가 3일 때 : j를 1→3로 증가시키면서 *를 출력한다. 그리고 줄 바꿈한다. (***)
i가 4일 때 : j를 1→4로 증가시키면서 *를 출력한다. 그리고 줄 바꿈한다. (****)
i가 5일 때 : j를 1→5로 증가시키면서 *를 출력한다. 그리고 줄 바꿈한다. (*****)

연습 문제

  • Q15) 다양한 이등변 삼각형을 출력하는 부분을 아래와 같은 형식의 메서드로 작성하세요.
static void triangleLB(int n) // 왼쪽 아래가 직각인 이등변 삼각형 출력
static void triangleLU(int n) // 왼쪽 위가 직각인 이등변 삼각형 출력
static void triangleRU(int n) // 오른쪽 위가 직각인 이등변 삼각형 출력
static void triangleRB(int n) // 오른쪽 아래가 직각인 이등변 삼각형 출력
// 왼쪽 아래가 직각인 이등변 삼각형 출력
static void triangleLB(int n) {
  for (int i = 1; i <= n; i++) {
    for (int j = 1; j <= i; j++) {
      System.out.print("*");
    }
	  System.out.println();
	}
}

// 왼쪽 위가 직각인 이등변 삼각형 출력
// 나의 풀이
static void triangleLU(int n) {
  for (int i = n; i <= n && i > 0; i--) {
    for (int j = 1; j <= i ; j++) {
      System.out.print("*");
    }
    System.out.println();
  }
}

// 해설
static void triangleLU(int n) {
	for (int i = 1; i <= n; i++) { // i행 (i = 1, 2, … ,n)
		for (int j = 1; j <= n - i + 1; j++) // n-i+1개의 '*'를 나타냄
			System.out.print('*');
		System.out.println();
	}
}

// 오른쪽 위가 직각인 이등변 삼각형 출력
static void triangleRU(int n) {
  for (int i = 1; i <= n; i++) { // i행 (i = 1, 2, … ,n)
    for (int j = 1; j <= i - 1; j++) // i-1개의 ' '를 나타냄
      System.out.print(' ');
    for (int j = 1; j <= n - i + 1; j++) // n-i+1개의 '*'를 나타냄
      System.out.print('*');
    System.out.println();
  }
}

// 오른쪽 아래가 직각인 이등변 삼각형 출력
// 나의 풀이
static void triangleRB(int n) {
  for (int i = 1; i <= n; i++) {
    for (int j = n; j > i ; j--) {
      System.out.print(" ");
    }
    for (int j = 1; j <= i; j++) {
      System.out.print("*");
    }
    System.out.println();
  }
}

// 해설
static void triangleRB(int n) {
	for (int i = 1; i <= n; i++) { // i행 (i = 1, 2, … ,ln)
		for (int j = 1; j <= n - i; j++) // n-i개의 ' '를 나타냄
			System.out.print(' ');
		for (int j = 1; j <= i; j++) // i개의 '*'를 나타냄
			System.out.print('*');
		System.out.println();
	}
}
  • Q16) n단의 피라미드를 출력하는 메서드를 작성하는데, i행에는 (i-1)*2+1개의 기호 문자 *가 출력되게 하세요.
// 나의 풀이
static void spira(int n) {
  for (int i = 1; i <= n; i++) {
    for (int j = 1; j <= n - i; j++) {
      System.out.print(" ");
    }
    for (int j = 1; j <= 2 * i - 1; j++) {
      System.out.print("*");
    }
    System.out.println();
  }
}

// 해설
static void spira(int n) {
  for (int i = 1; i <= n; i++) { // i행 (i = 1, 2, … ,n)
    for (int j = 1; j <= n - i + 1; j++) // n-i+1개의 ' '를 나타냄
      System.out.print(' ');
    for (int j = 1; j <= (i - 1) * 2 + 1; j++) // (i-1)*2+1개의 '*'를 나타냄
      System.out.print('*');
    System.out.println();
  }
}
  • Q17) 아래를 향한 n단의 숫자 피라미드를 출력하는 메서드를 작성하는데, i행에 출력하는 숫자는 i % 10으로 구하세요.
static void npira(int n) {
  for (int i = 1; i <= n; i++) {
    for (int j = 1; j <= n - i; j++) {
      System.out.print(" ");
    }
    for (int j = 1; j <= 2 * i - 1; j++) {
      System.out.print(i%10);
    }
    System.out.println();
  }
}