본문 바로가기

programming/문제 해결

[C언어, Python] 논리 연산자와 자주 발생하는 실수!

반응형

 1. 문제

 

 이 문제는 백준 알고리즘 2941번과 관련된 문제이다. 문제를 보면 알겠지만, 나는 이 문제를 논리 연산자와 반복문 만으로 해결하고자 하였다. 혹시 문제가 궁금하신 분들은 아래의 링크로 들어가면 볼 수 있다.

 

문제: https://apape1225.tistory.com/30

[백준 알고리즘] 2941번 - 크로아티아 알파벳

1. 문제, 예시 시행결과  이번 문제는 나름 시간이 걸린 문제이다. 문법으로 틀린 문제가 아니라, 논리로 우류가 났기 때문에...... 사실 이런 문제도많이 틀려가면서 스스로 부족한 점을 알아가��

apape1225.tistory.com

 보면 알겠지만 논리 연산자에 괄호도 있고...... && 연산자를 맨 뒤에다 쓰고...... 복잡해 보이기는 하지만, 나름 시행착오를 통해 작성한 코드이다. 자세한 설명은 아래에서 하겠다!

 

2. 논리연산자란?

 

 논리 연산자에 대해서 기본적인 개념을 설명하자면 논리에 관한 연산자이다. 당연하게 들리겠지만...... 고등학교 수학을 공부할 때, 다음과 같은 진리표에 대해서 배운적이 있을 것이다. 


 

진리표

 

출처: https://www.scienceall.com/%EC%A7%84%EB%A6%AC%ED%91%9Ctruth-table/

진리표(truth table) | 과학문화포털 사이언스올

진리값표라고도 한다. 진리식(논리식), 논리회로에 대한 연산표를 말한다. 입력가능한 모든 경우를 나열하고 각각의 경우 출력되는 결과를 기록한 표이다. 컴퓨터를 비롯한 디지털 회로에 있어�

www.scienceall.com


 논리합과 논리곱에 대한 개념인데, 이를 구현한 것이 논리 연산자라고 할 수 있다. C언어에서 사용하는 논리합과 논리곱 연산자는 다음과 같다.


논리합: ||

논리곱: &&


 어떤 사람들은 논리합을 OR연산자 논리 곱을 AND연산자라고 말하기도 한다. 실제로 Python에서는 and와 or가 각각 논리곱과 논리합의 역할을 한다. 저 둘의 역할은 질리표와 같다. 양 옆에 참과 거짓을 뜻하는 피연산자들이 존재하고, 그 값들에 대응하는 결과를 반환해 준다. 다음은 예시이다.


참 && 거짓 = 거짓

참 || 거짓 = 참


 C언어에서는 0은 거짓을 의미하고 그 외의 나머지 수는 참을 의미한다. 다음은 이에대한 예시 코드이다.

 

#include <stdio.h>

int main() {
	printf("%d\n", 1 && 1);
	printf("%d\n", 123 && 543);
	printf("%d\n", -1224 && 123);
	printf("%d\n", 1 && 0);
	return 0;
}

 

 위의 코드를 실행해보면 다음과 같은 결과가 나온다.


1

1

1

0


 마지막은 참과 거짓의 논리 곱이기 때문에 거짓을 뜻하는 '0'이 출력되고 나머지는 전부 참이기 때문에 참을 뜻하는 '1'이 출력된다. 가끔가다가 C언어에서 참을 출력할 때 '1'을 출력한다고 '1'만이 참을 의미한다고 생각하는 사람들이 있는데, 0을 제외한 모든 수는 참을 뜻한다는 사실은 유용하게 사용되니(메모리를 할당받을 때) 알아두는 것이 좋다.

 

- 논리 연산자의 특징

 

 여기서 사용하는 논리연산자는 하나의 특징이 있는데, 논리곱과 논리합의 성질을 알면 그 특징을 쉽게 이해할 수 있다. 만약 x를 참일 수도 있고 거짓일 수도 있는 변수라고 생각했을 때, 우리는 x의 값을 몰라도 다음과 같은 식의 답은 쉽게 알 수 있다.


1 || x = ?

0 && x = ?


 첫번째 문제를 보도록 하자, 논리합은 둘중 하나가 참이면 나머지 하나가 어떤 값이던 결과는 참이다. 즉 첫번째 문제의 답은 참이다. 두번째 문제도 비슷하다. 논리곱은 둘중 하나가 거짓이면 어떤 값이 뒤에 오던 결과는 거짓이다. 그렇기 때문에 두번째 문제의 답은 거짓이다.

 

 몇십년 전만해도 컴퓨터는 비싼 장비였다. 메모리도 비쌌고 연산비용도 비쌌다. 때문에 프로그래머들의 골치중 하나는 무조건 연산을 적게하고 메모리도 적게 사용하는 것이었다.(우리 교수님도 그러신다 ㅠㅠ) 아마 이런 이유 때문에 논리연산자의 특징이 생긴게 아닌가 싶다. 특징을 설명하기 전에 다음 코드를 보자.

 

#include <stdio.h>

int main() {
	printf("%d\n", 0 && printf("1"));
	printf("%d\n", 1 || printf("2"));

	return 0;
}

 

 위의 코드를 시행해보면 0과 1 만이 출력된다. 여기서 0은 거짓, 1은 참을 의미한다. 즉 printf("1")과 printf("2")는 실행되지 않았다는 뜻이다.

 

 앞서 말했듯이, 적은 연산을 하는 프로그램은 비용이 적게 들고 이는 프로그래머들의 목적이다. 만약 and연산자에서 앞 부분이 거짓이라면 뒷 부분에 어떤 값이 오던 결과는 거짓이다. 때문에 and연산자의 뒤에 오는 코드를 처리해봤자, 그것은 컴퓨터의 연산자원을 낭비한다는 뜻으로 해석할 수 있다. 따라서 and연산자의 앞 부분이 거짓이면 뒷부분은 신경도 안쓰고 1을 반환한다. 

 

 or연산자도 마찬가지이다. or연산자는 앞부분이 참이면 뒤에 어떤 값이 오던 결과는 참으로 정해져있기 때문에 뒷부분에 대한 연산은 실행하지 않고 1이라는 값을 반환하게 된다. 그리고 이것이 논리연산자의 특징이다.

 

-논리 연산자의 특징으로 인해 발생하는 실수

 

 만약 A는 무조건 참이어야 하고, A가 참일 때, B와 C 둘중 하나가 참이면 전체가 참인 식을 만든다고 가정하자. (말이 복잡해서 죄송합니다 ㅠㅠ) 그럼 다음과 같이 생각할 수 있을 것이다.


A && B || C


 그러나 위의 식은 논리 오류를 범할 수 있다. A가 거짓이어도 C가 참일때를 생각해보자, 컴퓨터는 왼쪽에서 오른쪽으로 논리식을 풀게 된다. 즉 A && B의 값을 먼저 구한다는 뜻이다. 그럼 다음과 같은 형태가 된다.


0 || C


 즉 A가 참이 아니어도 위의 식은 참이 되어버린다. (거짓 || 참)은 참이기 때문이다. 그렇다면 우리는 생각할 수 있을 것이다. " 아 그럼, 필수 조건인 A를 맨 뒤에 and연산자와 함께 넣으면 되겠구나! 이런 생각을 하면 다음과 같이 식을 작성할 수 있을 것이다.


B || C && A


 위와 같은 경우 C가 거짓이고, A가 참이어도 결과적으로는 B && A이기 때문에 거짓이 된다. 그러나 예외는 발생한. B가 참이고 A가 거짓인 경우를 생각해보자. 여기서 위에서 설명한 논리연산자의 특징이 발생한다. 한글로 풀어서 작성하면 다음과 같은 식이 될 것이다.


참 || C && 거짓


 위와 같은 식으로 변하게 되면 결과는 참이 되어버린다. 이유는 앞에서 말했듯이 or연산자의 앞 부분이 참이므로, 뒤에 부분은 무조건 실행하지 않고 결과를 반환하기 때문이다. 위와같은 해결책은 다음과 같이 괄호를 사용하면 된다.


(B || C) && A


 위의 방식으로 식을 작성하면 괄호로 묶인 결과가 A와 같이 && 연산의 피연산자로 작용하게 된다. 즉, B의 값이 참이어도 뒤의 연산을 넘어가지 않고, 다음과 같은 모양으로 연산을 진행하게 된다.


(참) && A


 결과적으로 필요조건인 A까지 검사하여 우리가 원하는 거짓을 반환하게 된다.

 

3. 결어

 

 위의 과정이 내가 문제를 풀면서 겪은 과정이다. 물런 여러 문제들이 발생하겠지만 새로운 문제들은 다시 추가적으로 글을 올릴 것이다. 혹시 위의 글에서 틀린 점이 있다면, 불쌍한 공대새을 구원해주는 셈 치고 댓글이나 메일로 문제를 지적해주었으면 한다 ㅠㅠ 앞으로도 나의 실수가 많아지고, 이를 해결하는과정을 올렸으면 좋겠다.

반응형