問題
- AからZまでの目盛りを持つダイヤルがあるとする。
- このダイヤルは右に回すことも左に回すことも出来る。
- ダイヤルは丸いのでAとZは隣り合わせだ。
ひとつの目盛りから別の目盛りまで、動かす必要のある最小の目盛り数を求めたい。
コード
XからBまでの距離を計算すると、移動距離は4であることが分かる。
B -> A -> Z -> Y -> X で4マス移動だ。
SCALES = ("A".."Z").to_a
current_dial = "X"
next_dial = "B"
current_dial_place = SCALES.index(current_dial) + 1
next_dial_place = SCALES.index(next_dial) + 1
formal_move_diff = (next_dial_place - current_dial_place).abs
reversed_move_diff = SCALES.size - formal_move_diff
minimum_diff = [formal_move_diff, reversed_move_diff].min # 4
解説
ダイアルの配列全体はこうだ。
SCALES = ("A".."Z").to_a
# => ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"]
XからBまでの距離を求める時は、位置の差分を求めれば良い。
Bは2番目、Xは24番目に位置する。
next_dial_place = SCALES.to_a.index("B")+1 # => 2
current_dial_place = SCALES.to_a.index("X")+1 # => 24
配列内での2つの位置の距離は、片方の値から片方を引き算して絶対値を得れば良い。
formal_move_diff = (next_dial_place - current_dial_place).abs # 22
AからZの配列の中では、XとBは22個離れている。
だがこれはダイヤルなので逆順にも回すことが出来る。
逆順での距離は ダイヤルの全ての目盛りの数 - 正順での距離
で求められる。
(ここでは便宜的に片方を正順、もう片方を逆順と呼ぶ)
reversed_move_diff = SCALES.size - formal_move_diff # 4
正順と逆順、両者の小さい方が最小の目盛り数である。
minimum_diff = [formal_move_diff, reversed_move_diff].min
感想
逆順での計算が大変だと思ったが、意外に引き算だけで大丈夫だった。
別案
AからZまでの同じ配列を2個つなげて、AとZが隣り合わせの配列を作ろうかとも考えていた。
こちらのほうが良かったかもしれない。
# AからZまでの配列二個分を連結する
SCALES = ("A".."Z").to_a*2
# XとBの位置を得る
# アルファベットは2セットなので合計4箇所が見つかる
places = SCALES.each_index.select { |i| ["X", "B"].include? SCALES[i] } # [1, 23, 27, 49]
# 最初のXから最初のBまでの距離
move1 = places[1] - places[0] # 22
# 最初のBから二個目のXまでの距離
move2 = places[2] - places[1] # 4
[move1, move2].min # 4
チャットメンバー募集
何か質問、悩み事、相談などあればLINEオープンチャットもご利用ください。