問題と経緯
以前、二つのコップの問題をプログラムで解きました。
3デシリットルのコップと5デシリットルのコップがあります。これらを使って1デシリットルの水をはかりとるにはどうしたらいいですか。
水差し問題とも呼ばれるそうです。
以前、解いたときは、水の移し替えなどの操作をランダムに行い、目的の量になったら終了、これを何度も繰り返し、最小の手順を答えとするものでした。
厳密には最小かどうかは怪しいです。
ところが「だんご屋のひまつぶし」をPythonで解いたところ、二つのコップの問題も同様に解けることに気付きました。
コード
import copy
def main():
cup_capacities = [3, 5]
target = 1
print("問題", cup_capacities, target)
steps = solve_cup(cup_capacities, target)
print("正解", steps)
print("️手順再現")
show_steps([0, 0], cup_capacities, steps)
def solve_cup(cup_capacities, target):
moves = [
"left_spill",
"right_spill",
"left_pour",
"right_pour",
"left_right",
"right_left",
]
state = [0, 0]
states = [state]
states_done = [state]
steps = {}
steps[str(state)] = []
for i in range(20):
states_new = []
for state in states:
if len(steps[str(state)]) < i:
continue
for step in moves:
state_next = transition_state(state, cup_capacities, step)
if state_next == []:
continue
if not state_next in states_done:
steps[str(state_next)] = steps[str(state)][:]
steps[str(state_next)].append(step)
states_new.append(state_next)
else:
pass
if state_next[0] == target or state_next[1] == target:
return steps[str(state_next)]
states_done += states_new
states = states_new
def transition_state(state_previous, cup_capacities, step):
state = copy.deepcopy(state_previous)
if step == "left_spill":
state[0] = 0
elif step == "right_spill":
state[1] = 0
elif step == "left_pour":
state[0] = cup_capacities[0]
elif step == "right_pour":
state[1] = cup_capacities[1]
elif step == "left_right":
if state[0] + state[1] > cup_capacities[1]:
state[0] = state[0] + state[1] - cup_capacities[1]
state[1] = cup_capacities[1]
else:
state[1] = state[0] + state[1]
state[0] = 0
elif step == "right_left":
if state[0] + state[1] > cup_capacities[0]:
state[1] = state[1] + state[0] - cup_capacities[0]
state[0] = cup_capacities[0]
else:
state[0] = state[0] + state[1]
state[1] = 0
return state
def show_steps(state_from, cup_capacities, steps):
state = copy.deepcopy(state_from)
print("初期状態", state)
for step in steps:
print("移動", step)
state = transition_state(state, cup_capacities, step)
print("状態", state)
print("回数", len(steps))
if __name__ == "__main__":
main()
def main():
cup_capacities = [3, 5]
target = 1
print("問題", cup_capacities, target)
steps = solve_cup(cup_capacities, target)
print("正解", steps)
print("️手順再現")
show_steps([0, 0], cup_capacities, steps)
def solve_cup(cup_capacities, target):
moves = [
"left_spill",
"right_spill",
"left_pour",
"right_pour",
"left_right",
"right_left",
]
state = [0, 0]
states = [state]
states_done = [state]
steps = {}
steps[str(state)] = []
for i in range(20):
states_new = []
for state in states:
if len(steps[str(state)]) < i:
continue
for step in moves:
state_next = transition_state(state, cup_capacities, step)
if state_next == []:
continue
if not state_next in states_done:
steps[str(state_next)] = steps[str(state)][:]
steps[str(state_next)].append(step)
states_new.append(state_next)
else:
pass
if state_next[0] == target or state_next[1] == target:
return steps[str(state_next)]
states_done += states_new
states = states_new
def transition_state(state_previous, cup_capacities, step):
state = copy.deepcopy(state_previous)
if step == "left_spill":
state[0] = 0
elif step == "right_spill":
state[1] = 0
elif step == "left_pour":
state[0] = cup_capacities[0]
elif step == "right_pour":
state[1] = cup_capacities[1]
elif step == "left_right":
if state[0] + state[1] > cup_capacities[1]:
state[0] = state[0] + state[1] - cup_capacities[1]
state[1] = cup_capacities[1]
else:
state[1] = state[0] + state[1]
state[0] = 0
elif step == "right_left":
if state[0] + state[1] > cup_capacities[0]:
state[1] = state[1] + state[0] - cup_capacities[0]
state[0] = cup_capacities[0]
else:
state[0] = state[0] + state[1]
state[1] = 0
return state
def show_steps(state_from, cup_capacities, steps):
state = copy.deepcopy(state_from)
print("初期状態", state)
for step in steps:
print("移動", step)
state = transition_state(state, cup_capacities, step)
print("状態", state)
print("回数", len(steps))
if __name__ == "__main__":
main()
実行結果
問題 [3, 5] 1
正解 [‘left_pour’, ‘left_right’, ‘left_pour’, ‘left_right’]
️手順再現
初期状態 [0, 0]
移動 left_pour
状態 [3, 0]
移動 left_right
状態 [0, 3]
移動 left_pour
状態 [3, 3]
移動 left_right
状態 [1, 5]
回数 4
left_spillは左のコップを空にする。
left_pourは左のコップをいっぱいにする。
left_rightは左のコップから右のコップに移し替える。
という意味です。
コメント