結城浩さん (id:hyuki) 出題のリンゴ問題に挑戦した

ベストリンゴ賞もらえた. 回答は C 言語で書いた. ハフマン符号を使わない回答だったので, 勉強のため vim script で書いてみた. こういうアルゴリズム問題は vim 力なくても書けるから練習に良い気がする.

" http://www.hyuki.com/codeiq/
" https://codeiq.jp/ace/yuki_hiroshi/q279
" りんごの圧縮問題
" 結城浩さんの解答をみながら実装
"
" type : leaf if 0
"      : leaf-dummy if -1
"      : node if 1
let s:sorted_list = [
\	{'count' : 1194, 'char':  'E', 'type' : 0},
\	{'count' : 1001, 'char':  'T', 'type' : 0},
\	{'count' :  805, 'char':  'A', 'type' : 0},
\	{'count' :  694, 'char':  'I', 'type' : 0},
\	{'count' :  662, 'char':  'O', 'type' : 0},
\	{'count' :  653, 'char':  'H', 'type' : 0},
\	{'count' :  581, 'char':  'N', 'type' : 0},
\	{'count' :  572, 'char':  'S', 'type' : 0},
\	{'count' :  460, 'char':  'R', 'type' : 0},
\	{'count' :  410, 'char':  'D', 'type' : 0},
\	{'count' :  398, 'char':  'L', 'type' : 0},
\	{'count' :  295, 'char':  'U', 'type' : 0},
\	{'count' :  246, 'char':  'W', 'type' : 0},
\	{'count' :  226, 'char':  'Y', 'type' : 0},
\	{'count' :  209, 'char':  'C', 'type' : 0},
\	{'count' :  201, 'char':  'M', 'type' : 0},
\	{'count' :  199, 'char':  'G', 'type' : 0},
\	{'count' :  123, 'char':  'K', 'type' : 0},
\	{'count' :  121, 'char':  'F', 'type' : 0},
\	{'count' :  110, 'char':  'P', 'type' : 0},
\	{'count' :  107, 'char':  'B', 'type' : 0},
\	{'count' :   74, 'char':  'V', 'type' : 0},
\	{'count' :   11, 'char':  'J', 'type' : 0},
\	{'count' :    9, 'char':  'Q', 'type' : 0},
\	{'count' :    5, 'char':  'Z', 'type' : 0},
\	{'count' :    5, 'char':  'X', 'type' : 0},
\	]


" ???
while len(s:sorted_list) % 3 != 0
	call add(s:sorted_list, {'count' : 0, 'type' : -1})
endwhile


while len(s:sorted_list) > 2 
	let a = remove(s:sorted_list, -1)
	let b = remove(s:sorted_list, -1)
	let c = remove(s:sorted_list, -1)
	let d = {
\		'count' : a.count + b.count + c.count, 
\		'type' : 1,
\		'child' : [a,b,c]}
	let i = len(s:sorted_list) 
	while i > 0
		let elm = s:sorted_list[i-1]
		if elm.count > d.count
			break
		endif
		let i = i - 1
	endwhile
	call insert(s:sorted_list, d, i)
endwhile

function! s:get_result(l, d, v)
	if a:l.type == 0
		let a:l.depth = a:d
		echo a:l.char . ':' . a:v
		return a:d * a:l.count
	elseif a:l.type == 1
		let c = a:l.child
		let c1 = s:get_result(c[0], a:d+1, a:v . "r")
		let c2 = s:get_result(c[1], a:d+1, a:v . "g")
		let c3 = s:get_result(c[2], a:d+1, a:v . "b")
		return c1+c2+c3
	else
		return 0
	endif
endfunction

echo s:get_result(s:sorted_list[0], 0, "")


note:

  • ハフマン符号が最適になる理由が理解できていないので, 3 の倍数にするためにダミーを追加している部分(??? のところ)の正しさがわかっていない.
  • 以下の部分は for in range() にしたかったけど, len(s:sorted_list)=0 の場合に対する例外処理が必要で悲しくなったので while 文にした. 恰好よくかけないのかな.
    let i = len(s:sorted_list)
    while i > 0
        ....
        let i = i - 1
    endwhile