-
CS50 Week1: Problem Set, CreditProgramming/CS50 2023. 7. 4. 16:21
하버드 CS50 1주차 Problem Set 과제 Credit 의 풀이 과정을 다룹니다.
C언어로 조건문과 반복문을 사용해 필요한 알고리즘을 구현하고 데이터를 검증해야 합니다.
Luhn's Algorithm을 사용하여 신용카드 번호의 유효성을 검증하고, 복잡한 조건문을 통해 신용카드를 발급한 회사를 파악합니다.Intro
문제 링크에서 자세한 내용을 확인할 수 있습니다.
주어진 신용카드 번호를 바탕으로 카드번호의 유효성 및 신용카드 회사를 판단하는 문제.
$ ./credit Number: 4003600000000014 VISA
Detail
- Input 데이터는 숫자 only, 0으로 시작하지 않음. CS50 내장 라이브러리의
get_long
함수를 사용할것(카드 번호의 길이 때문) - 카드 번호의 유효성은 Luhn's Algorithm 을 사용할 것
- 카드 회사 판별은 다음과 같은 규칙을 따를 것
- AMEX: 15자리 숫자, 34 혹은 37로 시작
- MasterCard: 16자리 숫자, 51, 52, 53, 54, 55로 시작
- VISA: 13 혹은 16자리 숫자, 4로 시작
Issue: 윈도우와 리눅스 환경은 long 타입의 크기가 다르다.
이번 문제는 푸는 게 생각보다 오래 걸렸다.
논리가 이전 문제들보다 복잡해 진 것도 있지만, 문제에서 쥐어준
get_long()
함수가 자꾸 카드 번호를 잘못 읽어오는 문제가 있었다.이것 저것 테스트 해 보니 특히 11자리 이상 숫자를 잘 받지 못했다. 그래서 열심히 구글링을 해보며 이유를 찾았다.
이유를 논하기 전에, 문제 제시문에서 던진 질문에 답해 보자.
But do not assume that the user's input will fit in an
int
! Best to useget_long
from CS50's library to get the user's input.(Why?)이유는
int
타입이 4바이트 자료형이기 때문이다. 최소-2,147,483,648
~2,147,483,647
까지의 값을 표현할 수 있다.
카드 번호는 15자리 이상의 숫자를 받을 수 있기 때문에 쌩 숫자로 해석하면int
타입보다 당연히 더 크다.그래서 8바이트 자료형인
long
타입을 사용하라고 하는 것이다.여기서 문제가 발생한다. 리눅스 기반 환경에서는
long
타입이 8바이트 자료형이지만, 윈도우에서는long
역시 4바이트 자료형이다.
더 큰 숫자를 입력받으려면long long
을 사용해야 한다.원래 CS50 수업은 수업 진행 용 linux 기반 웹 버전 VSCode IDE를 제공하는데, 나는 매번 로그인 하는 게 귀찮아서 컴퓨터(Windows) VSCode에 CS50 라이브러리만 다운받아 쓰고 있었기 때문에 이런 문제가 생겼다.
왼도우에서 리눅스 환경을 구현하는? 방법도 있다고는 하는데 일단은 그냥
get_long_long()
함수를 사용하기로 했다. 이것도 CS50 라이브러리에 포함되어 있다.Code
Functions
이런 저런 작업들을 처리할 함수들을 만들어서 코드를 깔끔하게 정리했다.
카드의 자리수를 확인할 일이 많다. 따라서 10의 거듭제곱을 계산하는 함수를 따로 만들었다.
long long power_of_ten(int n) { long long result = 1; for (int i = 0; i < n; i++) { result *= 10; } return result; }
카드의 자리수가 몇자리인지 계산하는 함수
int count_digits(long long card_num) { int num_digits = 0; while ((card_num / power_of_ten(num_digits)) != 0) { num_digits++; } return num_digits; }
Luhn's algorithm에 따라 카드 번호의 유효성을 검증하는 함수
bool is_valid_luhn_checksum(long long card_num, int num_digits) { int checksum = 0; for (int i = 0; i <= num_digits; i++) { int digit = (card_num / power_of_ten(i)) % 10; // Multiply the digit by 2 if it's on the even position (starting from second-to-last digit) if ((i + 1) % 2 == 0) { int digit_x2 = digit * 2; // Add the individual digits of the product to the checksum checksum += (digit_x2 / 10) + (digit_x2 % 10); } else { // Add the digit to the checksum if it's on the odd position checksum += digit; } } // Card number is valid by the Luhn's Algorithm if sum mod 10 is 0 return (checksum % 10 == 0); }
카드 번호 앞 두자리만 반환해주는 함수. 이건 코드도 짧고 간단한데 함수로 만들어주니까 코드 파악이 편해지는 것 같아서 만들어 봤다.
int get_first_two(long long card_num, int num_digits) { return (card_num / power_of_ten(num_digits - 2)); }
Full Code
#include "cs50.h" #include <stdio.h> #include <stdbool.h> long long power_of_ten(int n); int count_digits(long long card_num); bool is_valid_luhn_checksum(long long card_num, int num_digits); int get_first_two(long long card_num, int num_digits); int main(void) { long long card_num = get_long_long("Number: "); int num_digits = count_digits(card_num); if (is_valid_luhn_checksum(card_num, num_digits)) { int first_two_digits = get_first_two(card_num, num_digits); if ((num_digits == 15) && (first_two_digits == 34 || first_two_digits == 37)) { printf("AMEX\n"); } else if ((num_digits == 16) && (first_two_digits >= 51 && first_two_digits <= 55)) { printf("MASTERCARD\n"); } else if ((num_digits == 13 || num_digits == 16) && (first_two_digits >= 40 && first_two_digits <= 49)) { printf("VISA\n"); } else { printf("INVALID\n"); } } else { printf("INVALID\n"); } }
- Input 데이터는 숫자 only, 0으로 시작하지 않음. CS50 내장 라이브러리의