問題概略
6 を 1273 と 9854 にかけると次のようになる。
\begin{align*}
&6 \times 1273 = 7638\\
&6 \times 9854 = 59124
\end{align*}これらの積を連結すると,1 から 9 までを網羅するパンデジタル数 763859124 になる。
763859124 を「6 と の連結された積」と呼ぶことにする。
かける前の数を連結した 612739854 も 1 から 9 までを網羅するパンデジタル数である。
同じことが 0 から 9 までを網羅するパンデジタル数に対してもできる。
かける前の数を連結した数も 0 から 9 までのパンデジタル数となるような,ある整数と 2 つ以上の整数の連結された積が 0 から 9 までのパンデジタル数の最大値を求めよ。
https://projecteuler.net/problem=170
解説の pdf も作りました。きれいなレイアウトで読みたい方はこちらをどうぞ。
右から攻めるか左から攻めるか
パンデジタル数を 個に分割したときの各数を次のようにあらわします。
\begin{align*}
a\cdot b_1=c_1,\, a\cdot b_2=c_2,\, \cdots ,\, a\cdot b_n=c_n
\end{align*}
と ~ を連結した数を「左のパンデジタル数」とよんで,~ を連結した数を「右のパンデジタル数」とよぶことにします。
この問題で要求されているのは右のパンデジタル数の最大値です。
「左」→「右」と「右」→「左」のどちらで攻めるかを考えました。
「左」から「右」を求める方法:
- 左のパンデジタル数を と ~ に分割する
- の結果を連結してパンデジタルかどうか調べる
「右」から「左」を求める方法:
- 右のパンデジタル数を ~ に分割する
- ~ の公約数 を求める
- と を連結してパンデジタルかどうか調べる
- 以上をすべての に対して調べる
「左」→「右」の方が楽ですが,かけ算して連結しないと右のパンデジタル数がどんな数なのかわからないのが難点です。
本当に全探索しないと最大値は求められないので,計算量の面から「右」→「左」でいくことにしました。
実例を 1 つ探す
0~9 を使ったパンデジタル数は 個あります。
これらに対してすべての分割法を試すのは多分無駄なので,「適当な仮定をおいて実例を 1 個探す」→「その数より大きい数についてちゃんと調べる」でいこうと思いました。
「分割数 かつ は 2 桁以下」と仮定して計算しました。
In[]:= AbsoluteTiming[ lst = Select[Permutations@Reverse@Range[0, 9], First@# != 0 &]; testk[lst_, k_] := Module[{c1, c2, aLst}, {c1, c2} = FromDigits /@ TakeDrop[lst, k]; aLst = Select[Divisors@GCD[c1, c2], # < 100 &]; AnyTrue[aLst, Sort[Join @@ IntegerDigits /@ {#, c1/#, c2/#}] == Range[0, 9] &]]; isok[lst_] := AnyTrue[Range@9, testk[lst, #] &]; ans = FromDigits@SelectFirst[lst, isok]] Out[]= {5.51237, 9857164023}
各行の内容はこうです。
- 2 行目:解の候補となるパンデジタル数の桁数字のリストを作り,降順に並べる
- 4 行目:桁数字のリストを先頭の 個と残りにわけてそれぞれ数に直す(, )
- 5 行目:, の公約数 で 2 桁未満のもののリストを作る
- 7 行目:, , を連結してパンデジタルかどうか調べる
- 9 行目:条件をみたす最初の桁数字のリストを抽出して数に直す
これでみつけた「9857164023」より大きなパンデジタル数は意外に多くて,10565 個ありました。
これらすべてに対して「右」→「左」のチェックをするのは大変なのでもう少しくわしく調べます。
分割数などをしぼりこむ
分割数をしぼりこむためのコードを書きました。
以下, の桁数を であらわします。左のパンデジタル数の桁数の条件から
\begin{align*}
len(a)+\sum_{i=1}^n len(b_i)=10\mbox{ ……(1)}]
\end{align*}
一般に 桁の数と 桁の数の積は 桁か 桁です(例:, )。
が 回おこるとします。右のパンデジタル数の桁数の条件から
\begin{align*}
&\sum_{i=1}^n \left\{len(a)+len(b_i)\right\}-k=10\\
&\therefore n\cdot len(a)+\sum_{i=1}^n len(b_i)-k=10\mbox{ ……(2)}
\end{align*}
(1)(2)を辺ごとに引くと を得ます。
, , としてこれを解きました。
eqn = {(1 - n) len_a + k == 0, 1 <= len_a <= 8, 2 <= n <= 9, 0 <= k <= n}; sol = Reduce[eqn, {n, len_a, k}, Integers]
(3)をみたす は次の 9 個でした。
\begin{align*}
&(2,\, 1,\, 1),\, (2,\, 2,\, 2),\, (3,\, 1,\, 2),\, (4,\, 1,\, 3),\, (5,\, 1,\, 4)\\
&(6,\, 1,\, 5),\, (7,\, 1,\, 6),\, (8,\, 1,\, 7),\, (9,\, 1,\, 8)
\end{align*}
と は「実例を 1 つ探す」で調べたケースです。
残り 7 パターンについては実は調べる必要がありません。
これらすべてが「 かつ 」であることに注目しましょう。
個の のうち 個では が成り立ちます。
残り 1 個の では が成り立ちます。
これらを使うと のケースはすべて のケースに帰着できることが示せます。
の場合を考えます。
, , として一般性を失いません。
とおきます。(4)を 倍して(5)と辺ごとに足すと次のようになります。
要するに 2 つの式を連結して1つにできるわけです。
これを繰り返すと ~ のケースはすべて「, , 」のケースに帰着できます。
結局,「実例を 1 つ探す」で求めた例がこの問題の解だったわけです。
高速化のための微調整
条件をみたすパンデジタル数を全部求めてみたら はすべて 48 以下の 3 の倍数でした。
これを証明して計算にいかします。
a は 3 の倍数
, を辺ごとに足します。
パンデジタル数の条件から , , の桁数字の和も , の桁数字の和も で 3 の倍数です。
(7)を で考えます。
のうちこれをみたすのは だけなので は 3 の倍数です。
a は 50 未満
の場合を考えます。これは「分割数などをしぼりこむ」の のケースです。
\begin{align*}
&a\cdot b_1=c_1,\, a\cdot b_2=c_2\\
&len(a\cdot b_1)=len(a)+len(b_1)-1=len(b_1)+1\mbox{ ……(8)}\\
&len(a\cdot b_2)=len(a)+len(b_2)-1=len(b_2)+1
\end{align*}
, の少なくとも 1 つは首位の数字が 2 以上です。
の首位の数字が 2 以上だとしましょう。
これを 倍すると 2 桁以上増えます。
これは(8)と矛盾しているので です。
再計算
以上をふまえてもう一度計算しました。計算時間は約 4 秒です。
もともとの計算時間が約 5.5 秒で,その半分は候補となるパンデジタル数の集合を作るのに費やされているので純粋な計算時間はだいたい半分になりました。
In[]:= AbsoluteTiming[ lst = Select[Permutations@Reverse@Range[0, 9], First@# != 0 &]; testk[lst_, k_] := Module[{c1, c2, aLst}, {c1, c2} = FromDigits /@ TakeDrop[lst, k]; aLst = Intersection[Divisors@GCD[c1, c2], Range[3, 50, 3]]; AnyTrue[aLst, Sort[Join @@ IntegerDigits /@ {#, c1/#, c2/#}] == Range[0, 9] &]]; isok[lst_] := AnyTrue[Range@9, testk[lst, #] &]; ans = FromDigits@SelectFirst[lst, isok]]