Centos 7에서 설치를 진행하였습니다.

공식홈페이지에서 요구사항입니다.

Prerequisites

The list of prerequisites for running the NVIDIA device plugin is described below:

  • NVIDIA drivers ~= 361.93
  • nvidia-docker version > 2.0 (see how to install and it's prerequisites)
  • docker configured with nvidia as the default runtime.
  • Kubernetes version = 1.10
  • The DevicePlugins feature gate enabled


참고사이트 : 

먼저 nvidia-docker2 을 설치한다
이전에 버전1을 깔았다면 아래 명령어로 제거해준다.
$ yum install nvidia-docker2
docker volume ls -q -f driver=nvidia-docker | xargs -r -I{} -n1 docker ps -q -a -f volume={} | xargs -r docker rm -f
yum remove nvidia-docker


버전 2설치
$ yum install nvidia-docker2
$ pkill -SIGHUP dockerd


도커 실행

$ docker run --runtime=nvidia --rm nvidia/cuda nvidia-smi



도커에서 제대로 실행이 된다면 이젠 k8s에서 pod으로 띄워본다.


k8s에서 띄우기 위해선 몇가지 작업을 더 해줘야한다.


아래 과정들은 모든 노드에서 진행한다.

데몬 설정에 아래와 같이 해준다 path엔 실제 돌아가는 nvidia-container-runtime을 추가해준다.


$ vi /etc/docker/daemon.json
{
   "default-runtime": "nvidia",
    "runtimes": {
        "nvidia": {
            "path": "/usr/bin/nvidia-container-runtime-hook",
            "runtimeArgs": []
        }
    }
}

DevicePlugins에 아래와같이 하여 enable시켜준다
$ vi /etc/systemd/system/kubelet.service.d/10-kubeadm.conf
[Service]
Environment="KUBELET_KUBECONFIG_ARGS=--bootstrap-kubeconfig=/etc/kubernetes/bootstrap-kubelet.conf --kubeconfig=/etc/kubernetes/kubelet.conf"
Environment="KUBELET_SYSTEM_PODS_ARGS=--pod-manifest-path=/etc/kubernetes/manifests --allow-privileged=true"
Environment="KUBELET_NETWORK_ARGS=--network-plugin=cni --cni-conf-dir=/etc/cni/net.d --cni-bin-dir=/opt/cni/bin"
Environment="KUBELET_DNS_ARGS=--cluster-dns=10.96.0.10 --cluster-domain=cluster.local"
Environment="KUBELET_AUTHZ_ARGS=--authorization-mode=Webhook --client-ca-file=/etc/kubernetes/pki/ca.crt"
Environment="KUBELET_CADVISOR_ARGS=--cadvisor-port=0"
Environment="KUBELET_CGROUP_ARGS=--cgroup-driver=systemd"
Environment="KUBELET_CERTIFICATE_ARGS=--rotate-certificates=true --cert-dir=/var/lib/kubelet/pki"
Environment="KUBELET_EXTRA_ARGS=--feature-gates=DevicePlugins=true"

ExecStart=
ExecStart=/usr/bin/kubelet $KUBELET_KUBECONFIG_ARGS $KUBELET_SYSTEM_PODS_ARGS $KUBELET_NETWORK_ARGS $KUBELET_DNS_ARGS $KUBELET_AUTHZ_ARGS $KUBELET_CADVISOR_ARGS $KUBELET_CGROUP_ARGS $KUBELET_CERTIFICATE_ARGS $KUBELET_EXTRA_ARGS


데몬에 적용
$ systemctl daemon-reload
$ systemctl restart kubelet

쿠버네티스에 gpu support을 enable시켜준다. ( 버전 정보는 자신의 k8s 버전에 맞게 ^^ )

$ kubectl create -f https://raw.githubusercontent.com/NVIDIA/k8s-device-plugin/v1.10/nvidia-device-plugin.yml


pod이 제대로 떠있는지 확인 ( nvidia-device-plugin-daemonset 이 올라왔다면 완료 !) 

$ kubectl get pods --all-namespaces
NAMESPACE     NAME                                    READY     STATUS             RESTARTS   AGE
default       test-p-5f44f8586f-g98pd                 0/1       ImagePullBackOff   0          16m
kube-system   etcd-jframe-master                      1/1       Running            2          2d
kube-system   kube-apiserver-jframe-master            1/1       Running            2          2d
kube-system   kube-controller-manager-jframe-master   1/1       Running            2          2d
kube-system   kube-dns-86f4d74b45-nxqfr               3/3       Running            6          2d
kube-system   kube-flannel-ds-7dmtt                   1/1       Running            3          2d
kube-system   kube-flannel-ds-f9vbs                   1/1       Running            0          2d
kube-system   kube-proxy-9xgll                        1/1       Running            0          2d
kube-system   kube-proxy-w6hm8                        1/1       Running            3          2d
kube-system   kube-scheduler-jframe-master            1/1       Running            2          2d
kube-system   nvidia-device-plugin-daemonset-7vdwp    1/1       Running            6          2d

gpu의 개수를 출력해보았다 ( 실제론 master에도 gpu가 2개 장착되어있지만, 실제 pod가 실행되는 곳이 slave라 마스터엔 nvidia-device-plugin이 설치안된듯 )
만약 여기서 실제 노드에 gpu가 장착되어있음에도 0으로 잡힌다면 각 노드에서 docker ps을 통해 nvidia-device-plugin이 제대로 떠 있는지 확인해본다.
$ kubectl get nodes "-o=custom-columns=NAME:.metadata.name,GPU:.status.allocatable.nvidia\.com/gpu"
NAME             GPU
master    0
slave   2


만들어둔 yaml을 통해 실행시켜봤다.

$ cat test.yaml
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: gpu-demo
spec:
  replicas: 1
  template:
    metadata:
      labels:
        run: gpu-demo
      annotations:
        scheduler.alpha.kubernetes.io/nvidiaGPU: "{ \n  \"AllocationPriority\": \"Dense\"\n}\n"
    spec:
      containers:
      - name: gpu-demo
        image: nvidia/cuda:7.5-runtime
        command:
        - "/bin/sh"
        - "-c"
        args:
        - nvidia-smi && tail -f /dev/null
        resources:
          limits:
            alpha.kubernetes.io/gpu: 2

만들어둔 yaml을 통해 실행시켜봤다.

$ kubectl apply -f test.yaml

실행한 pod의 로그를 확인 ( 제대로 설치 된것을 확인할 수 있다 )
$ kubectl logs gpu-demo-85bdc64dd7-5grcg
Mon May 28 17:25:27 2018
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 390.59                 Driver Version: 390.59                    |
|-------------------------------+----------------------+----------------------+
| GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
|===============================+======================+======================|
|   0  GeForce GTX 108...  Off  | 00000000:01:00.0 Off |                  N/A |
|  0%   48C    P8    18W / 250W |      0MiB / 11176MiB |      1%      Default |
+-------------------------------+----------------------+----------------------+
|   1  GeForce GTX 108...  Off  | 00000000:04:00.0 Off |                  N/A |
|  0%   41C    P8     9W / 250W |      0MiB / 11178MiB |      0%      Default |
+-------------------------------+----------------------+----------------------+

+-----------------------------------------------------------------------------+
| Processes:                                                       GPU Memory |
|  GPU       PID   Type   Process name                             Usage      |
|=============================================================================|
|  No running processes found                                                 |
+-----------------------------------------------------------------------------+


k8s에서 gpu을 사용하는 pod을 만들일이 있어 관련 내용을 정리하였다. 


gpu을 사용하기 위해서는 nvidia driver가 있어야한다.

  

이번 포스팅엔 먼저 nvidia 드라이버 설치 관련 내용을 정리하였다.


nvdia driver설치하기 

$ yum update
$ yum install kernel-devel kernel-headers gcc make
 

nouveau 을 활성화시키면 충돌가능성이 있기에 blacklist해준다. 
$ echo 'blacklist nouveau' >> /etc/modprobe.d/blacklist.conf
$ dracut /boot/initramfs-$(uname -r).img $(uname -r) --force
$ reboot

파일다운

이것저것 뜨는데 다 ok, yes해줌 된다.
$ bash NVIDIA-Linux-x86_64-390.59.run

1. WARNING: nvidia-installer was forced to guess the X library path '/usr/lib64' and X
           module path '/usr/lib64/xorg/modules'; these paths were not queryable from the
           system.  If X fails to find the NVIDIA X driver module, please install the
           `pkg-config` utility and the X.Org SDK/development package for your distribution
           and reinstall the driver
OK
2. Install NVIDIA's 32-bit compatibility libraries?
YES

3. Would you like to run the nvidia-xconfig utility to automatically update your X
  configuration file so that the NVIDIA X driver will be used when you restart X?  Any
  pre-existing X configuration file will be backed up.
YES

4.  Your X configuration file has been successfully updated.  Installation of the NVIDIA
  Accelerated Graphics Driver for Linux-x86_64 (version: 390.59) is now complete.
OK



$ nvidia-smi


Thu May 24 21:39:10 2018
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 390.59                 Driver Version: 390.59                    |
|-------------------------------+----------------------+----------------------+
| GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
|===============================+======================+======================|
|   0  GeForce GTX 108...  Off  | 00000000:01:00.0 Off |                  N/A |
|  0%   34C    P5    15W / 250W |      0MiB / 11178MiB |      0%      Default |
+-------------------------------+----------------------+----------------------+
|   1  GeForce GTX 108...  Off  | 00000000:04:00.0 Off |                  N/A |
|  0%   33C    P5    13W / 250W |      0MiB / 11178MiB |      0%      Default |
+-------------------------------+----------------------+----------------------+

+-----------------------------------------------------------------------------+
| Processes:                                                       GPU Memory |
|  GPU       PID   Type   Process name                             Usage      |
|=============================================================================|
|  No running processes found                                                 | 


위 화면이 뜬다면 완료 

k8s을 설치하고 재 설치 시 전에 있던 데이터가 남아 있어서 문제가 생긴다.


$ kubeadm init 


필자의 경우 위 명령어가 time out으로 계속 실패하여 원인을 찾는데 많은 시간을 소비했다. 

아래 명령어를 통해 docker, k8s을 초기화하고 다시 설치하자 문제 없이 설치가 되었다. 


# docker 초기화

$ docker rm -f `docker ps -aq`

$ docker volume rm `docker volume ls -q`
$ umount /var/lib/docker/volumes
$ rm -rf /var/lib/docker/

$ systemctl restart docker 


# k8s 초기화

$ kubeadm reset

$ systemctl restart kublet


# iptables에 있는 데이터를 청소하기 위해

$ reboot 


 







참고사이트 : 

https://www.howtoforge.com/tutorial/centos-kubernetes-docker-cluster/



설치버전 :

CentOS버전 :
$ cat /etc/redhat-release
CentOS Linux release 7.4.1708 (Core)

리눅스 커널버전 : 

$ uname -sr
Linux 3.10.0-693.el7.x86_64 


호스트 : 
# cat /etc/hosts
127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6

192.168.0.2 master
192.168.0.3 slave01



모든 노드에서 진행해준다.

Disable selinux 
$ setenforce 0
$ sed -i --follow-symlinks 's/SELINUX=enforcing/SELINUX=disabled/g' /etc/sysconfig/selinux



Enable br_netfilter Kernel Module
$ modprobe br_netfilter
$ echo '1' > /proc/sys/net/bridge/bridge-nf-call-iptables
$ modprobe br_netfilter


위에 modprobe을 하면 bridge관련 모듈이 추가된다.

  
$ ll /proc/sys/net/
total 0
dr-xr-xr-x. 1 root root 0 May 24 22:23 core
dr-xr-xr-x. 1 root root 0 May 24 02:30 ipv4
dr-xr-xr-x. 1 root root 0 May 24 02:30 ipv6
dr-xr-xr-x. 1 root root 0 May 24 02:30 netfilter
-rw-r--r--. 1 root root 0 May 24 22:23 nf_conntrack_max
dr-xr-xr-x. 1 root root 0 May 24 02:30 unix

$ ll /proc/sys/net/
total 0
dr-xr-xr-x. 1 root root 0 May 24 01:19 bridge
dr-xr-xr-x. 1 root root 0 May 24 01:06 core
dr-xr-xr-x. 1 root root 0 May 23 21:09 ipv4
dr-xr-xr-x. 1 root root 0 May 23 21:09 ipv6
dr-xr-xr-x. 1 root root 0 May 23 21:09 netfilter
-rw-r--r--. 1 root root 0 May 24 01:06 nf_conntrack_max
dr-xr-xr-x. 1 root root 0 May 23 21:09 unix 


Disable SWAP

$ swapoff -a
# swap 파티션이나 swap 파일의 구동을 중단시키는 명령어

swap이란 하드디스크를 메모리처럼 사용하는 기법
-> 물리적인 메모리가 모자라면 하드디스크를 메모리처럼 데이터를 기록하여 메모리를 확보
프로그램들을 많이 실행해서 메모리가 부족해지면, 메모리 상에 적재된 프로그램 중 당장 필요하지 않은 프로그램 데이터를 하드디스크에 옮겨서 메모리 공간을 확보

출처: http://nextcube.tistory.com/137 [중성자 별의 충돌 에너지]



$ vi /etc/fstab

/dev/mapper/centos-root /                       xfs     defaults        0 0
UUID=e68efdc4-b1c1-4f94-ab67-72d611499e13 /boot                   xfs     defaults        0 0
UUID=E849-B774          /boot/efi               vfat    umask=0077,shortname=winnt 0 0
#/dev/mapper/centos-swap swap                    swap    defaults        0 0



Install Docker 

$ yum install -y yum-utils device-mapper-persistent-data lvm2
$ yum install -y docker-ce




Install Kubernetes

$ cat <<EOF > /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=https://packages.cloud.google.com/yum/repos/kubernetes-el7-x86_64
enabled=1
gpgcheck=1
repo_gpgcheck=1
gpgkey=https://packages.cloud.google.com/yum/doc/yum-key.gpg https://packages.cloud.google.com/yum/doc/rpm-package-key.gpg
EOF

# /etc/yum.repos.d/kubernetes.repo


$ yum install -y kubeadm


$ systemctl start docker && systemctl enable docker
$ systemctl start kubelet && systemctl enable kubelet 



Change the cgroup-driver

$ docker info | grep -i cgroup
Cgroup Driver: cgroupfs

$ sed -i 's/cgroup-driver=systemd/cgroup-driver=cgroupfs/g' /etc/systemd/system/kubelet.service.d/10-kubeadm.conf

$ systemctl daemon-reload
$ systemctl restart kubelet 



k8S Cluster Initialization


Master서버에서 ( apiserver-advertise-address 에 master서버 아이피 입력 ) 
$  kubeadm init --apiserver-advertise-address=192.168.0.2 --pod-network-cidr=10.244.0.0/16


Your Kubernetes master has initialized successfully!

To start using your cluster, you need to run the following as a regular user:

  mkdir -p $HOME/.kube
  sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
  sudo chown $(id -u):$(id -g) $HOME/.kube/config

You should now deploy a pod network to the cluster.
Run "kubectl apply -f [podnetwork].yaml" with one of the options listed at:
  https://kubernetes.io/docs/concepts/cluster-administration/addons/

You can now join any number of machines by running the following on each node
as root:

 kubeadm join 192.168.0.2:6443 --token bi09ej.5z5q8osipp4r9w5u --discovery-token-ca-cert-hash sha256:0aa7b7489d097ae88ad17c7dad7a591d2da711fca6ed533b4063979c917747a5

# 따로 메모 해둔다. 슬레이브서버에서 조인할때 필요



$ mkdir -p $HOME/.kube
$ sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
$ sudo chown $(id -u):$(id -g) $HOME/.kube/config 



# 여기선 flannel네트웍으로 사용



$ kubectl get nodes
$ kubectl get pods --all-namespaces
NAMESPACE     NAME                                    READY     STATUS    RESTARTS   AGE
kube-system   etcd-jframe-master                      1/1       Running   0          22s
kube-system   kube-apiserver-jframe-master            1/1       Running   0          28s
kube-system   kube-controller-manager-jframe-master   1/1       Running   0          42s
kube-system   kube-dns-86f4d74b45-nxqfr               3/3       Running   0          1m
kube-system   kube-flannel-ds-7dmtt                   1/1       Running   0          1m
kube-system   kube-proxy-w6hm8                        1/1       Running   0          1m
kube-system   kube-scheduler-jframe-master            1/1       Running   0          19s



슬레이브노드에서 진행 : 

$ kubeadm join 192.168.0.2:6443 --token bi09ej.5z5q8osipp4r9w5u --discovery-token-ca-cert-hash sha256:0aa7b7489d097ae88ad17c7dad7a591d2da711fca6ed533b4063979c917747a5


This node has joined the cluster:
* Certificate signing request was sent to master and a response
  was received.
* The Kubelet was informed of the new secure connection details.

Run 'kubectl get nodes' on the master to see this node join the cluster.


마스터노드에서 확인 : ( slave01이 제대로 안 올라오면 위에 과정을 제대로 입력했는지 확인해본다 ) 
$ kubectl get nodes
NAME             STATUS    ROLES     AGE       VERSION
master    Ready     master    21m       v1.10.3
slave01   Ready     <none>    57s       v1.10.3

$  kubectl get pods --all-namespaces
NAMESPACE     NAME                                    READY     STATUS    RESTARTS   AGE
kube-system   etcd-jframe-master                      1/1       Running   0          46m
kube-system   kube-apiserver-jframe-master            1/1       Running   0          46m
kube-system   kube-controller-manager-jframe-master   1/1       Running   0          46m
kube-system   kube-dns-86f4d74b45-nxqfr               3/3       Running   0          47m
kube-system   kube-flannel-ds-7dmtt                   1/1       Running   0          47m
kube-system   kube-flannel-ds-f9vbs                   1/1       Running   0          56s
kube-system   kube-proxy-9xgll                        1/1       Running   0          56s
kube-system   kube-proxy-w6hm8                        1/1       Running   0          47m
kube-system   kube-scheduler-jframe-master            1/1       Running   0          46m



마지막으로 팟을 만들어서 확인해본다.
Testing Create First Pod

$ kubectl create deployment nginx --image=nginx
$ kubectl describe deployment nginx
$ kubectl create service nodeport nginx --tcp=80:80
$ kubectl get svc
( 이 명령어를 통해 PORT에 포워딩한 포트를 기억한다 ) 

$  curl master:31280
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
    body {
        width: 35em;
        margin: 0 auto;
        font-family: Tahoma, Verdana, Arial, sans-serif;
    }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>


$ curl slave01:31280
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
    body {
        width: 35em;
        margin: 0 auto;
        font-family: Tahoma, Verdana, Arial, sans-serif;
    }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>





Could not fetch/save url https://download.docker.com/linux/centos/docker-ce.repo to file /etc/yum.repos.d/docker-ce.repo: [Errno 14] curl#60 - "Peer's Certificate has expired."


Docker설치하려던 중 위와 같은 에러가 나왔다.


$ date

2018. 01. 24. (수) 16:10:26 KST


날짜정보가 안 맞으면 이러한 오류가 난다



centos 기준 

$ rdate -p time.bora.net

rdate: [time.bora.net] Tue May 15 20:54:13 2018


p옵션을 통해 현재시간과 맞는지 확인  (rdate가 없을 시 yum 으로 설치해준다)


맞다면 아래명령어를 통해 설치 

$ rdate -s time.bora.net



다시 curl을 할 시 오류없이 제대로 동작하는것을 확인할 수 있다.

맥에서 마리아디비 설치하기 !


아주 쉽다.. 



brew 을 통해 mariadb설치

$ brew install mariadb 


서비스 시작

$ brew services start mariadb


$ mysql -uroot





'etc > mac' 카테고리의 다른 글

인텔리제이 단축키  (1) 2018.12.29
맥에서 윈도우 설치(VMWare Fusion )  (1) 2018.02.17
AppCleaner - 앱 깔끔하게 지우기  (0) 2018.02.16
맥에서 패키지관리(brew)  (0) 2018.02.10

https://algospot.com/judge/problem/read/PACKING#


문제 :


여행을 떠나기 전날까지 절대 짐을 싸지 않는 버릇이 있는 재훈이는 오늘도 비행기 타기 전날에야 가방을 싸기 위해 자리에 앉았습니다. 비행기 규정상 재훈이는 캐리어를 하나만 가지고 갈 수 있는데, 아무래도 가져가고 싶은 물건들이 캐리어 안에 다 들어가지 않을 것 같습니다. 재훈이는 가져가고 싶은 각 물건들의 부피와 얼마나 필요한지를 나타내는 절박도를 조사해 다음과 같은 목록을 만들었습니다. 


물건노트북 컴퓨터카메라XBOX365커피그라인더아령백과사전
부피4264210
절박도7106754
캐리어의 용량이 정해져 있기 때문에 가져갈 수 있는 물건들의 부피 합은 캐리어의 용량 w 이하여야 합니다. 이때 절박도를 최대화할 수 있는 물건들의 목록을 계산하는 프로그램을 작성하세요.

입력

입력의 첫 줄에는 테스트 케이스의 수 C (1≤C≤50)가 주어집니다. 각 테스트 케이스의 첫 줄에는 가져가고 싶은 물건의 수 N (1≤N≤100)과 캐리어의 용량 W (1≤W≤1000)가 주어집니다. 그 이후 N줄에 순서대로 각 물건의 정보가 주어집니다. 한 물건에 대한 정보는 물건의 이름, 부피, 절박도 순서대로 주어지며, 이름은 공백 없는 알파벳 대소문자 1글자 이상 20글자 이하의 문자열, 부피와 절박도는 1000 이하의 자연수입니다.

출력

각 테스트 케이스별 출력의 첫 줄에는 가져갈 수 있는 물건들의 최대 절박도 합과 가져갈 물건들의 개수를 출력합니다. 이후 한 줄에 하나씩 각 물건들의 이름을 출력합니다. 만약 절박도를 최대화하는 물건들의 조합이 여럿일 경우 아무 것이나 출력해도 좋습니다.


예제 입력

2
6 10
laptop 4 7
camera 2 10
xbox 6 6
grinder 4 7
dumbell 2 5
encyclopedia 10 4
6 17
laptop 4 7
camera 2 10
xbox 6 6
grinder 4 7
dumbell 2 5
encyclopedia 10 4

예제 출력

24 3
laptop
camera
grinder
30 4
laptop
camera
xbox
grinder



풀이 :


이 문제는 DP(Dynamic Programming) 문제로 유명하다는 Knapsack문제의 변형문제라고 한다. 


이 문제는 다르게 표현하면, 정해진 weight ( 즉, 캐리어의 용량 ) 내에서 최대 절박도를 구하는 문제다. 


구해야 하는 출력값에는 절박도, 물건 개수, 물건 이름이 있다. 


먼저 최대 절박도를 구해보자 .


간단 표현 식 :


findMaxHope ( 물건 리스트, 남은 캐리어 용량, 현재 아이템 ){

        # 기저부분

1. 현재 아이템이 마지막 아이템이면 나간다

2. 남은 용량이 0보다 같거나 작음 나간다.

# 현재 아이템에 포인트 1 해줌으로서, 지금 아이템을 선택 했을 절박도를 구한다

현재 아이템을 선택 할때 절박도 = findMaxHope ( 물건 리스트, 캐리어 용량, 현재 아이템 + 1) 


# 현재 아이템을 선택했을 절박도 ( 현재 아이템의 무게가, 남은 캐리어 용량보다 작아야된다 )

현재 아이템을 선택 할때 절박도  = findMaxHope ( 물건 리스트, 캐리어 용량 - 현재 아이템 무게 , 현재 아이템 + 1) 

        # 둘 중 큰 값 반환

return max ( 현재 아이템을 선택 할때 절박도, 현재 아이템을 선택 할때 절박도


}

 


식으로 간단히 표현해봤다. 

DP에서 가장 중요한 부분은 개인적인 의견으로 기저부분이라고 생각한다.. 

먼저 기저부분을 작성해준다. 


다음 논리적인 식을 적어주면된다. 현재 아이템을 선택 안 할때와 했을 때 절박도 중 최대값을 구해서 return해주면 절박도의 최대값을 구할 수 있다. 


But. 우리가 원하는 출력값에는 물건개수와 물건이름도 알아야한다. 

위의 식만 이용해선 물건개수와 물건이름을 알 수없다. 


아래 식을 추가해준다. 



getItemList ( 물건 리스트남은 캐리어 용량현재 아이템 , 아이템리스트 ){

      # 기저부분

      1. 현재 아이템이 마지막 아이템이면 나간다

      2. 남은 용량이 0보다 같거나 작음 나간다.

      

      # 두 값을 비교, 같다면 현재 아이템을 선택 안한 것이다. ( 현재 아이템을 선택 안 했기에, 절박도가 동일 ). 

      if ( findMaxHope ( 물건 리스트캐리어 용량현재 아이템 )  ==  findMaxHope ( 물건 리스트캐리어 용량현재 아이템 + 1)  )

      # 다음 포인트로 넘겨줌 

               getItemList ( 물건 리스트남은 캐리어 용량현재 아이템 + 1 , 아이템리스트 );

      else        

      # 현재 아이템까지 최대 절박도를 합친결과와 다음 아이템까지 최대 절박도를 합친 결과와 다르다면 현재 아이템은 선택된것이다. 

      # 선택 됐으니, 아이템 리스트에 추가하고, 무게를 빼서 다시 실행 

      ( 이 부분이 이해안간다면 , 위의 findMaxHope을 디버깅해가며 다시 본후 보면 이해가 빠를 것이다 ) 

              아이템리스트.add ( 현재 아이템 )

              getItemList ( 물건 리스트남은 캐리어 용량 - 현재 아이템 무게 현재 아이템 + 1 , 아이템리스트 );


}

 



이렇게 물건개수와 물건이름 또한 구할 수 있다. 


* 출처 : getItemList부분은 "알고리즘 문제해결 전략" 책을 참고하여 풀었습니다. 


 



쉽게 설명하기위해 한글로 적었는데 더 복잡한 느낌 ..

전체 코드를 보면 위에 그대로 코드로 표현하였다. 

추가된 부분 : cache 부분 추가 -- 이 부분이 없으면 timeout발생 

( dp를 이용할 때 memo기능을 이용해서 dp에서 실행된 결과는 cache처럼 저장하고 있다가 불러오면 시간단축을 할 수 있다 ) 



전체코드 

import java.util.ArrayList;

import java.util.List;

import java.util.Scanner;


public class Packing {


static int cache[][]  = new int[1001][100];

public static void main(String[] args) {

Scanner scanner = new Scanner(System.in);

int c = scanner.nextInt();

List<String> items = null;

for ( int i = 0 ; i < c ; i++) {

int n = scanner.nextInt();

int w = scanner.nextInt();


List<Stuff> stuffs = new ArrayList<Stuff>();

items = new ArrayList<String>();

for ( int j = 0 ; j < n ; j ++  ) {

String name = scanner.next();

int weight = scanner.nextInt();

int hope = scanner.nextInt();

Stuff stuff = new Stuff(name, weight, hope);

stuffs.add(stuff);

}

for (int j = 0; j < 1001 ; j++) {

for(int k = 0; k < 100 ; k++) {

cache[j][k] = -1;

}

}

int point = 0;

int hope = findMaxHope(stuffs, w, point);;

getItemList(stuffs, w, point, items );

System.out.println(hope + " "+items.size());

for ( int j = 0 ; j < items.size() ; j++ ) {

System.out.println(items.get(j));

}


}

}

private static void getItemList(List<Stuff> stuffs, int w, int point, List<String> items )

{

if( point == stuffs.size() ) {

return;

}

if( w <= 0 ) {

return;

}

// 값이 다르다면  , 최대 절박도를 얻을 있다

    if (findMaxHope(stuffs, w, point) == findMaxHope(stuffs, w, point+1))

    getItemList(stuffs, w, point + 1, items);

    else

    {

    items.add(stuffs.get(point).getName());

    getItemList(stuffs, w - stuffs.get(point).getWeight() , point + 1, items);

    }

}

private static int findMaxHope(List<Stuff> stuffs, int w, int point) {

// 기저부분 

if( point == stuffs.size() ) {

return 0;

}

if( w <= 0 ) {

return 0;

}

if( cache[w][point] != -1) {

return cache[w][point];

}

// hope 절박도 

int hope = 0;

// 현재 아이템을 선택하지 않는 경우 ( 무게가 무거워 베낭에 담을 없을 )

hope = findMaxHope(stuffs, w, point+1);

// 현재 아이템을 선택할 경우 

if( w >= stuffs.get(point).getWeight() )

{

int rhope = ( findMaxHope(stuffs, w - stuffs.get(point).weight , point+1) ) + stuffs.get(point).hope ;

if( rhope > hope ) {

hope = rhope;

}

}

cache[w][point] = hope;

return hope;

}



static class Stuff {

String name;

int weight;

int hope;


public Stuff(String name, int weight, int hope) {

this.name = name;

this.weight = weight;

this.hope = hope;

}

public String getName() {

return name;

}

public void setName(String name) {

this.name = name;

}

public int getWeight() {

return weight;

}

public void setWeight(int weight) {

this.weight = weight;

}

public int getHope() {

return hope;

}

public void setHope(int hope) {

this.hope = hope;

}

}

}

 




'algolism' 카테고리의 다른 글

[카카오예선문제]캠핑풀이설명  (0) 2018.02.24
[알골스팟] XHAENEUNG문제  (0) 2018.02.16




문제 

https://programmers.co.kr/learn/courses/30/lessons/1833


무지를 돌보느라 지친 콘은 한적한 시골의 한 캠핑장에 놀러 갔다. 캠핑장은 텐트를 칠 수 있는 넓은 평지를 제공하고 있는데, 이 평지에는 이미 캠핑장에서 설치해 놓은 n개의 쐐기가 박혀 있다. 캠핑장 이용 고객은 이 쐐기들 중 한 쌍을 골라 다음과 같은 조건을 만족하도록 텐트를 설치해야 한다.

텐트는 직사각형 형태여야 한다. 

텐트의 네 면이 정확하게 동, 서, 남, 북을 향해야 한다.

대각에 위치하는 텐트의 두 꼭짓점이 정확하게 선택한 두 개의 쐐기에 위치해야 한다.

텐트가 점유하는 직사각형 영역의 넓이는 0보다는 커야 한다.

텐트가 점유하는 직사각형 영역 내부에 다른 쐐기를 포함하면 안 된다. (다른 쐐기가 경계에 위치하는 경우는 허용함)

캠핑장에서는 위와 같은 조건을 만족하는 위치라면 어디든 고객이 텐트를 설치할 수 있도록 정확한 크기의 텐트를 모두 구비하여 대여해준다고 한다.

당신은 위와 같은 조건을 만족하는 텐트를 설치할 수 있는 쐐기의 쌍의 개수는 총 몇 가지가 될지 궁금해졌다.

n개의 쐐기의 위치가 좌표로 주어질 때, 위의 조건을 만족하는 쐐기의 쌍의 개수를 계산하는 프로그램을 작성하시오. 단, 동서 방향은 x축, 남북 방향은 y축과 평행하다고 가정한다. 



입력형식

입력은 쐐기의 개수를 의미하는 정수 n과, n × 2 크기의 2차원 배열 data로 주어지며, 배열의 각 행은 캠핑장에 설치된 쐐기의 x좌표와 y좌표를 의미한다. 제한 조건은 다음과 같다.


2 <= n <= 5,000

n개의 쐐기는 모두 x좌표 0 이상 2^31-1 이하, y좌표 0 이상 2^31-1 이하에 위치한다.

입력되는 n개의 쐐기 중 x좌표와 y좌표가 모두 같은 경우는 없다. 



출력형식

 입력에 주어진 각 케이스에 대해 가능한 텐트의 쐐기의 쌍의 개수를 정수 형태로 리턴한다.


예제 입출력

n    data anwer

4    [[0,0],[1,1],[0,2],[2,0]]    3




이 문제를 풀려면 O(n^3)으론 풀수 없다고한다. 최적화를 해서 푼사람도 있다는 ... 

참고 : http://tech.kakao.com/2017/08/11/code-festival-round-1/



그럼 O(n^2)로 풀어야 한다는 건데 아래와 같은 일반 방식으론 풀수없다.


일반방식 ( 가장 간단히 생각할 수 있는 방식 )  : 

모든 쐐기를 검사하며 확인하는 방법이다.

이방법의 경우 정한 두 쐐기 안에 다른 쐐기가 있는지 확인하기 위해 for문을 한번 더 돈다.

총 n의 크기가 5천개니까 5000^3까지 돌수 있기 때문에, 이 경우 실패한다.


int n;


// x,y포인트을 저장 변수

int data[n][2];


for ( int I = 0 ; I < n ; I++) {

      data[I][0] = x좌표;

      data[I][1] = y좌표;

}

// 첫번째 쐐기

for (  I = 0 ; I < 쐐기의 수 - 1 ; I++) {

// 두번째 쐐기

for ( j = I+1 ; j < 쐐기의 수 : j++ ) {


// 두 쐐기가 같은 행이나 열에 있는 경우 제외 

       if(data[i][0] == data[j][0] || data[i][1] == data[j][1] )

      {

        continue;

      }

      // 두 쐐기를 통해 사각형을 만들었을 때, 각 좌표값들을 저장

int endX = Math.max(data[j][0], data[i][0]);

int startX = Math.min(data[j][0], data[i][0]);

int endY = Math.max(data[j][1], data[i][1]);

int startY = Math.min(data[j][1], data[i][1]);


boolean value = true;

// 모든 쐐기를 검사하며 공간안에 쐐기가 박혀있는지 확인 

for ( k = 0 ; k < data.length ; k++ ) {

        if( startY < data[k][1] && data[k][1] < endY && startX <= data[k][0] && data[k][0] <= endX ) {

value = false;

break;

       }

}

if( value ) {


answer ++;

}

}


}


answer값 출력



결국 이 문제를 풀기 위해서는 좌표 압축과, 부분 합 배열을 알아야한다. 


필자의 경우 부분 합 배열을 아래 사이트를 보고 이해했다.

참고 : http://meansoup.blogspot.kr/2017/09/blog-post.html


먼저 좌표 압축이란 무엇인가 ....

말 그대로 좌표를 압축시키는 거다 .

예) (1 , 40) , ( 2, 7 ), ( 10, 20 )  --> (1,3) , (2,1) , (3, 2) 
이 와같은 좌표들이 있다면, 값 들의 크기는 이 문제에서 중요하지 않다. 오직 순서만 중요하다.

 

 



이 그림들이 여기 문제에서는 같은 의미라는 것을 알 수있다.

그럼 좌표 압축을 시켜준 후에, 부분 합 배열을 통해 내부에 쐐기가 있는 지 검사해야한다. 

부분 합배열 

 1

 1

  0 

  0

 0 

 0

  1

  0

 0

 1

  0

  1

 1

 0

  0

  1

위 그림은 x,y좌표의 쐐기 위치


 1

 2

 2

 2

 1

 2 

 3

 3

 1

 3

 4

 5

 2

 4

 5

 7

위 그림은 쐐기를 부분 합해서 구한 그림



그럼 부분합을 어떻게 이용하는지 보자 

첫번 째 방법에서는 내부에서 모든 쐐기를 검사하였다. 

여기서 두번 째 방법에서는 

1. 행이나 열이 하나만 차이나면 무조건 텐트를 칠 수 있기에 합을 해준다.

2. 그게 아닐 시, 부분합을 이용 쐐기를 검사해준다. 



2번의 경우를 설명하겠다. 

위에 주황색 사각형의 숫자 5는 3행 3열까지의 모든 쐐기를 더 한 경우다.  

위의 파랑색은 2번째 행의 모든 쐐기를 더한 경우이고 초록색은 2번째 열의 쐐기를 모두 더한 경우라고 생각하면된다.

그리고 핑크색은 2행 2열까지의 모든 쐐기를 더한 경우다.


그럼 예로서 (2,2)에서 (3,3)까지 텐트를 칠때, 안에 쐐기를 어떻게 검사하냐 ( 물론 행,열이 하나 차이나기에 무조건 텐트를 칠수있지만 예를 쉽게하기 위해 이런 예를 들었습니다 ). 

주황색 사각형에서 초록색 사각형과 파랑색사각형을 빼주고 핑크색이 중복으로 빼졌으니 핑크색 사각형을 더 해주면 해당 좌표안에 쐐기를 확인할 수있다. 

( 주황색 사각형 - ( 초록색 사각형 + 파랑색 사각형 ) + 핑크색 사각형 ) = ( 5 - ( 3  +  4)  + 3 )  

이 값이 0이 아닐 시 쐐기가 추가된거라고 보면된다.



이와 같은 방식으로 풀면 O(n^2) 으로 풀 수 있다.

아래는 문제를 풀때 도저히 답이 안나와서 참고한 링크..


코드 : 

http://stack07142.tistory.com/289





'algolism' 카테고리의 다른 글

[알골스팟] PACKING 문제  (0) 2018.03.03
[알골스팟] XHAENEUNG문제  (0) 2018.02.16

맥에서 윈도우를 사용하기 위해서는 가상 머신을 사용하면 된다.


가상머신 종류는 많지만, 필자의 경우 VMWare Fusion을 설치하였다. 


무료여서 여기 를 클릭하여 다운 받아서 사용하면 된다.

사이트 : https://my.vmware.com/en/web/vmware/info/slug/desktop_end_user_computing/vmware_fusion/10_0


처음에 버튼을 누르고 iso파일을 드래그 앤 드롭하면 설치가 되는데 


실행시  “Cannot find a valid peer process to connect to” 이 와같은 오류가 발생한다.


해결방법 : 

시스템 환경설정 > 일반 > 아래에서 VMWare Funsion에 대한 접근권한을 설정해준다. 




'etc > mac' 카테고리의 다른 글

인텔리제이 단축키  (1) 2018.12.29
맥에서 mariadb설치  (0) 2018.03.04
AppCleaner - 앱 깔끔하게 지우기  (0) 2018.02.16
맥에서 패키지관리(brew)  (0) 2018.02.10

맥을 사용하다보면 불 필요한 앱을 지울일이 생긴다.



위와 같이 LaunchPad에서 알트키를 눌러서 삭제하거나, 앱을 마우스로 push해서 지울 수가있다.


하지만 뭔가 찌꺼기 데이터가 남을 것같으면, 앱을 이용하면 관련 데이터를 깔끔히 지울 수 있다.


저는 아래사이트에 받아서 사용했습니다. 

http://freemacsoft.net/appcleaner/


광고는 아니고 최근 맥북을 사서 하나하나 써가면서 공유합니다.

'etc > mac' 카테고리의 다른 글

인텔리제이 단축키  (1) 2018.12.29
맥에서 mariadb설치  (0) 2018.03.04
맥에서 윈도우 설치(VMWare Fusion )  (1) 2018.02.17
맥에서 패키지관리(brew)  (0) 2018.02.10

+ Recent posts