🖥

Rubyでダイヤル問題を解く

に公開

問題

  • AからZまでの目盛りを持つダイヤルがあるとする。
  • このダイヤルは右に回すことも左に回すことも出来る。
  • ダイヤルは丸いのでAとZは隣り合わせだ。

ひとつの目盛りから別の目盛りまで、動かす必要のある最小の目盛り数を求めたい。

image.png

コード

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オープンチャットもご利用ください。

https://line.me/ti/g2/eEPltQ6Tzh3pYAZV8JXKZqc7PJ6L0rpm573dcQ

Twitter

https://twitter.com/YumaInaura

公開日時

2022-09-23

Discussion