Blog Archive

레이블이 엑셀인 게시물을 표시합니다. 모든 게시물 표시
레이블이 엑셀인 게시물을 표시합니다. 모든 게시물 표시

2024-08-28

최적화(자원 배분) 문제 - 엑셀의 해 찾기(solver)

제가 교육 업무 현업에 있을 때 대략 다음과 같은 문제에 자주 맞닥드리게 되었습니다. 

상황 1. 연간 기본 교육 계획 수립

문제 

주어진 총 예산은 1,000만원이다. 교육 프로그램의 종류가 4가지가 있는데, 제일 비싼 것 A는 100만원, 그 다음 B가 60만원, 그 다음 C가 30만원, 가장 가벼운 프로그램 D는 20만원이다. 예산 1,000만원을 다 쓰는 범위 내에서 각 프로그램을 몇 회 씩 운영해야 할 지 계획을 세워라. 
단, 모든 프로그램은 모두 연간 2회 이상 운영해야 하고, 가장 비싼 프로그램은 최대 3회 이내에서 운영할 수 있고, 가장 가벼운 프로그램도 최대 30회 이내에서 운영해야 한다.

문제 분해

이런 문제가 전형적인 최적화 문제입니다. 즉, 목표치가 주어지고, 목표치와 몇 가지 제약 조건에 맞추어서 주어진 자원을 어떻게 배분하느냐의 문제이지요. 엑셀, 구글 시트, 리브레오피스의 캘크의 "해 찾기(solver)" 기능으로 답을 찾을 수 있습니다. 

  • 목표: 연간 예산 총액 1,000만원에 맞추기
  • 변수: A, B, C, D 프로그램의 운영 횟수 (각각 a, b, c, d라고 하겠습니다.)
  • 제약 조건:
    • a, b, c, d는 모두 정수(integer)이다.
    • a, b, c, d는 모두 2 이상이다.
    • a는 3 이하이다.
    • d는 30 이하이다.
  • 방정식: 100a + 60b + 30c + 20d = 1000 을 만족하는 미지수 a, b, c, d를 구하라. 
    • 미지수가 여러 개인 다항식이기 때문에 일차 방정식이지만 해가 여러 개 존재한다. 
    • 따라서, 주어진 목표값에 이르기 위한 입력값을 여러 가지로 변화시켜보는 목표 탐색 기법을 써야 한다.

엑셀의 해 찾기에 대입

엑셀 (2024년 8월 28일 현재, Microsoft 365 기준)의 '해 찾기'에 이 문제를 넣기 위해 아래와 같은 표를 만들었습니다. 

목표는 총합이 천만원(분홍색 E7)이 되는 것이고, 변수는 횟수(노란색 D열)이다.
목표는 총합이 천만원(분홍색 E7)이 되는 것이고, 변수는 횟수(노란색 D열)이다.

목표 셀에 커서를 놓은 상태에서, 엑셀의 해 찾기를 실행합니다. 
  • 제일 위에 E7이라고 지정한 것이 목표 셀입니다. 바로 밑에서 목표 지정값으로 10,000,000원을 주었습니다. 
  • 중간에 변수들의 범위를 지정합니다. 운영 횟수, 즉 D3 ~ D6가 변수 부분입니다.
  • 마지막으로, 특별히 제약 조건이 무엇인지 지정합니다. 예를 들면, 변수는 모두 정수이고, 2 이상이고 등 총 4가지 제약 조건이 들어가 있습니다. 
엑셀의 해 찾기 대화 상자
엑셀의 해 찾기 대화 상자

이렇게 해서 해 찾기를 실행하면, 아래와 같은 계산 결과가 나옵니다. 

해 찾기를 했더니 노란색 D열의 운영 횟수가 채워졌습니다.
해 찾기를 했더니 노란색 D열의 운영 횟수가 채워졌습니다.

이런 결과가 마음에 들지 않으면, 다시 해 찾기를 실행하거나, 제약 조건을 더 추가해서 실행하면 다른 해를 찾아줍니다.

상황 2. 팀간 예산 균등 배분

문제 

사무실 소모품(과자, 물티슈, 휴지 등)으로 10만원의 예산을 썼다. 사후에 이것을 3개의 팀에 되도록 공평하게 부담시키려고 한다. 즉, 대략 3.3만원 내외에서 이미 써버린 항목들을 각 팀에 배분해야 한다. 어떤 항목을 어떤 팀에 배분해야 하는가?

예산 균등 배분 문제: 노란색에 팀 번호가 들어가면, 해당 팀의 배분액에
      금액이 들어간다.
예산 균등 배분 문제: 노란색에 팀 번호가 들어가면, 해당 팀의 배분액에 금액이 들어간다. 각 팀에 최대한 비슷하게 예산이 배분되어야 한다.


문제 분해

이 문제는 앞의 문제와는 달리 명확한 목표가 잘 안 보입니다. "3개 팀에 공평하게 배분"한다는 것을 어떻게 단일한 목표값으로 치환할 수 있을까요? 

제가 쓴 방법은 "세 팀의 예산 배분액의 표준편차를 최소화"하는 단일한 목표를 설정함으로써 문제를 단순화했습니다!

  • 목표: 3개 팀 예산 배분액 합의 표준편차가 최소가 되도록 한다. (아래 그림에서 E12, F12, G12의 표준편차가 최소가 되도록)
  • 변수: 각 항목별 팀 배정 번호(내역)
  • 제약 조건:
    • 모든 번호는 정수이다.
    • 모든 번호는 1 이상이다.
    • 모든 번호는 3 이하이다.
표준 편차 최소화라는 단일한 목표 설정
표준편차의 최소화라는 단일한 목표를 세웠습니다.


엑셀의 해 찾기 실행

목표 셀인 C13에 커서를 두고, 해 찾기를 실행합니다. 

해 찾기 대화상자. 목표값을 최소로 하는 해를 찾는다. 해법은 Evolutionary를 사용
해 찾기 대화상자. 목표값을 최소로 하는 해를 찾는다. 해법은 Evolutionary를 사용

  • 목표 셀은 C13 (3개 팀 배분액의 표준편차)입니다. 이번에는 목표 값을 지정한 것이 아니고, 목표값이 최소가 되도록 요구하였습니다.
  • 변수는 각 항목별 팀 배정 번호인 D3 ~ D11 노란색 부분이죠.
  • 제약사항은 변수들이 정수로서 1, 2, 3 중에 하나의 값을 갖도록 하였습니다. 
  • 해법 선택: 이번에는 해를 찾는 방법에 '단순 LP'나 'GRG 비선형' 대신에 'Evolutionary'를 선택했습니다. 각 방법의 차이는 수학적으로 좀 복잡하니 여기서는 다루지 않겠습니다.

계산 결과

표준편차가 289라는 작은 값으로 각 팀에 예산이 배분되었다.
표준편차가 289라는 작은 값으로 각 팀에 예산이 배분되었다.

위 그림과 같이 표준편차가 289라는 상당히 작은 값으로, 세 개의 팀에 3만3천5백원, 3만3천5백원, 3만3천원으로 거의 비슷하게 예산이 배분되었습니다. 

예시에 사용된 엑셀 파일: 예산 균등 배분 - 해 찾기.xlsx

해 찾기 기능을 이용하면, 이외에도 경영상에 부딫히는 수많은 최적화 문제, what-if 문제를 해결할 수 있습니다. 여러분의 문제 해결에도 써보시기 바랍니다.

2024-08-06

복수 선택 항목 설문 문항의 통계 처리

온라인 설문지를 만들다보면, 한 질문에 대해 복수 응답이 가능하도록 문항을 만드는 경우가 자주 발생합니다. 예를 들면, "지금 먹고 싶은 과일을 모두 선택하세요."라고 하면, 선택한 과일의 갯수가 0개가 될 수도 있고, 1개가 될 수도 있고, 그보다 많을 수도 있습니다. 

1. 체크박스를 이용한 복수 선택 문항

이런 설문지를 만들 때, 보통은 복수 선택형 체크박스(checkbox)를 사용합니다. 


체크박스로 구현한 복수 응답 설문 문항
체크박스로 구현한 복수 응답 설문 문항


이렇게 문항을 만들면 결과 데이터를 담는 스프레드시트(구글 시트 또는 엑셀 등)의 한 개의 셀에 여러 개 과일 이름이 한꺼번에 들어갑니다. 예를 들면, 셀 하나에 "사과, 수박" 이렇게 값이 들어갑니다. 설문 응답자가 여러 명일 때에, 전체 응답자 중에 "사과"라는 응답이 몇 개 나왔는지 카운트하려면 매우 어렵습니다. 

이런 경우, 텍스트 구분자(이 경우 쉼표)를 기준으로 컬럼을 잘라준 다음, 데이터 분석을 하는 방법이 있고, 그렇지 않으면, 셀 안에 있는 텍스트들을 분석하는 다소 복잡한 수식을 만들어야 합니다. 

결과가 저장된 스프레드 시트. 선택한 응답들이 한 셀에 모두 들어가 있어, 빈도를 분석하기 어렵다
선택한 응답들이 한 셀에 모두 들어가 있어, 빈도를 분석하기가 어렵습니다.

위의 그림에서 총3명의 응답을 분석해보면 사과가 2회, 딸기가 1회, 수박이 2회, 포도가 1회 나왔습니다. 과일 이름이 셀 단위로 분리되지 않았으므로, 셀 안에 있는 특정 단어를 카운트하기 위해서

  1. 3명 응답 전체의 텍스트 길이를 구합니다.
    =sumproduct(len(b$2:b$4) ...
  2. A10 셀에 있는 "포도"라는 단어가 몇 번 나왔는지 카운트하기 위해 "포도"를 제거한 전체 텍스트 길이를 구합니다.
    len(substitute(b$2:b$4,A10,""))
  3. 이제 전체 텍스트 길이에서 포도를 제거한 텍스트 길이를 빼줍니다.
    =sumproduct(len(b$2:b$4)-len(substitute(b$2:b$4,A10,"")))
  4. "포도"라는 단어가 나온 횟수(빈도)를 계산하기 위해 방금 뺀 값 전체를 "포도" 텍스트 길이로 나눠줍니다.
    =sumproduct(...)/len(A10)
이렇게 하면, 각각의 단어가 여러 셀에 걸쳐서 몇 번 출현했는지 빈도를 구할 수 있습니다. 하지만 이 방법은 꽤 번잡하고, 번거롭습니다. 


2. 체크박스 그리드 형식으로 설문을 만들기

구글 설문에서는 "체크박스 그리드" 형식을 선택하면 훨씬 수월하게 통계 처리가 가능합니다. 네이버 폼에서는 문항 형식을 "표형"을 선택하면 됩니다.

체크박스 그리드로 구성한 복수 응답 설문 문항
체크박스 그리드로 구성한 복수 응답 설문 문항

이 경우에는 결과분석 시트가 훨씬 단순하게 나옵니다. 각 응답 항목 하나가 하나의 컬럼으로 분리되어 나오기 때문에, 각 컬럼별로 단어가 들어있는 셀의 갯수만 세어주면 됩니다.

체크박스 그리드 형식의 문항에 대한 결과 시트. 응답 항목 하나가 하나의 셀로 분리가 되어 빈도 계산이 훨씬 쉽다.
응답 항목 하나가 하나의 셀로 분리가 되어 빈도 계산이 훨씬 쉽습니다.


즉, 위의 그림에서는 컬럼 C에 사과, D에 딸기, E에 수박, F에 포도라는 응답이 분리되어 저장되므로, 각각의 빈도만 계산해주면 됩니다. 예를 들어, 포도라는 응답이 나온 횟수를 계산하기 위해, 포도 컬럼에서 "선택"이라는 단어가 몇 개 있는지만 세어주면 됩니다.

=countif(f2:f4, "선택")

3. 결론

특별한 경우가 아니라면, 복수 응답을 받아야 하는 설문지를 설계한다면, 단순한 체크박스 형식으로 문항을 만들지 말고, 결과 처리를 미리 생각해서, 체크박스 그리드로 만드는 것이 훨씬 좋습니다. 

그러나, 이미 체크박스 형식으로 설문지를 만들어서, 응답들이 한 셀에 모두 한꺼번에 들어가 있을 수 있습니다. 위에서 소개한 대로, 찾고자 하는 항목의 텍스트 길이와 전체 텍스트 길이를 계산해주면, 그 항목의 출현 빈도를 계산할 수 있으니, 천천히 해보시기 바랍니다.