🌊

【数字】コラッツ予想を解きたい!16

2022/12/18に公開

前回のまとめ

前回はいろいろ検証しましたが、最終的には「3」というものが満たすべき条件というのを考えるべきというところで終わりました。

追加する条件

「3」が満たす条件を抽象化したいところですがどのようにするべきでしょうか。

普通に考えると、

\frac{3}{2} \cdot \frac{1}{2} < 1

であることに加え、
「3」は分母に変更を加えないので、小さくなっていけば、収束することができるという2点でしょう。

ただ、2つも条件があるというのもなんか気持ち悪いし、スッキリ書くことはできないか・・・と思うと、1つありそうなことに気付きます。

f = t_e \circ t_o

とすると f はいつか収束する……というような条件はどうでしょうか。

ただ、このままだと、

\begin{align*} t_o:\ &O \rightarrow \mathbb{J} \\ t_e:\ &E \rightarrow \mathbb{J} \\ \end{align*}

なので、修正が必要です。
t_o を3倍してから、\mathrm{next} を作用させるという風にみなすと、
g(x) = 3x\mathbb{J} \rightarrow \mathbb{J} の関数ですから、

t_o = \mathrm{next} \circ g

と定義すればこれは \mathbb{J} \rightarrow \mathbb{J} の関数となります。
同様に、t_e についても、

t_e = \mathrm{next}

とすれば、f が全域で定義できて、「3」が満たすべき条件を次のように一般化できると思います。

\begin{align*} \forall a \in L,\ & \exists n,m \in \mathbb{N},\ \\ & f^n(a) = f^m(a) \end{align*}

と書けるのではないでしょうか。
ただし、f^nOE が交互にあるという定義の仕方をしましたが、より抽象化して、OE の比率が1:1になる場合、収束するという風な定義の方がそれっぽい気がします。

つまり、OEの並び * が比率が1:1に収束するならば、任意の a \in L について、ある n, m が存在して、

t_*^n(a) = t_*^m(a)

となる。

解くための方針

コラッツ予想を解くためにどういう風な議論で解くのか。

発想としてはいろいろあると思いますが、僕のイメージは次のような方法です。

ループ集合 L について、t を何度作用させても収束しない要素があったとすると、その要素に作用させた t_o, t_e の比率は1:1に収束することを示す。

そのとき、上で考えた条件から、その要素の収束が言える。

なぜこのような方法が良さそうかというと、L の外の要素を考える意味があるような展開にしたいからです。(せっかく巡回列空間 \mathbb{J} も作ったことですし)

ちなみにこのような議論が成り立つような t の条件は分かっていないのですが、具体的に計算して調査してみると以下のようなものに広く成り立つようです。

t(x) = \left\{ \begin{align*} x/2 \quad & (x \in E) \\ \frac{5x-1}{2} \quad & (x \in O) \end{align*} \right.

3倍ではなく5倍のやつや、

t(x) = \left\{ \begin{align*} x/3 \quad & (x \equiv 0 \mod 3) \\ \frac{5x-1}{3} \quad & (x \equiv 2 \mod 3)\\ \frac{7x-1}{3} \quad & (x \equiv 1 \mod 3) \end{align*} \right.

2てはなく3で割るタイプの関数とか。

とりあえず、こんな風に有理数範囲の演算なら、収束しない要素について、OE比率は1:1になりそうです。

四則演算を使わないで考えてみる

大まかな方針は見つかったとして、それじゃあこっからどうしていくか?

まずは、

OEの並び * が比率が1:1に収束するならば、任意の a \in L について、ある n, m が存在して、

t_*^n(a) = t_*^m(a)

となる。

という性質が、和や積を使ったものではないということに着目します。
コラッツ予想というものを、和、積を使わずに表現したときどうなるかを見てみて、視点を得ようと思います。

今考えている関数はすべて、第 n 項まで一致する2つの要素 a, b について、f(a), f(b) ともに第 n 項までがが一致するという性質を維持するようなものである。

つまり、関数を 0, 1 の羅列で表現するならば、
    
    ┏━━0
 ┏━━0
 ┃  ┗━━1
start
 ┃  ┏━━1
 ┗━━1
    ┗━━0

こんな感じで倍々で増えていくような表現を用いて表せる。
上の例だと、

f(00...) = 00.. \\ f(01...) = 01.. \\ f(10...) = 11.. \\ f(11...) = 10..

ということを表しているみたいな感じで表現できる。

このような形で、例えば f(x)=3x を表現しようとしたら、どうなるでしょうか。

┏━━0(a)
start
┗━━1(a)

┏━━0(a)
0(a)
┗━━1(a)

┏━━1(b)
1(a)
┗━━0(b)

┏━━0(c)
0(b)
┗━━1(c)

┏━━0(a)
1(b)
┗━━1(a)

┏━━1(b)
0(c)
┗━━0(b)

┏━━0(c)
1(c)
┗━━1(c)

このような形で表現することができるようです。
これの意味としては、
       
       ┏━━0(a)
    ┏━━0(a)
    ┃  ┗━━1(a)
 ┏━━0(a)
 ┃  ┃  ┏━━1(b)
 ┃  ┗━━1(a)
 ┃     ┗━━0(b)
start
 ┃     ┏━━0(a)
 ┃  ┏━━1(b)
 ┃  ┃  ┗━━1(a)
 ┗━━1(a)
    ┃  ┏━━0(c)
    ┗━━0(b)
       ┗━━1(c)

という感じです。

1 = 100000...

start--> 1(a)--> 1(b)--> 0(a)--> 0(a)--> ...

より、

3 = 110000...

となる。
他には、

1/3 = 1101010...

start--> 1(a)--> 0(b)--> 0(c)--> 0(b)--> 0(c)-->...

より、

1 = 100000...

となる。
他には、

-3/7 = 110110110...

start--> 1(a)--> 0(b)--> 0(c)--> 0(b)--> 1(c)--> 0(c)--> ...

より、

-9/7 = 10001001001...

となる。

このような発想を用いれば、和や積を使わずに表現すること自体は可能です。

ただこれをどう扱っていくのかは難しいところです。

また、このような考察から、1つ非常に基礎的な関数を考えることもできます。

┏━━0
start
┗━━1

┏━━0

┗━━1

┏━━1

┗━━0

によって構築される関数です:
       
       ┏━━0
    ┏━━0
    ┃  ┗━━1
 ┏━━0
 ┃  ┃  ┏━━1
 ┃  ┗━━1
 ┃     ┗━━0
start
 ┃     ┏━━1
 ┃  ┏━━1
 ┃  ┃  ┗━━0
 ┗━━1
    ┃  ┏━━0
    ┗━━0
       ┗━━1

この関数を u としたときに、

t = \mathrm{next} \circ u

を考えて、これの j_t について考察するというのは、最も基礎的なものの1つと見なせると思います。

例えば、a = 111111... に対し、j_t を計算をしていくとどうなるか。

\begin{align*} t(a) &= 010101.. \\ t^2(a) &= 11001100.. \\ t^3(a) &= 00010001.. \\ t^4(a) &= 00111100.. \end{align*}

という感じになっていく。
ループはしなさそうてすが、考えたいところは、OE比率が1:1になってくれているかどうかというところである。

で、実際にやってみると、比率は1:1になってくれないようです。

1010001000000010000000000000001000000000000000000000000000000010000000000000000000000000000000000000...
ソースコード
python
N = 100
a = [1] * N
output = []

def u():
	node_id = 0
	for i in range(len(a)):
		if(node_id == 0):
			node_id = a[i]
		else:
			if(a[i] == 0):
				a[i] = 1
			else:
				a[i] = 0
			node_id = a[i]
	return

def next():
	output.append(a[0])
	print(a.pop(0),  end="")

for i in range(N):
	u()
	next()		

となると、奇数と偶数で分けたらどうだろうと考えてみたくなります。

つまり奇数のときはこの u 使い、偶数のときは特に何もしないという方法です。

実際にやってみると、比率は1:1になりそうではあったのですが、連続して同じ数字が並ぶことが多すぎる感じでした。

1011100111111000011111111111100000000111111111111111111111111000000000000000011111111111111111111111...

これはどのように捉えるべきでしょうか。
OE比率は確かに1:1ではありそうですが、これでよいのでしょうか。

イメージとしては、OE比率が1:1であるとは、言い換えると、「ランダム」であるということです。
ランダムに0と1を選択していって、それをずっと続けていけば比率は1:1になって欲しい。
なればこそ、00, 01, 10, 11 という区切りかたでみたときも、比率1:1:1:1になっていて欲しいわけですし、任意の長さの区切りかたに対して、比率1:1:...:1になって欲しいところです。

このような「ランダム」かどうかという見方をした場合、上の例は満たさないということになります。

このことはどういうことを示唆しているのでしょうか。
コラッツ予想を四則演算を用いずに表現することはできますが、より本質的なのは四則演算を用いた表現である可能性が高いということです。

ランダムな列

今の僕のたちの方針としては比率1:1かどうかを判定したいのですが、そもそも比率1:1(ランダム)である要素の例を実は何も知らないという状況です。

ランダムになるっぽいな~、といっているだけで、じゃあ具体的にランダムな要素ってなんですかと言われたら、答えられない状況になります。

そこで1つ考えたいのは、「簡単なランダム列の例」があるかというところなわけです。
前の項ではうまく見つけられませんでしたが、実はもう1つ候補はありました:

これは第08回で少しみたものですが、よくよくみるとランダム性を獲得しているのではないでしょうか。

第08回抜粋

問:(3/2)^n はループするか

分割 E \subset \mathbb{R} を、

E = \sum_{i \in \mathbb{Z}}\ (2i-1,\ 2i \rbrack

とし、\mathbb{R} 上でツリー T=\mathrm{Tree}(t) を次のように定義する:

t(x) := \frac{3}{2} x

このとき、1 を起点とした巡回列

\chi_E(1),\ \chi_E(3/2),\ \chi_E(9/4),\ \chi_E(27/8),\ ...

はループするか?

これは意外と難問ですか。
いや、そもそも...

コンピューターの力を借りて計算してみます。すると...

0,\ 1,\ 0,\ 1,\ 1,\ 1,\ 1,\ 1,\ 1,\ 0,\ 1,\ 0,\ 1,\ 0,\ 1,\ 1,\ 0,\ 1,\ 1,\ 0,\ 1,\ 1,\ 1,\ 0,\ 0,\ 1,\ 0,\ 1,\ 0,\ 0,\ 1,\ 0,\ 1,\ 1,\ 1,\ 1,\ 0,\ 0,\ 1,\ 0,\ 0,\ 0,\ 1,\ 0,\ 0,\ 1,\ 1,\ 0,\ 1,\ 0,\ 0,\ ...

というような巡回列になります。
あまり規則性は感じられません。これはおそらくループしないでしょう。

これは、ランダムな列になっているような気もします。
ただ、、、これ、今みると正しくないかもしれません。何度も1.5倍しているので、誤差が蓄積してたり、数値が大きくなりすぎたりする危険もあります。。。

プログラムで回すなら、すべて整数で完結させるべきでしょうし、倍率もなるべく小さくした方がよさそうです。

t(x) := [\frac{101}{100} x]

このようなガウス記号を用いた1.01倍する関数の場合どうなるでしょうか。

ソースコード
python
a = 1000
count_E = [0]
count_O = [0]

count_EE = [0]
count_OE = [0]
count_EO = [0]
count_OO = [0]

def multiple_3over2(x):
	return 101*x // 100

def judge_OE(x):
	u = x % 2
	if(u == 0):
		count_E[0] += 1
		print("0", end="")
		return
	count_O[0] +=1
	print("1", end="")
	return
	
def judge_OE_2(x, y):
	if(x == 0):
		if(y == 0):
			count_EE[0] += 1
			return
		count_EO[0] += 1
		return
	if(y == 0):
		count_OE[0] += 1
		return
	count_OO[0] += 1
	return

old_a = 0
for i in range(1000):
	a = multiple_3over2(a)
	judge_OE(a)
	if(i>0):
		judge_OE_2(old_a, a%2)
	old_a = a%2
	
print("")
print(a)
print("E, O:", count_E, count_O)
print("EE, OE, EO, OO:", count_EE, count_OE, count_EO, count_OO)

第1000項までやってみると以下のようになります。

0000000000101010101000000000101010100000000101010111111101010111111010101111110101000001010000001010000010111110100000101111010000101111011110100010111011110111011101110111011101110001001000111000111000111001001110011100110110011001100110011001100110001100011000111000111000100010...(途中まで)
t^{1000}(1000)=19939560

それぞれ、0,1の個数は、
E, O: [498] [502]

00, 10, 01, 11の個数は、
EE, OE, EO, OO: [250] [247] [247] [255]

となって、いい感じに見えます。

今持っている方針は正しそうに思えてきました。とはいえ、この簡単な例ですら、実際に証明しようとしてもパッとはできないようです。。。

なんとなくイメージ的に、OE率が1:1だけを示そうとするのは難しくて、1:1:1:1とかもすべて同時に示すような方法が本質的なんじゃないかと思います。
つまり、解くためにはどうしたらよいか?
現状は、O,Eという分け方とOO,OE,EO,EEという分け方は別個にしか考えられませんが、同時に考える方法を模索するべきでしょうか。

とりあえ今回はこの辺りで。
合っているかどうかは別にして、今回は大まかな方針は得られたということで、終わりにしたいと思います。ではでは。

Discussion