안경잡이개발자

728x90
반응형

  커널(Kernel)운영체제(OS)에서 가장 코어(핵심) 파트에 해당합니다. 커널은 컴퓨터의 각종 드라이버, 메모리, 프로세스 등을 관리하는 역할을 수행합니다. 일반적으로 배포판으로 제공되는 커널은 이미 일반적인 많은 기능을 포함하고 있기 때문에 일반적인 리눅스 OS 사용자는 커널을 직접 컴파일할 필요가 없습니다.

 

  그래서 어떤 하드웨어를 사용할 때, 우리는 적절한 커널 이미지가 존재하는지 먼저 찾아본 뒤에 이를 그대로 이용합니다. 예를 들어 라즈베리파이(Raspberry Pi)Debian 기반의 OS를 사용할 수 있는데, 라즈베리파이의 부품과 적절히 호환될 수 있는 형태로 커널이 제공됩니다. 정확히는 라즈비안(Raspbian)이라는 이름의 별도의 라즈베리 파이 버전의 OS를 사용하면 됩니다.

 

  하지만 기본적인 커널에서 제공하지 않는 기능을 사용하고 싶을 때나 컴퓨터 부품과 관련하여 바뀐 부분이 있을 때 커널을 변경하여 컴파일해 볼 필요가 있습니다. 혹은 현재 보드에서는 필요 없는 드라이버를 지워서 커널을 최적화하여 부팅 시간을 빠르게 설정할 수 있습니다. 예를 들어 특정한 하드웨어를 관리하기 위한 디바이스 드라이버(device driver)를 작성할 때 커널 모듈 형태로 작성할 수 있습니다. 커널을 변경하기 위한 가장 첫 번째 단계는 커널 소스 코드를 직접 컴파일해 보는 것입니다.

※ 커널의 주요 디렉토리 ※

  ▶ linux/kernel: 커널 핵심 소스 코드

  ▶ linux/include: 커널 소스 코드의 헤더 파일
  ▶ linux/lib: 커널 내부에서 사용되는 함수 라이브러리
  ▶ linux/drivers: 하드웨어 관리 목적의 디바이스 드라이버

 

  기본적으로 최신 버전의 리눅스 커널은 www.kernel.org 경로에서 받을 수 있습니다. 당연히 커널을 컴파일하기 위해서는 컴파일하고자 하는 소스 코드가 필요하기 때문에 이는 기본적인 과정입니다. 2019년 11월 25일에 리눅스 커널(Linux Kernel) 5.4 버전이 출시되었습니다. 2020년 10월 기준으로 Raspberry Pi의 리눅스 커널 기본 버전도 5.4 버전입니다.

 

  라즈비안(Raspbian) 커널(Kernel) 빌드 가이드라인을 보면서 그대로 따라 진행하면 큰 문제 없이 진행할 수 있습니다. 저는 이 중에서 크로스 컴파일(cross compile) 방식을 이용해 실습을 진행했습니다.

 

※ Raspberry Pi 커널 크로스 컴파일 방법

  일반적으로 크로스 컴파일(cross compile)특정한 OS에서 컴파일한 프로그램이 다른 OS에서 돌아가도록 하는 것을 의미합니다. Raspbian은 Ubuntu와 같은 일반적인 리눅스 OS와는 차이가 있습니다. 따라서 Ubuntu OS에서 Raspbian을 컴파일하는 것을 의미한다. 외부 리눅스(Linux) OS 기반의 컴퓨터를 하나 준비합니. 아무튼 별도의 호스트(Host) PC에서 커널을 빌드한 뒤에 Raspberry Pi에 업로드하면 됩니다.

1. 관련 종속성(Dependencies) 및 툴체인(Toolchain) 설치하기

 

sudo apt install git bc bison flex libssl-dev make libc6-dev libncurses5-dev

 

  제 Ubuntu 컴퓨터에는 관련 종속성 라이브러리가 이미 설치되어 있는 상황이라서 다음과 같이 나옵니다.

 

 

2. 32-bit 커널을 위해 32-bit 툴체인을 설치하기

  라즈비안(Raspbian) OS의 경우 기본적으로 32-bit입니다. 따라서 본 예시에서도 32-bit을 이용하겠습니다.

 

sudo apt install crossbuild-essential-armhf

 

  32-bit 툴체인도 이미 설치가 되어있어서 다음과 같이 나오는 것을 확인할 수 있습니다.

 

 

3. 소스 코드 다운로드 받기

 

  이제 라즈비안(Raspbian) OS 소스 코드를 다운로드 받겠습니다.

 

git clone --depth=1 https://github.com/raspberrypi/linux

 

  소스 코드를 확인해 보면 기본적으로 일반적인 Linux와 매우 유사한 구조를 가지고 있습니다. 마찬가지로 kernel 디렉토리, drivers 디렉토리, security 디렉토리 등이 있습니다.

 

 

  실제로 각각의 디렉토리를 직접 확인해 보실 수 있습니다. 예를 들어 각종 드라이버(driver) 관련 소스코드를 확인하고 싶다면 다음의 경로에 들어가시면 됩니다.

 

 

  저는 여기에서 제가 만든 커널이 정상적으로 동작하는지 확인하기 위해 드라이버 코드를 아주 조금 바꾸어 보겠습니다. drivers/usb/gadget/function/f_mass_storage.c는 Mass Stoarge 기능(function)을 위한 소스코드 구현부입니다. 저는 다음과 같이 printk() 명령을 이용하여 간단한 로그를 남길 수 있도록 했습니다.

 

 

4. 소스코드 빌드하기

  Raspberry Pi Zero W의 경우 다음과 같은 명령어로 config 파일을 만들 수 있습니다. 

 

cd linux
KERNEL=kernel
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- bcmrpi_defconfig

 

 

  이후에 실제로 환경설정 파일을 토대로 빌드를 진행합니다. 말 그대로 커널 소스코드를 새롭게 빌드할 수 있는 것입니다. 예를 들어 우리가 직접 새로운 드라이버를 작성하고 싶다면, 드라이버 쪽 코드를 고친 뒤에 커널 소스코드를 빌드하면 그 내용이 적용되는 것입니다.

 

make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- zImage modules dtbs

 

  명령어를 입력하면 다음과 같이 실질적으로 빌드가 진행됩니다. 컴퓨터의 성능마다 다를 수 있지만 수십 분 정도의 시간이 소요될 수 있습니다. 한 번 빌드를 수행한 뒤에 코드를 고치고 다시 빌드하는 경우에는 빠릅니다.

 

 

5. SD 카드에 인스톨하기

  커널을 빌드한 후에는 현재 가지고 있는 라즈베리파이(Raspberry Pi)커널을 복사해 모듈을 설치해야 합니다. 일반적으로 Raspberry Pi OS가 설치되어 있는 SD Card를 넣으면 다음과 같은 형태가 됩니다. sdb1FAT 파티션, sdb2ext4 파일시스템(root) 파티션을 의미합니다.

 

sdb
    sdb1
    sdb2

 

  실제 디바이스 이름은 fdisk -l 명령을 이용하여 확인할 수 있도록 합니다.

 

 

  확인해보니 제 SD 카드는 sde라는 이름을 가지네요. 따라서 차례대로 FAT과 ext4 파티션을 각각 마운트합니다.

 

mkdir mnt
mkdir mnt/fat32
mkdir mnt/ext4
sudo mount /dev/sde1 mnt/fat32
sudo mount /dev/sde2 mnt/ext4

 

  다음과 같이 별도로 mnt 라는 이름의 폴더를 만들어서 마운트를 진행할 수 있습니다.

 

 

  이후에 df -h 명령을 이용해 용량이 충분한지 체크를 진행해 보겠습니다. 용량은 충분하네요.

 

 

  이제 커널 모듈SD 카드에 설치하면 됩니다. (32-bit) 여기에서 나오는 modules_install은 말 그대로 커널 모듈을 설치하는 명령입니다.

 

sudo env PATH=$PATH make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- INSTALL_MOD_PATH=mnt/ext4 modules_install

 

  만들어진 커널 이미지를 SD 카드에 복사할 겁니다. 아무래도 커널 이미지가 정상적으로 동작하지 않을 수도 있으므로 현재 존재하는 커널 이미지를 미리 백업해 둘 필요가 있습니다. (32-bit 기준)

 

sudo cp mnt/fat32/$KERNEL.img mnt/fat32/$KERNEL-backup.img

 

  주피터 노트북의 경우 환경 변수가 제대로 동작하지 않을 수 있습니다.

 

 

  이후에 SD 카드의 mnt 폴더로 만들어진 커널 이미지를 옮깁니다.

 

sudo cp arch/arm/boot/zImage mnt/fat32/$KERNEL.img
sudo cp arch/arm/boot/dts/*.dtb mnt/fat32/
sudo cp arch/arm/boot/dts/overlays/*.dtb* mnt/fat32/overlays/
sudo cp arch/arm/boot/dts/overlays/README mnt/fat32/overlays/

 

 

  이후에 언마운트를 진행합니다.

sudo umount mnt/fat32 
sudo umount mnt/ext4

 

 

  이제 SD 카드를 뽑은 뒤에 Raspberry Pi에 꽂아서 부팅합니다. 저는 부팅 이후에 Multi Gadget Driver를 사용하도록 하고, Mass Storage 기능을 활성화하도록 설정했습니다. 이후에 dmesg를 입력했을 때 다음과 같이 로그(log)가 찍혀 있는 것을 확인할 수 있었습니다. 다시 말해 정상적으로 커널 컴파일 및 업로드 완료 된 것입니다.

 

 

※ 커널 소스코드 수정 이후에 다시 빌드하는 방법 ※

 

  결과적으로 커널 소스코드를 수정한 뒤에, 다시 빌드하는 명령어를 요약하자면 다음과 같습니다.

 

make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- zImage modules dtbs -j8

mkdir mnt
mkdir mnt/fat32
mkdir mnt/ext4
sudo mount /dev/sde1 mnt/fat32
sudo mount /dev/sde2 mnt/ext4

sudo env PATH=$PATH make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- INSTALL_MOD_PATH=mnt/ext4 modules_install
sudo cp mnt/fat32/kernel.img mnt/fat32/kernel-backup.img

sudo cp arch/arm/boot/zImage mnt/fat32/kernel.img
sudo cp arch/arm/boot/dts/*.dtb mnt/fat32/
sudo cp arch/arm/boot/dts/overlays/*.dtb* mnt/fat32/overlays/
sudo cp arch/arm/boot/dts/overlays/README mnt/fat32/overlays/

sudo umount mnt/fat32 
sudo umount mnt/ext4

sync
sync
728x90
반응형

728x90
반응형

  이미지넷(ImageNet) 데이터셋은 MNIST, CIFAR 데이터셋과 더불어 굉장히 유명한 데이터셋이다. 일반적으로 MNIST나 CIFAR는 아이디어에 대한 검증 목적으로 사용한다. 최신 컴퓨팅 장치를 기준으로 보았을 때 데이터셋 자체가 아주 작기 때문에, 어지간한 크기의 뉴럴 네트워크를 이용해도 학습 과정에서 길어야 하루 이내의 시간이 소요되기 때문이다.

 

  ▶ MNIST: 0부터 9까지의 28 x 28 손글씨 사진을 모은 데이터셋 (학습용: 60,000개 / 테스트용: 10,000)

  ▶ CIFAR-10: 10개의 클래스로 구분된 32 x 32 사물 사진을 모은 데이터셋 (학습용: 50,000개 / 테스트용: 10,000개)

 

  다양한 논문에서는 새로운 아이디어에 대한 검증을 위한 목적으로 간단히 MNIST나 CIFAR-10을 이용하곤 한다. 이때 ImageNet을 학습시키기 버거운 경우 CIFAR-100 정도까지의 데이터셋을 이용할 수도 있다. 실제로 대학원 수업 때 과제 목적으로 가장 많이 사용하는 것도 MNIST나 CIFAR-10이다.

 

  반면에 이미지넷(ImageNet)대표적인 대규모(large-scale) 데이터셋이다. 전체 데이터셋에 포함된 이미지만 해도 1,000만 개가 넘는다. 유명한 Amazon Mechanical Turk 서비스를 이용하여 일일이 사람이 분류한 데이터셋이다. 이 데이터셋은 ILSVRC (ImageNet Large Scale Visual Recognition Challenge)로 잘 알려진 국제 대회에서 사용되는 데이터셋으로도 유명하다. 논문에서 가장 자주 등장하는 데이터셋은 ILSVRC 2012 데이터셋이다. 최신 논문도 대개 ILSVRC 2012를 이용해 학습/평가를 진행한다.

 

  사실 일반적으로 딥러닝에 사용되는 데이터셋은 평가(training) / 검증(validation) / 테스트(test) 데이터셋으로 나누어지는데, ILSVRC 2012는 실제 대회에서 사용되었던 데이터셋이며 테스트 데이터셋은 공개하지 않고 있다. 그래서 대개 논문을 쓰기 위한 목적으로는 평가(training) 데이터셋과 검증(validation) 데이터셋을 사용한다.

 

  이 데이터셋은 1,000개의 클래스로 구성되며 총 백만 개가 넘는 데이터를 포함한다. 약 120만 개는 학습(training)에 쓰고, 5만개는 검증(validation)에 쓴다. 학습 데이터셋 용량은 약 138GB, 검증 데이터셋 용량은 약 6GB이다. 특히 분류(classification) 문제에 관심이 있는 딥러닝 연구자라면 대개 이미지넷 데이터셋을 다운로드하는 편이다. 학습 데이터를 확인해 보면 각 클래스당 약 1,000개가량의 사진으로 구성되어 있다.

 

  전체 레이블(label) 이름을 확인하고 싶다면 다음과 같이 이미지넷(ImageNet) 클래스 정보가 기록되어 있는 JSON 파일을 확인하면 된다. 이 경로에서 전체 레이블 정보를 확인할 수 있다. 실제로 파이썬 프로그램상으로도 이 경로에서 JSON 파일을 받아와 사용하는 식으로 사용하곤 한다.

 

 

  ILSVRC 2012를 다운로드하는 방법은 다양하다. 대표적으로 토렌트(torrent)를 이용해서 받을 수 있다. 다음과 같이 학습 데이터셋과 검증(validation) 데이터셋을 다운로드할 수 있고, 이를 이용해 실험을 진행할 수 있다.

 

 

  예를 들어 Validation Set을 다운로드한다면, 다음과 같이 해당 링크로 접속한 뒤에 [Download] 버튼을 눌러서 토렌트(Torrent) 파일을 다운로드 하면 된다. 컴퓨터에 유토렌트(uTorrent)와 같은 프로그램이 설치되어 있다면, 곧바로 다운로드를 진행할 수 있다.

 

 

  실제로 필자는 adversarial attack 관련 연구를 진행하고 있어서 검증(validation) 데이터셋만 있으면 되는 상황이다. 이럴 때는 ILSVRC 2012 Validation Set을 다운로드받으면 된다. 실제로 토렌트를 이용해 받으면 대략 1MB/s ~ 8MB/s 사이의 속도로 다운로드가 진행된다. 데이터셋의 용량이 크기 때문에 넉넉히 기다리는 것이 좋다.

 

 

  필자는 윈도우 환경에서 다운로드를 진행했다. 검증(validation) 데이터셋을 다운로드한 뒤에는 압축 파일을 확인해 보자. 총 50,000개의 데이터가 존재하는 것을 확인할 수 있다. 확인해 보면 단순히 이미지 파일만 존재하는 것을 알 수 있다. 따라서 약간의 전처리가 필요하다.

 

 

  압축을 풀어보자. 그러면 다음과 같은 전체 검증용 사진을 확인할 수 있다.

 

 

  이미 다른 사람이 검증(validation)용 데이터를 정리하는 방법을 제안하고 있다. 이 경로에서 쉘 스크립트를 다운로드하자. 쉘 스크립트(.sh)를 실행하면 모든 이미지를 폴더별로 정리한다. 가장 먼저 압축 해제한 폴더에 스크립트 파일이 저장될 수 있도록 하자. 필자의 경우 script.sh 라는 이름으로 스크립트 파일을 만들었다. 참고로 윈도우(Windows) OS에서 쉘 스크립트를 실행하려면 bash 모드로 들어간 뒤에 리눅스 명령을 입력하면 된다. 다음과 같이 스크립트를 실행하자. 스크립트를 실행하면 모든 이미지가 처리될 때까지 최대 2시간까지의 시간이 소요될 수 있다.

 

 

  쉘 스크립트가 완전히 실행된 뒤에는 다음과 같이 50,000개의 검증용 데이터셋이 총 1,000개의 클래스(폴더)에 분리되어 저장된다. 폴더 번호가 낮은 것부터 차례대로 인덱스가 부여되는 것이다. 다만 폴더의 이름이 약간 어색하게 느껴질 수 있다. 폴더명에 따른 실제 클래스 이름은 다음의 경로에 기록되어 있다. 실제로 이 JSON 파일을 이용하여 구분할 수 있다. 예를 들어 19번째 폴더인 n01582220 폴더는 까치(magpie)를 의미한다.

 

 

  더불어 혹시나 n으로 시작하지 않는 폴더가 존재한다면 해당 폴더는 제거할 수 있도록 하자. 파이썬 상에서 실제로 학습 및 평가를 진행할 때에도 이러한 폴더 구조를 그대로 따르기 때문에, 규칙에 맞지 않는 폴더가 존재한다면 오류가 발생할 수 있다. 필자의 경우 script.sh 파일이 남아 있어서 이를 삭제했다.

 

 

  또한 참고로 필자의 경우 각 클래스(class)마다 5개의 validation 이미지가 필요했다. 따라서 다음과 같은 코드를 추가적으로 실행했다. 이러한 코드를 실행하면 각 클래스마다 이미지를 5개만 남기고, 폴더명을 차례대로 0부터 999까지 할당해준다.

 

import os
import json


def get_list(path):
    return os.listdir(path)


def rename_directory(root_path, directory, targets):
    source = os.path.join(root_path, directory)
    target = os.path.join(root_path, targets[directory])
    os.rename(source, target)


def remove_files(root_path, directory, n_remained):
    files = get_list(os.path.join(root_path, directory))
    files = files[n_remained:]
    for file_name in files:
        os.remove(os.path.join(root_path, directory, file_name))


# Run the program
root_path = './ILSVRC2012_img_val'
with open('./imagenet_class_index.json') as f:
    json_data = json.load(f)
targets = {}
for key in json_data.keys():
    targets[json_data[key][0]] = key

directories = get_list(root_path)
for directory in directories:
    remove_files(root_path, directory, 5)
for directory in directories:
    rename_directory(root_path, directory, targets)

 

  필자는 결과적으로 다음과 같은 데이터를 얻을 수 있었다.

 

728x90
반응형

728x90
반응형

  Xmanager를 사용하고 있는 상황에서 어느 날부터 모르는 임의의 호스트가 연결을 시도한다. 이는 해킹 시도인데, 그냥 인증되지 않은 클라이언트로부터의 연결은 무시하도록 하는 것이 좋다.

 

 

  Xconfig를 실행하여 XDMCP 부분을 더블 클릭한다.

 

 

  이후에 [보안] 탭에서 [허가되지 않은 연결 경고(W)]를 체크 해제하자. 그러면 신뢰할 수 있는 호스트가 아니라면 접근 제어가 작동하는 모든 X 응용프로그램의 접속은 거부된다.

 

 

  ▶ 출처

728x90
반응형

728x90
반응형

  Cisco 회사의 웹엑스(WebEx)화상회의 목적의 소프트웨어다. 화상회의 소프트웨어로 유명한 줌(Zoom)과 자주 비교되는 프로그램이기도 하며, 널리 사용되고 있는 프로그램이다. 필자도 강의 목적으로 웹엑스(WebEx)를 사용해 본 적이 있다. 또한 무료 버전을 사용해도 최대 100명이 참가할 수 있고, 최대 50분 동안 지속이 가능하기 때문에 일반적으로 학생들이 팀 미팅 목적으로도 웹엑스를 매우 효과적으로 사용할 수 있다.

 

  ▶ 웹엑스(WebEx) 다운로드: www.webex.com/downloads.html

 

  웹엑스는 공식 다운로드 사이트에 접속하여 다운로드할 수 있다. 일반적인 경우 웹엑스 미팅(WebEx Meetings) 프로그램을 사용한다. 접속하면 자동으로 자신의 운영체제 맞는 설치 프로그램을 추천해준다. 필자의 경우 윈도우(Windows) 운영체제를 쓰고 있기 때문에, 다음과 같이 [Download for Windows] 버튼이 등장한다. 이를 눌러 다운로드를 진행할 수 있다.

 

 

  설치 프로그램이 실행되면 [다음] 버튼을 눌러서 다운로드를 진행한다.

 

 

    라이센스에 동의하고 넘어간다.

 

 

  이후에 [설치] 버튼을 눌러서 실제로 프로그램 설치를 진행한다.

 

 

  그러면 다음과 같이 실제로 설치가 진행된다.

 

 

  설치가 모두 이루어진 이후에는 [완료] 버튼을 눌러 프로그램을 닫아준다.

 

 

  설치가 끝나면 다음과 같이 로그인 이후에 사용이 가능하다.

 

 

※ 호스트 운영 관련 팁 ※

 

  호스트(Host)는 방을 만들어 운영하는 사람을 의미한다. 호스트 입장에서 방을 개설한 뒤에 미팅을 진행할 때는 몇 가지 알아 두면 좋은 팁이 있다. 미팅이 시작된 이후에 [참가자] 탭에서 [참여시 자동 음소거]에 체크를 진행하는 것이 좋다. (특히 강의 목적으로 사용할 때) 그렇지 않으면 마이크가 있는 사용자들은 자동으로 목소리가 들리게 된다. 그리고 [들어올 때와 나갈 때 발신음]체크 해제를 하도록 하자. 미팅 진행 중에 발신음이 들리면 신경 쓰일 수 있기 때문이다.

 

 

  또한 미팅 진행 중에 화면을 공유하고 싶다면 [공유] 버튼을 눌러서 화면 공유를 진행할 수 있다.

 

 

  이후에 참가자 명단채팅창 내용을 보고 싶다면 오른쪽 아래 버튼을 클릭하면 된다.

 

728x90
반응형

728x90
반응형

  일반적으로 노트북이나 컴퓨터를 이용해 와이파이(WiFi)를 이용하곤 한다. 하지만 예전에 와이파이를 사용할 때 입력했던 비밀번호를 까먹었다면 어떻게 해야 할까? 이번 포스팅에서는 netsh 유틸리티(utility)를 이용해 내 컴퓨터에 저장된 와이파이로부터 비밀번호를 찾는 방법을 알아보자.

 

  윈도우(Windows) 운영체제는 기본적으로 netsh 유틸리티(utility)를 제공한다. 이러한 netsh 명령어를 이용해 네트워크 설정을 변경할 수 있다. 굳이 제어판에 접속하지 않아도 커맨드 명령을 이용해 네트워크 설정이 가능하다는 점은 장점이다. netsh 유틸리티는 현재 컴퓨터나 노트북에 기록되어 있는 와이파이 정보를 보여준다.

 

  CMD에서 netsh wlan show profile을 입력해보자. 참고로 WLAN이란 무선 랜(Wireless LAN)을 의미한다. 다시 말해 현재 저장된 와이파이(WiFi)의 프로필들을 모두 출력하게 된다. 실행 결과는 다음과 같다.

 

 

  이때 SSIDService Set IDentifier의 약자로, 간단히 말하면 와이파이 네트워크의 이름을 의미한다. 자신이 비밀번호를 알고자 하는 와이파이의 SSID를 입력하여 비밀번호를 찾을 수 있다. 명령어는 다음과 같다.

 

  netsh wlan show profile "SSID 이름" key=clear

 

  예를 들어 SSID가 HPC_WLAN이라면 다음 그림과 같이 입력하면 된다.

 

 

  이때 출력되는 정보 중에서 [보안 설정] 탭의 키 콘텐츠 부분에서 비밀번호를 찾을 수 있다.

 

728x90
반응형

728x90
반응형

  백준 사이트에서 문제 난이도 보는 방법은 다음과 같습니다. 먼저 [설정] 탭으로 이동합니다.

 

 

  이후에 [solved.ac] 탭에서 [사용하기] 버튼을 눌러서 백준 온라인 저지에서 문제에 대한 난이도를 바로 확인 가능합니다.

 

 

  이어서 [문제 보기 설정] 탭으로 이동하여 solved.ac 티어란에서 [보기]를 체크하면 됩니다.

 

 

  또한 유저별로 랭킹을 보기 위해서는 solved.ac에 방문하시면 됩니다.

 

 

solved.ac

우리 모두가 만들어가는알고리즘 문제해결 학습의 이정표 solved.ac는 Baekjoon Online Judge 문제들에 태그와 난이도를 붙이는 커뮤니티 프로젝트입니다. 현재 10,475개 문제에 난이도 정보를 제공하고 �

solved.ac

 

  검색 버튼을 누른 뒤에 유저, 문제, 태그 등의 키워드로 검색할 수 있습니다.

 

 

 

  저는 다음과 같이 제가 출제했던 [함께 블록 쌓기] 문제를 검색해 보았습니다.

 

 

  검색 이후에 해당 문제로 들어가서 문제를 확인할 수 있습니다.

 

728x90
반응형

728x90
반응형

피보나치 수: 0 1 1 2 3 5 8 13 21 ...

 

<문제> 피보나치 수가 n 자릿수 이상이 되기 위한 가장 작은 인덱스를 구해보자. 예를 들어 n = 1이면 답은 0이다. n = 2이면 답은 7이다.

 

<답안 예시>

 

def fibonacciIndex(n):
    result = 0
    if n == 1:
        return result

    a = 0
    b = 1
    while a < (10 ** (n - 1)):
        c = a + b
        a = b
        b = c
        result += 1

    return result
728x90
반응형

728x90
반응형

  아래 그래프에는 3개의 연결 요소가 있다.

 

 

  연결 요소의 개수를 세는 코드는 다음과 같다.

 

n = 6
m = 4
# 그래프 정보 초기화
graph = [
    [1, 2],
    [0, 2],
    [0, 1],
    [4],
    [3],
    []
]
# 각 노드의 방문 여부
visited = [False] * n

# 특정 노드에서부터 연결된 모든 노드를 방문 처리
def dfs(x):
    visited[x] = True
    # 현재 노드와 인접한 노드 또한 방문 처리
    for i in graph[x]:
        if not visited[i]:
            dfs(i)

result = 0
for i in range(n):
    # 아직 방문하지 않았다면 방문
    if not visited[i]:
        dfs(i)
        result += 1
print(result)
728x90
반응형

728x90
반응형

  어도비 프리미어(Premiere)에서 특정 시간 범위만 동영상으로 내보내는 방법은 간단하다. 가장 먼저 메뉴 아이콘을 클릭해서 [작업 영역 막대]를 누르자.

 

 

  이후에 다음과 같이 작업 영역 막대가 보이게 된다. 이 작업 영역 막대를 움직여 전체 동영상 클립 중에서 내보낼 구간을 설정할 수 있다.

 

 

  이후에 [파일(F)] - [내보내기] - [미디어]로 이동하여 내보낼 수 있다. 여기에서 다음과 같이 설정했던 작업 영역에 맞게 소스 범위가 정확히 설정되어있는지 다시 확인할 수 있다. 결과적으로 [내보내기]를 눌러서 동영상을 만들면 된다.

 

728x90
반응형

728x90
반응형

대회 링크: codeforces.com/contest/1408

A번 문제: codeforces.com/contest/1408/problem/A

 

  세 개의 수열(Sequence) a, b, c가 존재한다. 이때 수열의 길이는 모두 n으로 동일하다. 또한 각 인덱스에 따른 ai, bi, ci는 서로 다르다. 이때 다음의 조건을 만족하는, 길이가 n인 수열 p를 하나라도 찾으면 된다.

 


[ 문제 해설 ]

 

  기본적으로 ai, bi, ci가 서로 다르다는 조건이 있다. 따라서 일단 a[0]를 출력한 뒤에 그다음부터는 그리디(greedy)하게 하나씩 출력하면 된다. 이때 (순환적으로) 인접한 두 수가 서로 달라지도록 하면 된다. 매번 바로 이전에 출력했던 수를 제외하고 다른 수를 하나 출력하도록 하자. 단, 마지막 수를 출력할 때는 첫째 수 또한 제외하고 출력해야 한다.

 

[ 정답 코드 ]

 

for _ in range(int(input())):
    n = int(input())
    a = list(map(int, input().split()))
    b = list(map(int, input().split()))
    c = list(map(int, input().split()))

    # 첫 번째 수 출력
    now = a[0]
    print(now, end=' ')

    # n - 2개의 수 출력
    for i in range(1, n - 1):
        if now != a[i]:
            now = a[i]
        elif now != b[i]:
            now = b[i]
        elif now != c[i]:
            now = c[i]
    print(now, end=' ')
    
    # 마지막 수 출력
    if now != a[n - 1] and a[n - 1] != a[0]:
        now = a[n - 1]
    elif now != b[n - 1] and b[n - 1] != a[0]:
        now = b[n - 1]
    elif now != c[n - 1] and c[n - 1] != a[0]:
        now = c[n - 1]
    print(now)

 

B번 문제: codeforces.com/contest/1408/problem/B

 

  길이가 n0 이상(non-negative)의 정수로 구성된 비내림차순(non-decreasing) 배열 a가 있다. 우리는 m개의 0 이상(non-negative)의 정수로 구성된 비내림차순(non-decreasing) 배열의 집합 b를 찾는 것이다. 또한 k 값이 입력으로 주어진다. 이때 다음과 같은 조건을 만족하는 가장 작은 m을 찾으면 된다.

 

 

[ 문제 해설 ]

 

  이 문제는 테이블 형태로 그려보면 아이디어를 쉽게 찾을 수 있다. 예를 들어 n = 9, k = 4라고 하자.

 

  a = [0, 1, 2, 2, 3, 3, 3, 4, 4, 4, 4]라고 하면 다음과 같은 수열 b들을 찾을 수 있다.

 

  2 2 3 5 7 11 13 13 17
b1 2 2 3 5 7 7 7 7 7
b2 0 0 0 0 0 4 6 6 10

 

  결과적으로 m의 최솟값은 2이다. 문제 해결 아이디어는 간단하다. 먼저 배열 a에서 서로 다른 원소의 개수를 cnt라고 하자. 이때 cnt를 줄여나가는 방식을 사용한다. b1은 항상 k개만큼의 cnt를 줄일 수 있다. 이후에 b2부터 모든 배열은 (k - 1)개 만큼의 cnt를 줄일 수 있다.

 

[ 정답 코드 ]

 

import math

for _ in range(int(input())):
    n, k = map(int, input().split())

    # 서로 다른 원소 개수
    cnt = len(set(map(int, input().split())))

    if cnt > 1 and k == 1: # (k - 1)이 0이므로 만들 수 없는 경우
        print(-1)
    elif cnt <= k:
        print(1)
    else:
        print(1 + math.ceil((cnt - k) / (k - 1)))

 

C번 문제: codeforces.com/contest/1408/problem/C

 

  길이가 l인 도로가 있다. 이 도로는 좌표 0부터 l까지의 정수로 구성된다. 왼쪽 차는 0에서 시작해서 오른쪽으로 간다. 오른쪽 차는 l에서 시작해서 왼쪽으로 간다. 처음 두 자동차의 속력은 1/s이다. 그리고 n개의 깃발(flag)이 존재한다. 두 자동차는 깃발을 지날 때마다 속력이 1/s씩 증가한다.

 

  결과적으로 두 자동차가 만나는 시간을 출력하면 된다.

 

[ 문제 해설 ]

 

  예를 들어 l = 7이고, 다음과 같이 5개의 깃발이 있다고 해보자.

 

0 1 2 3 4 5 6 7
왼쪽 차 깃발(flag) 깃발(flag) 깃발(flag) 깃발(flag)   깃발(flag) 오른쪽 차

 

  아이디어는 간단하다. 총 n개의 깃발(flag)을 확인하면 된다. 다시 말해 n번을 반복하는데, 반복할 때마다 매번 ① 왼쪽 차 기준으로 다음 깃발까지의 남은 시간, ② 오른쪽 차 기준으로 다음 깃발까지의 남은 시간을 계산하자. 이때 시간이 더 짧은 쪽이 해당 깃발까지 이동하도록 만들면 된다. 그 시간 동안 상대방 차 또한 이동하도록 만들면 된다. 결과적으로 n개의 깃발을 다 처리한 뒤에, 두 자동차가 만나기 위해 걸리는 시간까지 계산해 더해주면 된다.

 

  거리 = 속도 X 시간

 

  위와 같은 공식(거속시)을 이용하여 코드를 작성하면 다음과 같다.

 

[ 정답 코드 ]

 

#include <bits/stdc++.h>
 
using namespace std;
 
int main() {
    int testCase;
    cin >> testCase;
    for (int tc = 0; tc < testCase; tc++) {
        int n, l;
        cin >> n >> l;

        // n개의 깃발(flag) 정보 입력받기
        vector<int> data;
        for (int i = 0; i < n; i ++) {
            int x;
            cin >> x;
            data.push_back(x);
        }

        // 처음 A와 B의 정보 초기화
        long double aPos = 0;
        int aNext = 0;
        int aSpeed = 1;

        long double bPos = l;
        int bNext = n - 1;
        int bSpeed = 1;

        // A와 B가 만나기까지 걸리는 총 시간(time)
        long double time = 0.0;

        // n개의 깃발에 대하여 하나씩 처리
        int cnt = 0;
        while (cnt != n) {
            // A와 B의 다음 깃발까지의 거리
            long double aDistance = data[aNext] - aPos;
            long double bDistance = bPos - data[bNext];

            // A와 B의 다음 깃발까지의 시간
            long double aTime = aDistance / aSpeed;
            long double bTime = bDistance / bSpeed;

            // 더 짧은 시간이 걸리는 자동차를 기준으로 이동
            if (aTime <= bTime) {
                aPos = data[aNext];
                bPos -= aTime * bSpeed;
                aNext += 1;
                aSpeed += 1;
                time += aTime;
            }
            else {
                bPos = data[bNext];
                aPos += bTime * aSpeed;
                bNext -= 1;
                bSpeed += 1;
                time += bTime;
            }
            cnt++;
        }

        // 마지막으로 A와 B가 서로 만나도록 하기
        long double distance = bPos - aPos;
        time += distance / (aSpeed + bSpeed);

        cout.precision(15);
        cout << fixed;
        cout << time << '\n';
    }
}

 

D번 문제: codeforces.com/contest/1408/problem/D

 

[ 정답 코드 ]

 

n, m = map(int, input().split())

robbers = []
lights = []

for i in range(n):
    a, b = map(int, input().split())
    robbers.append((a, b))

for i in range(m):
    c, d = map(int, input().split())
    lights.append((c, d))

v = [0] * 1000002

for a, b in robbers:
    for c, d in lights:
        # 현재 robber보다 light의 x가 더 크거나 같다면 감시 가능
        if a <= c:
            # 그때 robber가 y 방향으로 얼마만큼 이동해야 숨을 수 있는지 계산
            v[c - a] = max(v[c - a], d - b + 1)

result = 1000001 # 1000001만큼 움직이는 것이 최악의 경우
dy_max = 0 # 가장 큰 dy 값

# 1000001부터 0까지 1씩 줄여가며, 모든 dx에 대하여
for dx in range(1000001, -1, -1):
    dy_max = max(dy_max, v[dx])
    # (dx + dy_max) 값이 가장 작은 경우를 출력
    result = min(result, dx + dy_max)

print(result)
728x90
반응형