3つの❖の中の数の和が等しくなるようにせよ

※記法については『算数の問題文の記法のまとめ【随時更新】 - 算数の問題文の記法を考える』を読んでください。


【2000年度ジュニア算数オリンピックトライアル問題6問目】
 下の10個の◇の中に1から10までの数を1つずつ入れて3つの❖の中の数の和が等しくなるようにしなさい。


【記法化】

n_table := [
    [0, 0, 0, 0],
    [0, 0, 0, 0],
    [0, 0, 0, 0],
    [0, 0, 0, 0]
];

実行;

[n_table[0][0], n_table[0][1], n_table[1][0], n_table[1][1], n_table[1][2], n_table[2][1], n_table[2][2], n_table[2][3], n_table[3][2], n_table[3][3]].every := 被りなしで挿入 : 被りなしで全て移動 := [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].every;

実行;

n_table[0][0] + n_table[0][1] + n_table[1][0] + n_table[1][1] == n_table[1][1] + n_table[1][2] + n_table[2][1] + n_table[2][2] == n_table[2][2] + n_table[2][3] + n_table[3][2] + n_table[3][3];

print(n_table);


【工夫】
2度数える部分をa、bとして、1+2+3+4+5+6+7+8+9+10+a+bが3の倍数でなければならない。よってa+bの候補は5、8、11、14、17。そのそれぞれで正解を見つけることができるらしい。5の場合、❖内は20なのでそうなるようにする。
正直記述が難しい所以外は普通の制約の問題であるし、最適化方法をここで考えるのはよそうと思う。
ちなみにCP-SAT ソルバー  |  OR-Tools  |  Google for Developersで普通に解くとこうなる。

from ortools.sat.python import cp_model

model = cp_model.CpModel()

num_vals = 11
A = model.new_int_var(1, num_vals - 1, "A")
B = model.new_int_var(1, num_vals - 1, "B")
C = model.new_int_var(1, num_vals - 1, "C")
D = model.new_int_var(1, num_vals - 1, "D")
E = model.new_int_var(1, num_vals - 1, "E")
F = model.new_int_var(1, num_vals - 1, "F")
G = model.new_int_var(1, num_vals - 1, "G")
H = model.new_int_var(1, num_vals - 1, "H")
I = model.new_int_var(1, num_vals - 1, "I")
J = model.new_int_var(1, num_vals - 1, "J")

model.add_all_different([A, B, C, D, E, F, G, H, I, J])
model.add(A + B + C + D == D + E + F + G)
model.add(D + E + F + G == G + H + I + J)

solver = cp_model.CpSolver()
status = solver.solve(model)

if status == cp_model.OPTIMAL or status == cp_model.FEASIBLE:
    print(f"A = {solver.value(A)}")
    print(f"B = {solver.value(B)}")
    print(f"C = {solver.value(C)}")
    print(f"D = {solver.value(D)}")
    print(f"E = {solver.value(E)}")
    print(f"F = {solver.value(F)}")
    print(f"G = {solver.value(G)}")
    print(f"H = {solver.value(H)}")
    print(f"I = {solver.value(I)}")
    print(f"J = {solver.value(J)}")
else:
    print("No solution found.")

コードを流用したけどnum_valsってどういう意味なんだろう。数字の値ぐらいの認識で良いんだろうか。


結果

A = 1
B = 5
C = 8
D = 10
E = 3
F = 4
G = 7
H = 6
I = 2
J = 9

【随時更新】ジュニア算数オリンピックトライアル問題「普通の制約の問題」の進捗

97年度問題1
98年度問題1 新しさがないのでパス
98年度問題2 新しさがないのでパス
98年度問題4 新しさがないのでパス
98年度問題6 新しさがないのでパス
99年度問題1
99年度問題2 新しさがないのでパス
99年度問題5
99年度問題6 新しさがないのでパス
99年度問題10
99年度問題11
99年度問題13
00年度問題1
00年度問題2 新しさがないのでパス
00年度問題3 新しさがないのでパス
00年度問題5
00年度問題6
00年度問題7
00年度問題10 新しさがないのでパス


01年度問題3
01年度問題5
01年度問題6
01年度問題8
01年度問題11
02年度問題1
02年度問題2
02年度問題5
02年度問題6
02年度問題7
02年度問題8
02年度問題11
03年度問題1
03年度問題3
03年度問題5
03年度問題6
03年度問題8
03年度問題9
03年度問題10
03年度問題13
03年度問題14
04年度問題1
04年度問題4
04年度問題6
04年度問題8
04年度問題10
04年度問題11
05年度問題1
05年度問題2
05年度問題3
05年度問題5
05年度問題6
05年度問題7
06年度問題1 グラフの問題
06年度問題10
06年度問題11
07年度問題1
07年度問題8
08年度問題1
08年度問題3
08年度問題4 記法の問題のようだが何とかなる
08年度問題10
08年度問題11
09年度問題1
09年度問題2
09年度問題6
09年度問題7
09年度問題8
10年度問題2
10年度問題5
10年度問題6
10年度問題8
10年度問題9
10年度問題10 絶対値の記法さえあれば
10年度問題11 ゲームの問題のようでありながら違う
11年度問題1
11年度問題2
11年度問題4
11年度問題5
11年度問題6
11年度問題8 ソートがあり複雑だが
12年度問題1
12年度問題2
12年度問題3
12年度問題4
13年度問題1
13年度問題2
13年度問題7
13年度問題8
13年度問題9

【随時更新】ジュニア算数オリンピックトライアル問題の分類

「普通の制約の問題」
97年度問題1
98年度問題1
98年度問題2
98年度問題4
98年度問題6
99年度問題1
99年度問題2
99年度問題5
99年度問題6
99年度問題10
99年度問題11
99年度問題13
00年度問題1
00年度問題2
00年度問題3
00年度問題5
00年度問題6
00年度問題7
00年度問題10
01年度問題3
01年度問題5
01年度問題6
01年度問題8
01年度問題11
02年度問題1
02年度問題2
02年度問題5
02年度問題6
02年度問題7
02年度問題8
02年度問題11
03年度問題1
03年度問題3
03年度問題5
03年度問題6
03年度問題8
03年度問題9
03年度問題10
03年度問題13
03年度問題14
04年度問題1
04年度問題4
04年度問題6
04年度問題8
04年度問題10
04年度問題11
05年度問題1
05年度問題2
05年度問題3
05年度問題5
05年度問題6
05年度問題7
06年度問題1 グラフの問題
06年度問題10
06年度問題11
07年度問題1
07年度問題8
08年度問題1
08年度問題3
08年度問題4 記法の問題のようだが何とかなる
08年度問題10
08年度問題11
09年度問題1
09年度問題2
09年度問題6
09年度問題7
09年度問題8
10年度問題2
10年度問題5
10年度問題6
10年度問題8
10年度問題9
10年度問題10 絶対値の記法さえあれば
10年度問題11 ゲームの問題のようでありながら違う
11年度問題1
11年度問題2
11年度問題4
11年度問題5
11年度問題6
11年度問題8 ソートがあり複雑だが
12年度問題1
12年度問題2
12年度問題3
12年度問題4
13年度問題1
13年度問題2
13年度問題7
13年度問題8
13年度問題9


「記法の問題」
97年度問題4
99年度問題3
07年度問題4
07年度問題6
08年度問題8
12年度問題6


「数列の問題」 終わりがない記述と普遍化するともっと多くの問題に該当するかもしれない
97年度問題8
04年度問題12
06年度問題6


「並びの問題」 ソートアルゴリズムの存在を考えれば純粋に新しい記法を考えても良いかもしれない。あるいはそれなしで実装できるか
98年度問題3
11年度問題3
12年度問題5


「ゲームの問題」 ソートアルゴリズムに対してこれはAIの経路探索に対応するだろう
97年度問題6
99年度問題9
01年度問題7
13年度問題5 ゲームというか視点が関わってくるのが不思議だ


「謎」
02年度問題3
03年度問題11 組み合わせの記法が欲しい
03年度問題12 組み合わせの記法か
04年度問題3 新しい記法が欲しい
05年度問題4 組み合わせの記法か
05年度問題9 表記の問題ではない気がする
05年度問題10 組み合わせの記法か
05年度問題11 組み合わせの記法か
06年度問題3 移動したものの保存
06年度問題4 法則自体を見つけろ
06年度問題8 組み合わせの問題サンプル
07年度問題10 謎ではない気もするが、しかし記述に困る
08年度問題2
08年度問題7
09年度問題4
09年度問題5
09年度問題9
10年度問題4
11年度問題7
11年度問題10
12年度問題7
12年度問題8 グラフの問題だが
12年度問題9 表記の問題だか数列の問題だか
13年度問題3 並びの問題のようでもある
13年度問題6


幾何学の問題」
97年度問題2
97年度問題3
97年度問題5
97年度問題7
98年度問題5
98年度問題7
98年度問題8
99年度問題4
99年度問題7
99年度問題8
99年度問題12
99年度問題14
00年度問題4
00年度問題8
00年度問題9
00年度問題11
01年度問題1
01年度問題2
01年度問題4
01年度問題9
01年度問題10
02年度問題4
02年度問題9
02年度問題10
03年度問題2
03年度問題4
03年度問題7
03年度問題15
04年度問題2
04年度問題5
04年度問題7
04年度問題9
05年度問題8
06年度問題2
06年度問題5
06年度問題7
06年度問題9
07年度問題2
07年度問題3
07年度問題5
07年度問題7
07年度問題9
08年度問題5
08年度問題6 どうモデル化するかまで含めるとこっち
08年度問題9
08年度問題12
09年度問題3
09年度問題10
09年度問題11
10年度問題1 どうモデル化するかまで含めるとこっち
10年度問題3 グラフだが
10年度問題7
10年度問題12
11年度問題9
11年度問題11
12年度問題10
13年度問題4
13年度問題10

もとの6けたの整数はいくつ?

※記法については『算数の問題文の記法のまとめ【随時更新】 - 算数の問題文の記法を考える』を読んでください。


【2000年度ジュニア算数オリンピックトライアル問題7問目】
 6けたの整数があり一の位の数字は9です。今、この「9」を一番上の位に移したら、もとの整数の4倍になりました。もとの6けたの整数はいくつですか。


【記法化】

Aは0から9の整数である;
Bは0から9の整数である;
Cは0から9の整数である;
Dは0から9の整数である;
Eは0から9の整数である;
(100000*A + 10000*B + 1000*C + 100*D + 10*E + 9) * 4 == 900000 + 10000*A + 1000*B + 100*C + 10*D + E;

print(100000*A + 10000*B + 1000*C + 100*D + 10*E + 9)


ちょっと普通の制約プログラミングで解けるか気になったのでCP-SAT ソルバー  |  OR-Tools  |  Google for Developersを参考にして試してみた。

from ortools.sat.python import cp_model

model = cp_model.CpModel()

num_vals = 10
A = model.new_int_var(0, num_vals - 1, "A")
B = model.new_int_var(0, num_vals - 1, "B")
C = model.new_int_var(0, num_vals - 1, "C")
D = model.new_int_var(0, num_vals - 1, "D")
E = model.new_int_var(0, num_vals - 1, "E")

model.add((100000*A + 10000*B + 1000*C + 100*D + 10*E + 9) * 4 == 900000 + 10000*A + 1000*B + 100*C + 10*D + E)

solver = cp_model.CpSolver()
status = solver.solve(model)

if status == cp_model.OPTIMAL or status == cp_model.FEASIBLE:
    print(f"A = {solver.value(A)}")
    print(f"B = {solver.value(B)}")
    print(f"C = {solver.value(C)}")
    print(f"D = {solver.value(D)}")
    print(f"E = {solver.value(E)}")
else:
    print("No solution found.")


結果

A = 2
B = 3
C = 0
D = 7
E = 6


【工夫】
「ABCDE9 * 4 == 9ABCDE」なので、まず左辺の計算結果の下1ケタは6だと分かり、Eは6だと分かる。Eが分かれば下2ケタ目も求められるし、下2ケタ目が求められれば同じように下3ケタ目も求められる。そういうことが普通の制約プログラミングでできるか分からなかったのだけど、どうもできるらしいので工夫はいらないものと考える。

三郎のもっている残りの3個の玉に書かれた数字はそれぞれいくつ?

※記法については『算数の問題文の記法のまとめ【随時更新】 - 算数の問題文の記法を考える』を読んでください。


【2000年度ジュニア算数オリンピックトライアル問題5問目】
 ①~⑫の数が書かれた玉が1つずつ、合計12個あります。
 一郎、二郎、三郎の3人が4個ずつとりました。すると、3人のもっている玉に書かれた数の和が3人とも同じになりました。
 つぎの3つのヒントを読んで問いに答えなさい。
 ヒント1:一郎には⑤と⑫があります。
 ヒント2:二郎には⑥と⑧があります。
 ヒント3:三郎には①があります。
 さて、三郎のもっている残りの3個の玉に書かれた数字はそれぞれいくつですか。


【記法化】

ichiro := [];
ziro := [];
saburo := [];

実行;

[ichiro, ziro, saburo].every := 1つ辺り4つ追加 : 被りなしで全部 := [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12].every;

実行;

sum(ichiro) == sum(ziro) == sum(saburo);
5 in ichiro;
12 in ichiro;
6 in ziro;
8 in ziro;
1 in saburo;

print(saburo);

「[ichiro, ziro, saburo].every := 1つ辺り4つ追加 : 被りなしで全部 := [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12].every;」という表記は正直迷っている。この算数の問題文の記法の本質の1つは、例えば一郎や二郎や三郎が何番を取るかで実行結果が分岐していくことなのだけど、分岐要因の1つである移動がどういう表記であるべきかは定まっていない。


【工夫】

ichiro := [];
ziro := [];
saburo := [];

実行;

[ichiro, ziro, saburo].every := 1つ辺り4つ追加 : 被りなしで全部 := [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12].every;

実行;

sum(ichiro) == sum(ziro) == sum(saburo) == sum([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]) / 3;
5 in ichiro;
12 in ichiro;
6 in ziro;
8 in ziro;
1 in saburo;

print(saburo);

「sum([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]) / 3」なのは当たり前だろとも思うのだけど、内部的にどう振る舞うのかがまだハッキリしてないので、もしかしたら必要かもしれない。そうするとそれぞれにおいて合計26になると分かるので、例えば一郎は「26 - 5 - 12」で残り9。「1、8」「3、6」「4、5」の組は選択肢にないので自動的に「2、7」が残り。二郎も同じように「3、9」。それ以外が三郎と分かる。
しかしこの工夫にしても、色々なことを前提にしてしまっている気がする。例えば一郎の場合に、残りが9であること、更には「1、8」「3、6」「4、5」の組は選択肢にないという判断を内部的に行うと考えて良いものだろうか。良いような気もするし良くないような気もする。

【未解決】この本は何ページの本か?

※記法については『算数の問題文の記法のまとめ【随時更新】 - 算数の問題文の記法を考える』を読んでください。


【1999年度ジュニア算数オリンピックトライアル問題13問目】
 ある本をたろう君は1日目35頁、2日目40頁、3日目45頁と5頁ずつ増やして読んでいったら読み終わった日は35頁ですみました。同じ本を花子さんは1日目45頁、2日目50頁、3日目55頁と5頁ずつ増やして読んでいったら読み終わった日は40頁ですみました。この本は何頁の本ですか。


【記法化】

pages_Aは自然数;
pages_Bは自然数;
pages_A == pages_B;

記録1;
実行;

i := 0;

実行;

while(pages_A != 35) {
    pages_A -= (35 + 5*i);
    実行;
    i += 1;
    実行;
}

i := 0;

実行;

while(pages_B != 40) {
    pages_B -= (45 + 5*i);
    実行;
    i += 1;
    実行;
}

記録1 {
    print(pages_A);
}


【工夫】
解答を見ると、余分なページを前に持ってくるみたいだ。
35、35、40、45……。
40、45、50、55……。
で後者において余分な70ページが1日で読まれることが分かる(増加ペース的に2日以上には分けれない)から、答えは40+45+50+55+60+65+70。

【未解決】20回目にはウサギはどこにいるか?

※記法については『算数の問題文の記法のまとめ【随時更新】 - 算数の問題文の記法を考える』を読んでください。


【2000年度ジュニア算数オリンピックトライアル問題1問目】
 図のように下の左(ア)にはイヌ、下の右(イ)にはネコ、上の左(ウ)にはサル、上の右(エ)にはウサギがいます。今、1回目は上と下を入れかえ(イヌとサル、ネコとウサギを入れかえる)、2回目は左と右を入れかえ、3回目は、また上と下を入れかえ、次はまた左と右を入れかえ・・・・と、次々と入れかえていきます。
 20回目にはウサギはどこにいますか、(ア)~(エ)の記号で答えなさい。


【記法化】

house := [
    ["Saru", "Usagi"],
    ["Inu", "Neko"]
];
実行;

for(i = 1;i <= 20;i++){
    if(i%2 == 1){
        house[0] := house[1];
        house[1] := house[0];
        実行;
    }

    if(i%2 == 0){
        for(i = 0; i < len(house); i++){
            house[i][0] := house[i][1];
            house[i][1] := house[i][0];
            実行;
        }
    }
}

print(house);


【工夫】

house := [
    ["Saru", "Usagi"],
    ["Inu", "Neko"]
];
shuffle_count := 20;
実行;

shuffle_count := shuffle_count % 4;
実行;

for(i = 1;i <= shuffle_count;i++){
    if(i%2 == 1){
        house[0] := house[1];
        house[1] := house[0];
        実行;
    }

    if(i%2 == 0){
        for(i = 0; i < len(house); i++){
            house[i][0] := house[i][1];
            house[i][1] := house[i][0];
            実行;
        }
    }
}

print(house);

この解決策は微妙だ。4回で最初に戻ることを人間側が分析して、その余り分つまり0回実行した。分類としては画像圧縮の仕組みに近いと思う。
人間側の判断すらどうにか自動化できないだろうかとは思うが、houseが最初の状態に戻るまでをカウントしても、シャッフルの過程が同じ振る舞いでなければこのように省略できない。いやシャッフルの過程が同じ振る舞いでも場合によっては省略できないかもしれない。何にせよケースバイケースで、人間の判断が入り込んでいる。
算数の問題がプログラミングの計算量圧縮のアイデアの源泉になり得るんじゃないかとは思っているが、同じような問題が集まってこなければ何とも言えない。