넥슨 입사시험 3번 문제 풀이

Tags:

last updated: Aug. 18, 2006.

그냥 재미로 오랜만에;;

문제는 http://orbi7.com/bbs/zboard.php?id=pls_amu_imported&no=20076에. 풀다보니 어느새 루비로 코딩하고 있던 자신을 발견함;; 그래서 풀이는 루비.

#!/usr/bin/ruby -w

numbers = [49, 49, 50, 52, 49, 47, 47, 46, 44, 42, \
42, 38, 38, 38, 36, 34, 33, 33, 33, 32, \
34, 38, 42, 41, 42, 42, 40, 41, 40, 38, \
38, 37, 37, 39, 41, 40, 40, 40, 40, 42, \
45, 47, 47, 46, 46, 46, 47, 47, 47, 47, \
46, 44, 43, 39, 36, 34, 34, 32, 30, 29, \
30, 31, 31, 31, 30, 28, 25, 23, 24, 22, \
22, 25, 25, 27, 31, 33, 35, 37, 38, 39, \
39, 40, 40, 41, 40, 40, 40, 40, 39, 38, \
37, 35, 35, 34, 33, 32, 31, 30, 30, 29, \
29, 28, 27, 27, 28, 27, 25, 26, 0, 0, \
90, 90, 90, 90, 90, 91, 90, 88, 87, 84, \
80, 78, 83, 89, 91, 90, 89, 92]

# Average value of given numbers
def average(nums)
nums.inject(0.0) { |tot, i| tot + i } / nums.size
end

def error_sum(nums)
rmse(nums, [encode(nums)]) ** 2 * nums.size
end

# Split numbers into every possible split point, and find out the split
# point where the difference of averages of left partition and the right
# one is the biggest.
def best_split_point(nums)
left_sum = nums[0]
right_sum = nums[1..-1].inject(0) { |tot, i| tot + i }

min_dif = error_sum(nums[0..0]) + error_sum(nums[1..-1])
min_dif_idx = 0

for i in (1..nums.size – 2)
cur_dif = error_sum(nums[0..i]) + error_sum(nums[i+1..-1])

if min_dif > cur_dif
min_dif_idx = i
min_dif = cur_dif
end
end

min_dif_idx
end

# Compute rmse value
def rmse(nums, encoding)
inflated = encoding.inject([]) do |memo, i|
memo << [i[0]] * i[1] end.flatten squared_sum = nums.zip(inflated).inject(0.0) do |tot, i| tot += (i[0] - i[1]) ** 2 end Math.sqrt(squared_sum / nums.size) end # Encode numbers. This is simply done by returning # [average of numbers, # of numbers] def encode(nums) [(nums.inject(0.0) { |tot, i| tot += i } / nums.size).round, nums.size] end # Split nums into two partition where rmse is expected to be minimized most. def split(nums) split_point = best_split_point(nums) num_partition = [nums[0..split_point], nums[split_point+1..-1]] [num_partition, [encode(num_partition[0]), encode(num_partition[1])]] end # Find out best run length encoding (RLE) based on rmse def find_best_encoding(nums, max_encoding_len) num_partition, encoding = split(nums) while encoding.size < max_encoding_len # Find the worst split worst_rmse = nil worst_rmse_idx = -1 num_partition.zip(encoding).inject(0) do |idx, (n, e)| if worst_rmse_idx == -1 || (rmse(n, [e]) ** 2 * n.size) > worst_rmse
worst_rmse = rmse(n, [e]) ** 2 * n.size
worst_rmse_idx = idx
end
idx + 1
end

# Split the bad guy
num_partition[worst_rmse_idx], encoding[worst_rmse_idx] = split(num_partition[worst_rmse_idx])

# Flatten – WTF! Do I really have to do this? I’m so stupid.
tmp_num_partition = []
num_partition.each do |v|
if v[0].kind_of?(Array)
v.each do |i|
tmp_num_partition << i end else tmp_num_partition << v end end num_partition = tmp_num_partition tmp_encoding = [] encoding.each do |v| if v[0].kind_of?(Array) v.each do |i| tmp_encoding << i end else tmp_encoding << v end end encoding = tmp_encoding end [num_partition, encoding] end num_parition, encoding = find_best_encoding(numbers, 16) puts "Number partitions=#{num_parition.inspect}" puts puts "RLE=#{encoding.inspect}" puts puts "RMSE=#{rmse(numbers, encoding)}" [/code] 음 일단 비효율성은 신경안쓰더라도 아직 1.89 [code] mkseo@mkseo:~/tmp$ ./rmse.rb Number partitions=[[49, 49, 50, 52, 49, 47, 47, 46], [44, 42, 42], [38, 38, 38, 36, 34, 33, 33, 33, 32, 34, 38], [42, 41, 42, 42, 40, 41, 40, 38, 38, 37, 37, 39, 41, 40, 40, 40, 40], [42, 45, 47, 47, 46, 46, 46, 47, 47, 47, 47, 46, 44], [43, 39], [36, 34, 34, 32, 30, 29, 30, 31, 31, 31, 30], [28, 25, 23, 24, 22, 22, 25, 25, 27, 31], [33, 35, 37, 38, 39, 39, 40, 40, 41, 40, 40, 40, 40, 39, 38, 37], [35, 35, 34], [33, 32, 31, 30, 30, 29, 29, 28, 27, 27, 28, 27, 25, 26], [0, 0], [90, 90, 90, 90, 90, 91, 90], [88, 87, 84], [80, 78, 83], [89, 91, 90, 89, 92]] RLE=[[49, 8], [43, 3], [35, 11], [40, 17], [46, 13], [41, 2], [32, 11], [25, 10], [39, 16], [35, 3], [29, 14], [0, 2], [90, 7], [86, 3], [80, 3], [90, 5]] RMSE=1.89571886101289 [/code] 아직까지 greedy인데, greedy로 하면 안되는건지 버그인지; 다시 수정 예정입니다.

Comments

16 responses to “넥슨 입사시험 3번 문제 풀이”

  1. Sam Kong Avatar
    Sam Kong

    재밌는 문제네요.
    근데 문제를 잘 읽어보시면 꼭 32개의 정수만을(즉, 16개의 파티션) 이용해야 한다고 돼 있습니다.
    오답 예1에 보시면 민구님과 같은 경우가 나오네요.
    그걸 수정해도 결과는 꽤 좋을 것 같습니다.

    저는 다른 건 복잡해서 엄두가 안 나고 1번 문제만 풀어봤어요.

    n=5000
    p ([*1…n]-[*1…n].map{|i|i.to_s.split(//).inject(i){|m,j|m+j.to_i}}).inject{|m,i|m+i}

    #=> 1227365

    맞는 건가요?
    모범답안이 없어서리…

    총잡이 문제도 시간 나면 풀어보고 싶네요.
    지난 주 루비퀴즈와도 비슷하구요.
    요즘 이런 Sudoku류의 문제가 유행인가봐요.
    Backtracking을 옵티마이즈하는 류 말예요.

    좋은 정보 감사합니다.

  2. MKSeo Avatar
    MKSeo

    읔 그렇군요 ㅎㅎ. 16개의 파티션;;
    어라 16개로 바꿨더니 2.97나와버린;;;;;;;;;;;;;
    흐음. 다르게 풀 수 있는건가보군요;;
    파티션 나눌때 좌우의 평균차가 크도록 하는게 아닌가 보내요.
    rmse가 가장 작아지도록 나눴어야하는듯…

  3. MKSeo Avatar
    MKSeo

    8-queens 의 변형인거 같은데
    총잡이 풀어볼까요? ㅎㅎ

  4. MKSeo Avatar
    MKSeo

    읔.. [*1..n] 이라니.. 좋은거 배웠습니다. (__;)
    대체 어디서 보신거에요. 저런 문법은..

  5. 공성식 Avatar
    공성식

    Golfing을 즐기다 보면 그런 데 관심을 갖게 된답니다….ㅎㅎ
    주로 루비 메일링 리스트죠 뭐…

  6. MKSeo Avatar
    MKSeo

    golfing이 무슨말이예요??? 골프???? ;;;;;;
    사전에도 안나온 말인데;;

  7. Sam Kong Avatar
    Sam Kong

    프로그래밍에서 Golfing이라고 하면 가장 짧은 코드로 원하는 결과를 얻어내는 것을 의미합니다.
    골프 경기에서 least strokes 가 우승하는 것과 같은 논리라 그런 용어를 쓰는 것 같습니다.
    주로 Perl 커뮤니티에서 많이 사용됩니다.
    물론 코드의 효율성이나 우아함을 어느 정도는 포기해야겠죠.
    변수명이나 함수명을 짧게 하는 것은 기본이구요.
    특히 루비는 메소드나 블록을 체인으로 쓸 수가 있어서 골핑하기에 적합합니다.
    코드를 읽는 사람에게는 짜증나는 일이겠지만 작성하는 사람의 입장에서는 도전적이면서도 재밌습니다.

  8. MKSeo Avatar
    MKSeo

    음.. 네… 그게 골핑이군요. ㅎㅎ

    메소드 연결하기.. 그런게 아마도 펄의 흔적..
    이런걸 chainable method라고 하는 사람들이 있더군요.
    그런데 공식명칭(?)은 없는듯.

    파이썬이 제일 짜증나는게 메소드 연결해서 쓰기가 안된다는 것..
    한줄에는 하나의 아이디어를 쓰라는 강제사항으로 이해는 하지만,
    루비에 익숙해진 뒤에 접했더니 갑갑하긴 하더군요..

  9. 염산맛황산 Avatar

    이 문제를 최대한 멍청하게 풀면 어느정도의 성능이 나올까 하고 도전해 봤는데요…

    [code lang=”cpp]
    void compress() {
    int count = 0;
    compressed[count][0] = srcMatrix[0];
    compressed[count][1]++;
    for(int i = 1 ; i compressed[count][0] – 6.1)
    compressed[count][1]++;
    else {
    count++;
    if(count >= 16) {cout

  10. 염산맛황산 Avatar

    if(count >= 16) {cout

  11. MKSeo Avatar
    MKSeo

    흠 닫는 따옴표를 안넣으셨어요;; lang=”cpp” 이렇게 해야하는데. 제가 죄송스럽군요.

    cout << "hello" world << endl; [/code]

  12. 미친병아리 Avatar

    이야.. 루비로.. 멋지십니다..

  13. JM Avatar
    JM

    훈련 다녀와서 리플.. -.-;;
    전에 풀어본 적이 있어용. 3번은 dynamic programming 문제입니당..
    http://ricanet.com/new/view.php?id=blog/060228#none

    맨 아래 보면, 이제 바뀌어서 난이도가 올라갔다는 설이. = p

    지금 다니는 회사 입사할때는 hardcore C 문법 시험을 봤었는데..
    이런 알고리즘 problem solving 능력 (4번 같은 경우에는 창의력이랄까 발상이랄까도 좀 필요한지 모르지만) 을 테스트 하는 것과 어느쪽이 더 맞는 걸까요?

    거 왜 조엘이 한참 뭐라고 했던거 같은데.. 저는 봉투 뒤의 계산밖에 기억나지 않는군요. -.-;;

  14. MKSeo Avatar
    MKSeo

    @미친병아리: 감사 ^^

    @JM: dynamic programming으로 어떻게 한다는건지 이해가 잘 안가요;; 면접은 아마 회사에서 필요로 하는 기술에따라 다르게 보는게 맞지 않을까요. 예를들어 임베디드 프로그래머를 구한다면, 실제 임베디드 프로그래밍에서 맞딱뜨리는 문제를 시험보는게 맞겠고, 그냥 일반적인 의미의 소프트웨어 기술자라면 일반적인 의미의 문제해결능력을 보는게 맞을거라고 생각되요. 하지만 정말 problem solving의 능력을 측정하려면, 그가 답을 말하는가 아닌가 보다 계속 변화되는 문제 셋팅을 주면서 어떻게 대처해나가는가를 봐야하는데, 쉽진 않겠죠. 객관적인 평가 시스템을 만들어야하는데, 그게 에너지가 너무 많이 드는 일이니가요.. 저 개인적으로는 하드코어 C문법 시험은 별로 ㅎㅎ

  15. MKSeo Avatar
    MKSeo

    @JM: 블로그는 없애셨나요? 다시 만드세요 ㅎㅎ 글도 열심히 안쓰시더만 없애다니 너무해.

  16. newzun Avatar
    newzun

    1.32

Leave a Reply

Your email address will not be published. Required fields are marked *