くどー問題とその数値計算
Mathlogでくどーさんがとある数学の問題を記事にされています:
そろそろ誰かに解いてほしい自作問題(未解決)
私はこれを「くどー問題」と呼んでいます。問題の内容は上記記事を見ていただくのがよいと思いますが、一応引用させていただきます:
からまでの整数が書かれたカードが1枚ずつある。この時、以下の条件を満たしながらカードを枚ずつ取り除いていくことを考える。
- どの時点においても「カードに書かれた数の乗和」が「カードに書かれた数の和」で割り切れる
- とのカードは最後の枚になるまで残す
この時、条件を満たしながらカードがなくなるまで取り除くことができるようなは無数に存在するか?
くどーさんは条件を満たすとしてを見つけています。またでは条件を満たすは存在しないことを数値計算で確かめてもらった記憶がある、と述べています。
これに関し私は以前数値計算を行いました:
「くどー問題」の数値計算
この記事ではの範囲で数値計算を行い条件を満たす数を探索しましたが、以外見つけることはできませんでした。
今回はJuliaというコンピュータ言語を用いてまでの範囲の殆どの数(※20個を除く)に関して検証を行いました。結果は以下です:
において以外条件を満たす数はみつかっていない
(※そのうち20個のに関しては条件を満たすか否か判明していない) 本記事ではこれに関して説明します。前回同様、今回も整数論的考察はないです。また前回の記事は読まなくても大丈夫です。
用語の定義
まず議論に必要な用語を定義します。
議論に必要な用語
以下は最大のカードの番号を表す
以下はカードを取り除く枚数を表す。取り除くことを以下「削除」と呼ぶ。各に対しはの値を取る
番号の削除の列をのように四角カッコで表すことにする。ここではからのどれかの数字(重複はない)であり、左の数字から初期のカードの集合の対応する要素を削除していく。
以下の条件を満たすときを「可能な削除列」と呼ぶことにする:
に含まれる数字をから削除した数字の集合をとして、がで割り切れる。さらにを満たすすべての整数に対しても同様の条件:「に含まれる数字をから削除した数字の集合をとして、がで割り切れる」を満たす
例えばの場合、可能な削除列は各で以下のようになる:
では削除の各段階で1通りしか可能な削除列が存在しないが、一般には複数存在する。
を「枚のカードから枚を削除するときの可能な削除列の集合」とする。なら上記のとおり
である。もっと非自明な例として、の場合は
である。
を「枚のカードから枚を取り除いたときに可能な残りのカードの組み合わせの集合」とする。例えばの場合
である(残ったカードに関してはその数字の並び方は重要ではないので集合の記号で表す)。
(は有限集合)はの要素の数を表す:
を「あるに対しを満たす最大の」とする(の場合とする)。例えば
となる。これは「可能な削除列が存在する最大の」である。なら最後までカードを削除できる。
以上からくどーさんの提示した問題は
「となるは無限に存在するか?」と言えます。数値計算の概要
計算アルゴリズムは単純です。集合から要素を1つづつ取り除いて条件を満たすか確認し、を各で逐一構成することでを計算します。
前回の記事ではすべてのを計算しました。しかしそれだと計算量が膨大になります(前章のとを比較してみてください)。そこで今回は基本的にのみ計算し、そこに至るまでの経路()は必要な場合だけ計算しました。
結果
の依存性
まずをに対してプロットしてみます。
との関係
図1は各に対するのグラフです。青の点が、緑の線はのグラフです。この線に青点が乗れば、そのは最後までカードを削除できる数字です。オレンジの上向き三角形は探索が終了していないに対するの下限を示しています。つまりこれらのでははこれより大きいことしか判明していません。
図1の詳細は下のリンクのグラフからアクセスすることができます:
インタラクティブなグラフ(図1)
このグラフは拡大・縮小することができ、かつ各データポイントにカーソルを重ねればそのデータのが表示されます(計算の終わってないデータではが表示される)。
これらより以下がわかります:
- いまのところ以外で条件を満たす数は見つかっていない。特にには条件を満たす数は存在しない
- 多くのではまたはである。ある程度大きなでははに対しのように0と2がそれぞれ2回づつ繰り返される傾向が見られる
- 付近にが存在する一連のが存在する。ある程度大きなに対するは0付近または付近以上に存在し、「ギャップ」が見られる
これに関してコメントです:
の構造を持つ部分の拡大図
このような周期的な変化は、がある程度大きくなれば普遍的に現れます。よろしければインタラクティブな図を拡大して確認してみてください。
- 3.に関して
- 付近に存在するは、およびも特徴的な性質を持ちます。これに関しては次章で述べます。
ギャップに関しては、探索が終了していないのが0からの中間付近の値を持つ可能性はあり、そうであればギャップはそれほど明確ではなくなります。しかしながら、探索の終了していないはがあるから指数的に大きくなり、経験的にはその場合より有意に小さなをもつことはないです。なのでギャップの存在はロバストであるように思います。あくまで予想ではありますが。
2.3.は何らかの理論的背景を持つと思うのですが、いまのところよくわかりません。
「カードの削除数」と「残りのカードの可能な組み合わせの数」の関係
次にとの関係を見てみます。すなわち各における「カードの削除数」と「残りのカードの可能な組み合わせの数」との関係を調べてみます。
がある程度大きい時、 vs は次の4つのパターンに分けられます:
- 程度で終わるため、の依存性を見る意味があまりない
- 付近にを持ち、はずっと程度
- の線より有意に大きいを持ち、はずっと程度
- の線より有意に大きいを持ち、はあるまで程度であり、その後指数的に大きくなる
1.は図1のに存在する点です。殆どのはこれに分類されます。
2.の典型的なは例えば()であり、 vs をプロットすると次の図のようになります:
における vs
図3のように殆どのに対しては、時にになることがあり、そしてそのような小さい値を保ったまま最終的にゼロになります(※ゼロの点は示されていません。一番右側の点が)。
より有意に大きなをもつの場合、2.と同様にが小さい値を保ったままゼロになる場合と、途中で指数的に大きくなる場合の2つに分けられます。3.は前者であり、例えばはその例です。の図をプロットすると以下のようになります:
における vs
これは図3と同様の振る舞いです。よりはが2になる回数は多いですが、結局小さい値を保ったままゼロになります。
一方4.の例はなどです。この場合が指数関数的に増加したのちにゼロになるまでの振る舞いに違いがあります。そのうち典型的なを図示します(軸logプロットです):
における vs (軸 log scale)
における vs (軸 log scale)
における vs (軸 log scale)
図5,6,7に対応するインタラクティブなグラフへのリンクも貼っておきます:
では指数的増加ののちすぐにがゼロになります。では一度小さくなってからもしばらくある程度の値を保ち、そして最後は急激に小さくなってゼロになります。では指数増加する領域が2つあり、小さくなってからもある程度の値を保つ領域を経てゼロになります。
exponentの計算
ここで指数的増加のexponentを求めてみます。指数的増加を示す領域において2点をとりexponent (で振る舞う)を計算します。そのような領域が複数存在する場合はそれぞれの領域で別々にを求めます。さらにとすると、はに対するの平均的な増加率:の指標となります。 以下の表がの計算結果です:
を求めるのに2点とを用いています。"region"は指数的増加領域が複数ある場合の各領域を示しています。の値より、指数増加領域ではが1増えるときは平均してだいたい1から3%程度増加することがわかります。
注意してほしいのは、この増加率はの増加率とは違うことです。前回の記事で調べたの範囲()ではは1.2から1.4程度でした。今回はちゃんと計算していませんが、がからくらいでもおそらく同じようなものだと思います。これが示しているのは、違うの要素の違う数字の削除が同じの要素をもたらすことがけっこうある、ということです。実際今回の数値計算においての要素に対して削除作業をすると同じの要素に行き着くものが多くあるので、そのredundancyを取り除く作業をしています(「定義1」の4.と5.に示したとを比較してみてください)。
の依存性
これは前回の計算でもわかっていたのですが、がある程度大きくなるようなではは非常に特徴的な振る舞いをします。言葉で言うよりを見たほうが早いので、幾つか具体的に例を示してみます。まずのにおける(の一部)を示します:
- n=19, m=14(=mmax):
[15, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 16, 2, 18]
[15, 4, 5, 6, 7, 8, 9, 16, 18, 12, 3, 17, 10, 11, 13]
[15, 4, 5, 6, 7, 8, 9, 16, 18, 12, 3, 17, 10, 11, 14]
[15, 4, 5, 6, 7, 8, 9, 16, 18, 12, 3, 17, 10, 14, 11]
[15, 4, 5, 6, 7, 8, 9, 16, 18, 12, 10, 17, 3, 11, 13]
[15, 4, 5, 6, 7, 8, 9, 16, 18, 12, 10, 17, 3, 11, 14]
[15, 4, 5, 6, 7, 8, 9, 16, 18, 12, 10, 17, 3, 14, 11]
[15, 4, 5, 6, 7, 9, 8, 10, 11, 12, 13, 14, 16, 2, 18]
[15, 4, 5, 6, 7, 9, 8, 16, 18, 12, 3, 17, 10, 11, 13]
[15, 4, 5, 6, 7, 9, 8, 16, 18, 12, 3, 17, 10, 11, 14]
[15, 4, 5, 6, 7, 9, 8, 16, 18, 12, 3, 17, 10, 14, 11]
[15, 4, 5, 6, 7, 9, 8, 16, 18, 12, 10, 17, 3, 11, 13]
[15, 4, 5, 6, 7, 9, 8, 16, 18, 12, 10, 17, 3, 11, 14]
[15, 4, 5, 6, 7, 9, 8, 16, 18, 12, 10, 17, 3, 14, 11]
- n=59 m=21(=mmax)
[30, 29, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28]
[30, 29, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 53]
[30, 29, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 27, 26, 28]
[30, 29, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 27, 26, 53]
[30, 29, 10, 11, 52, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28]
[30, 29, 10, 11, 52, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 38]
[30, 29, 10, 11, 52, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 32, 58, 57, 56, 55]
[30, 29, 10, 11, 52, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 54, 42, 57, 55, 44]
[30, 29, 10, 11, 52, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 54, 42, 57, 55, 56]
[30, 29, 10, 11, 52, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 54, 42, 57, 56, 43]
...
- n=119, m=51(=mmax)
[60, 10, 11, 12, 13, ..., 56, 57, 58, 59]
[60, 10, 11, 12, 13, ..., 56, 57, 58, 94]
[60, 10, 11, 12, 13, ..., 37, 38, 39, 94, ..., 56, 57, 58, 40]
...
ではすべての、では一部のを示しました。これをみると
最初1つ(または2つ)の削除可能な数字があり、その次から連続した整数が並ぶ。最初に削除可能な数字はが大きいとに近い。という特徴があることがわかります。次にの依存性をが小さな領域で見てみます。例えば上に存在するであるに関してこれを見てみると以下のようになります:
これも最初の削除可能な数字がであり、次にと続き、実にまで連続した数字が並びます。しかもまでは可能な削除列は1つだけであり、になって初めて2つ現れます。
これらの傾向はが大きくなるに関してかなり普遍的に当てはまります。またこれはなんとなくなのですが、そのようなは一桁目がやであることが多いように見えます(インタラクティブなグラフで確認してみてください)。
まとめ
くどーさんのご提案された問題:
そろそろ誰かに解いてほしい自作問題(未解決)
をJuliaによる数値計算により調べました。その結果以下の結果を得ました:
- の範囲でくどー条件を満たす数は見つからなかった(※20個だけ条件を満たすか判明していない数が存在する)
- 多くのでは0または2。ある程度大きなにおいて、はに対し0,0,2,2,0,0,2,2,...のように0と2がそれぞれ2回づつ繰り返される振る舞いをもつ
- 付近にをもつ一連のが存在する。ある程度大きなのは付近または付近以上に存在し、ギャップが見られる
- ある程度が大きくなるにおけるのに対する振る舞いは、ずっと1のオーダーでそのままゼロになるか、またはあるから指数的に大きくなるかの2つに大きく分類される。そのうち指数的に増加する場合、その指数的増加領域におけるはだいたいからである。
- が大きくなるのは、その要素の削除列を見ると、連続した整数が出現する。削除列の最初の数はが大きい時ほぼ。
これらの現象の理論的背景はいまのところわかりません。
くどーさんのオリジナルの問題「条件を満たしながらカードがなくなるまで取り除くことができるような𝑛は無数に存在するか?」ですが、これに関しても今のところ何も言えないです。数値計算でこれにアプローチするには、条件を満たすを数多く求め、あるまでで条件を満たす数の出現頻度を求めることが必要かと思います。また条件を満たす数の何らかのルールを見出すことが重要です。しかし今のところ3,6以外で条件を満たす数が求まっていないです。まずは6より大きな数で条件を満たすものを見つけるのが目標かと思います。ただ数値計算では、アルゴリズムに何らかの根本的な工夫をするか、または大規模計算機を使わないとこれ以上はなかなか厳しいかもしれません。
あと逆方向に探索することもできることを付記しておきます。これはのカードの集合にカードを付け加えながら3乗和が1乗和で割り切れるか調べ、最終的に集合がからのカードすべてを持つかどうかを確認する探索法です。前回の記事ではこれを行ったのですが、非常に多くのパターンが現れるため数値計算が重くなりあまり有効ではありませんでした。ただ、前回は違う「付け加え」で結局同じ状態に行き着く場合の重複を除かずに計算していたので、これを取り除くと計算が軽くなるかもしれません。計算が非常に重くなるに対しては、両方向から計算し、のところで両者を比較することで完全なカードの取り除きが可能か調べるのもよいかもしれません。
個人的な感想ですが、この問題が何らかの物理系と対応がつくと面白いように思います。テキトーな話ですが、「割り算の余り」と関係するといえば磁場中の量子系のエネルギー準位が思い浮かびます。Aharanov=Bohm効果、Diracの量子化条件のように、磁場中の半古典的な量子化条件では「割り切れる」ことが重要です。また量子ホール効果のエネルギー準位にも「余り」が登場します。しかしながら、からの3乗和が現れ、それを1乗和で割った余りがエネルギー準位や量子化条件等に現れるような物理系は存在するでしょうか...。
おしまい。
Appendix
Juliaのコード
Juliaは2012年に最初のバージョンがローンチされた比較的新しいコンピュータ言語ですjulia。Pythonと似通った言語でありながらCやfortranに匹敵するほど計算が高速であるという特徴を持ちます。Pythonを使える人ならJuliaもすぐに使えるようになると思います。
参考までに今回使用したコードをGistにアップロードしましたjulia_code。使い方を以下に説明します
コードおよびパラメータの設定に関する説明
本コードは与えられたに対してを計算します。計算量が大きくなる場合を想定し、にも範囲を設け、その範囲で計算して結果をファイルに出力します。そしてファイルから結果を読み出すことで計算を途中から再開できるようにしてあります。
本コードには5つのパラメータを入力する必要があります:
- : スレッド並列の並列数
- : 本文の(カードの枚数)
- : 読み取りファイルのファイル名に記載されているの最小値 (ファイルがない場合1にする)
- : 読み取りファイルのファイル名に記載されているの最大値 (ファイルがない場合1にする)
- : 計算するの最大値(※本文におけるとは関係ないことに注意)
1.はマシンの環境等に合わせて設定してください。2.5.は説明の必要はないと思います。
3.4.は説明が必要です。例えば、をからまで計算したいとします。初めて計算する場合
※これはご自身の環境に合わせて設定してください
とします。とすると、ファイルからデータを読み出さず、最初から(から)計算を行います。計算が終わると
というファイルが作成されると思います。
この結果を用いて、をからまで計算したいとします。その場合は上記ファイル名の1と100をとに設定してください:
このときコードはからにおける計算結果を読み出し、から計算を開始し、まで計算します。
設定したまでで計算が終了した場合(すなわち本文のがパラメータ名のよりも小さいとき)、ファイル名に""が添付されます。例えばの場合、(※本文の)なので
とすると計算はまでで終了します。このとき作成されるファイルは
という名前になります。
アウトプットファイルの中身
アウトプットファイルはHDF5という形式で作成されます。Juliaでなくとも扱えるファイル形式かと思います。
juliaのREPLというインタラクティブなモードでファイルの構造を見てみます(、最初に"using HDF5"というコマンドを実行しておく):
julia> file_open = h5open("KudoProb_n-27_mmin-1_mmax-100_finished.dat","r")
🗂️ HDF5.File: (read-only) KudoProb_n-27_mmin-1_mmax-100_finished.dat
├─ 🔢 list_length_pos_list
└─ 🔢 m_final
list_length_pos_listには、各に対するが記されています:
julia> list_length_pos_list = read(file_open,"list_length_pos_list")
2×9 Matrix{Int64}:
1 2 3 4 5 6 7 8 9
1 1 1 2 1 1 1 1 0
データは行列の形で保存されており、1行目が、2行目がそのに対応するです。m_finalは初めてとなるであり、つまりはです:
julia> m_final = read(file_open,"m_final")
9
計算が終わらなかった場合は以下のような構造を持つファイルが出力されます:
julia> file_open2=h5open("KudoProb_n-143_mmin-1_mmax-100.dat","r")
🗂️ HDF5.File: (read-only) KudoProb_n-143_mmin-1_mmax-100.dat
├─ 🔢 list_length_pos_list
├─ 🔢 m_final
└─ 🔢 pos_list
計算が終わらなかった場合はm_finalには入力したが代入されます。またこの場合、におけるもpos_listという名前で保存されます。このファイルは時にかなり大きなサイズになるので(今回計算した範囲だと1GB程度になることもある)ご注意ください。
ソースファイルで修正を施すべき箇所
ソースファイルで変更するべき場所は2箇所(以下でDirectInput()を有効化した場合は144-152行目も)です。
1箇所目166,167,168行のうち1つを有効化します(=どれかひとつだけコメントアウト記号#を外す)
- DirectInput()を有効化した場合、インプットパラメータとしてソースファイル中の144-152行に記述されたものを読み取ります。よってこの場合144-152行のインプットパラメータを書き換えてください。
- ArgInput()を有効化した場合、インプットパラメータをjuliaコマンドに与えられた値から読み取ります。juliaコマンドへ引数を渡すには以下のようにします:
julia calc_KudoProb_parallel.jl (n_threadの値) (nの値) (mmin_fileの値) (mmax_fileの値) (mmaxの値)
- StandardInput()をactivateした場合、インプットパラメータはコード実行時に標準入力から入力します。この場合、コードを実行してから指示に従ってこれらの値を入力してください。
多くのに対して計算を行う場合はArgInput()を有効化し、juliaコマンドを様々なパラメータに対してシェルスクリプトに記述し、そのシェルスクリプトを実行するのがよいと思います(プロセス並列もできるし)。
2箇所目170行目のpath_fileを設定します。この変数にデータファイルを保存するディレクトリを指定します。
基本的にはこれ以外の部分を変更する必要はないです。
その他注意
- スレッド並列化しています。でもそれほど速くはなりません(4スレッド、8スレッドでも倍の速さにはならない)。たぶんもっとよい並列化の方法があると思います。またGoogle Colaboratoryで計算していると時折segmentation faultにより計算がダウンします。おそらく並列化の影響だと思うのですがよくわかっていません(スタック領域の問題?)。Colaboratoryでも計算は他のマシンでのJuliaによる計算結果およびPythonでの計算結果を再現しているので信用できると思いますが、完全な保証はできませんのでご注意ください。
- 特定のでは取り除きのパターンが非常に多くなり計算が大変重くなります。そのため保存ファイルの大きさが1GB近くになる場合があるのでご注意ください。
- リストの要素は基本Int16で扱っています。よって扱えるの上限はですのでご注意ください(1乗和・3乗和の計算では精度をInt64に変換しています)。Int32やInt64にすればもっと大きなまで扱えますが、その場合使用メモリも保存されたデータファイルも大きくなります。
- (ある意味重要)ソースコードは単純です。本質は"Kudo_prob_ver06Apr2024"という関数の12行目だけです。ある程度知識がある方は、ご自身で使いやすいように書き換えてもらうのがよいと思います。