今もまだ、少しずつ問題を解き進めています。
第七回 参考問題 XY-Sort
第七回の参考問題は「XY-Sort」。8x8 の数の並びを行方向または列方向で並べ替えて、最後に 1 行目の数の並びを出力する問題。
数の並びを配列の配列などで表現した場合、列の並び替えならば転置して並び替え結果を転置する、という方法が思いつけば簡単に解くことができます。
Elixir で実装するとこんな感じ。
リストのリストを転置するtranspose/1 の実装ができれば、あとは難しいところはありません。
defmodule YHPGexercise do @table ~w""" 4 1 4 2 1 3 7 3 2 0 5 0 2 3 6 0 6 7 6 4 5 7 5 1 3 1 6 6 2 4 6 0 5 5 5 1 """ |> Enum.chunk_every(6) def solve(input) when is_binary(input) do for(<<c <- input>>, do: c) |> Enum.reduce(@table, fn c, table when c in ?A..?F -> table |> transpose() |> Enum.sort_by(&Enum.at(&1, c - ?A)) |> transpose() c, table -> table |> Enum.sort_by(&Enum.at(&1, c - ?u)) end) |> hd() |> Enum.join() end def transpose([[] | _]), do: [] def transpose(table), do: [Enum.map(table, &hd/1) | transpose(Enum.map(table, &tl/1))] end # 以下、テストコード ExUnit.start(trace: true, seed: 0) defmodule YHPGexerciseTest do use ExUnit.Case [ {"0", "AvEx", "305027"}, {"1", "A", "112344"}, {"2", "C", "241413"}, {"3", "F", "134214"}, {"4", "u", "236067"}, {"5", "w", "732050"}, {"6", "y", "414213"}, {"7", "yx", "732050"}, {"8", "ux", "236067"}, {"9", "EF", "131424"}, {"10", "DF", "134124"}, {"11", "Au", "055165"}, {"12", "uA", "023667"}, {"13", "By", "234114"}, {"14", "yB", "114342"}, {"15", "yBy", "357020"}, {"16", "yByB", "350072"}, {"17", "AuBvCw", "131244"}, {"18", "FAuFBvFCw", "300527"}, {"19", "AuBv", "112344"}, {"20", "CwDx", "515056"}, {"21", "FzyE", "324114"}, {"22", "uAwDyB", "114324"}, {"23", "zExCvF", "073520"}, {"24", "uFxEv", "002357"}, {"25", "DyCwB", "076362"} ] |> Enum.each(fn {number, input, expected} -> @tag input: input, expected: expected test "#{number} #{input}", %{input: input, expected: expected} do assert expected == YHPGexercise.solve(input) end end) end
$ elixir exercise.exs Running ExUnit with seed: 0, max_cases: 1 YHPGexerciseTest [7/exercise.exs] * test 0 AvEx (0.01ms) [L#68] (以下略)
別解が見つからない
その後、別の解法がないか一週間ほど考えてみたのですが、どうにも見つかりません。 配列の配列以外のデータの持ち方も考えてみたのですが、データを変えても操作の方法は同じものしか思いつかない。
「どう書く」の問題を解いていて、一番悔しさを感じるときです。 気の利いた解法をじぶんで見つけられなかったときもかなり悔しいですが、すぐに思いつきすぐに解ける方法以外が見つからないときは、自分の中から出てくるアイディアはそんなものでしかないのか、と悲嘆してしまいます。
わたしは趣味のプログラマから職業プログラマへ転身した身ですが、こんなとき、仕事と趣味では向き合い方がまったく違うのだなと気付かされます。
逆に、前回の記事にしたような「こじらせた」コードができたときは満足度が高い。 仕事では絶対やりませんが。
第六回 当日のお題 続柄
この問題は当時も Prolog で解いているのですが、そのときの自分の記事を見ずに再発見することにしました。
genealogy(0, []) :- !. genealogy(Person, [Person | Generation]) :- Parent is (Person + 1) div 3, genealogy(Parent, Generation). me(P, P). mo([_ | G], G). da(G, [_ | G]). si([_ | G], [_ | G]). au([_, _ | G], [_ | G]). ni([_ | G], [_, _ | G]). co([_, _ | G], [_, _ | G]). relative(G1, G2, "me") :- me(G1, G2). relative(G1, G2, "mo") :- mo(G1, G2). relative(G1, G2, "da") :- da(G1, G2). relative(G1, G2, "si") :- si(G1, G2). relative(G1, G2, "au") :- au(G1, G2). relative(G1, G2, "ni") :- ni(G1, G2). relative(G1, G2, "co") :- co(G1, G2). relative(_, _, "-"). solve(P1, P2, R) :- genealogy(P1, G1), genealogy(P2, G2), relative(G1, G2, R), !. to_integer([C], I) :- I is C - 48. to_integer([C1, C0], I) :- I is (C1 - 48) * 10 + (C0 - 48). split(Input, P1, P2) :- append(S1, [45, 62 | S2], Input), to_integer(S1, P1), to_integer(S2, P2), !. % 以下、テストコード judge(Input, Expect, Expect) :- format("o passed input: ~s, result: ~s~n", [Input, Expect]). judge(Input, Expect, Actual) :- format("x failed input: ~s, expected: ~s, actual: ~s~n", [Input, Expect, Actual]). test(Input, Expect) :- split(Input, P1, P2), solve(P1, P2, R), judge(Input, Expect, R), !. test_all :- test("5->2", "mo"), test("28->10", "au"), test("1->1", "me"), test("40->40", "me"), test("27->27", "me"), test("7->2", "mo"), test("40->13", "mo"), test("9->3", "mo"), test("4->1", "mo"), test("1->3", "da"), test("12->35", "da"), test("3->8", "da"), test("6->19", "da"), test("38->40", "si"), test("9->8", "si"), test("4->2", "si"), test("15->16", "si"), test("40->12", "au"), test("10->4", "au"), test("21->5", "au"), test("8->2", "au"), test("3->5", "ni"), test("11->39", "ni"), test("2->13", "ni"), test("13->32", "ni"), test("14->22", "co"), test("40->34", "co"), test("5->8", "co"), test("12->10", "co"), test("1->27", "-"), test("8->1", "-"), test("12->22", "-"), test("2->40", "-"), test("32->31", "-"), test("13->14", "-").
使った処理系は GNU Prolog です。
$ gprolog --consult-file yhpg.pl --entry-goal test_all --query-goal halt GNU Prolog 1.5.0 (64 bits) Compiled Oct 29 2021, 07:27:11 with clang Copyright (C) 1999-2021 Daniel Diaz compiling /Users/matsumotoeiji/yhpg.pl for byte code... /Users/matsumotoeiji/yhpg.pl compiled, 86 lines read - 24634 bytes written, 8 ms o passed input: 5->2, result: mo (以下略)
実のところ、si/2 以降の続柄を判定する述語の実装は正確ではありません。
例えば si/2 で自分自身同士を比較すると結果は真( yes )になります。
$ gprolog | ?- ['yhpg.pl']. % コードの読み込み | ?- si([2, 1], [2, 1]). yes
この実装では、si/2 よりも me/2 を先に評価し、真になったら評価を打ち切る実装をしているので、結果としてこの実装でも問題を解くことができています。
アイディアがコードになるとき
この Prolog の実装では、思いついたアイディアをそのままコードで表現できました。 アイディアを正確に思い描くのに少し時間がかかったのですが、イメージが固まったあとは、実装の都合のためのコードといったものがなく、ほぼそのまま写しとれた感じがします。
こういうときは、とても気分がよい。 コードの端まで意識が届いたような、そんな感じ。
単に一過性の満足感だけでなく、何か新しいものが自分の中に増えたような、そんな気になります。気がするだけですけれども。











