ループの長さが奇数になる数を具体的に並べてみましょう。
2, 5, 10, 13, 17, 26, 29, 37, 41, 50, 53, 58, 61, 65, 73, ...
これらを素因数分解すると、因数は2か4で割って1余る素数になっています。
ただ、逆は違うようです。すなわち、すべて2か4で割って1余る素数から成っていてもループの長さが奇数になるとは限りません。すぐにわかるのは、2は一つしか使ってはいけないということです。例えば、20などはこの中に入りません。それ以外では、例えば、34は偶数のようです。これらの例外を並べてみると、
34, 146, 178, 194, 205, 221, 305, 377, 386, 410, ...
素数は例外にならないようです。それ以外は例外に成り得ます。なので、素数以外の場合は前回行った判定をしなければなりません。このロジックで問題無いかは1000万まで確認しました。
これで組んでみたところ、前回のコードより10倍以上速くなりました。
import scala.math.sqrt import scala.testing.Benchmark def gcd(m :Int, n :Int) :Int = if(n == 0) m else gcd(n, m % n) def num_steps(N :Int) :Int = { val n = sqrt(N.toDouble).toInt val x0 = (1, n, 1, 0) def next(x :(Int,Int,Int,Int)) :(Int,Int,Int,Int) = { val (a, b, c, m) = x val m1 = (a * n + b) / c val e = m1 * c - b val (a1, b1, c1) = (a * c, c * e, a * a * N - e * e) val d = gcd(gcd(a1, b1), c1) (a1 / d, b1 / d, c1 / d, m1) } def equalto(x :(Int,Int,Int,Int)) = x._1 == x0._1 && x._2 == x0._2 && x._3 == x0._3 Iterator.iterate(next(x0))(next).takeWhile(x => !equalto(x)).size + 1 } // 250, 5 -> (3, 2) def div_pow(n :Int, d :Int) :(Int,Int) = if(n % d != 0) (0, n) else { val (e, m) = div_pow(n / d, d) (e + 1, m) } // [ 0, 1, 2, 3, 2, 5, 2, 7, 2, 3, ... ] def sieve(M :Int) :Array[Int] = { val a = new Array[Int](M + 1) for(k <- 2 to M) a(k) = k for(p <- Iterator.from(2).takeWhile(n => n * n <= M) if a(p) == p; k <- Iterator.range(p * 2, M + 1, p) if a(k) == k) a(k) = p a } def is_odd_loop_length(n :Int, a :Array[Int]) = { def f(m :Int, bsq :Boolean) :Boolean = { val p = a(m) if(m == 1) !bsq else if(p % 4 == 3) false else if(p == m) true else { val (e, q) = div_pow(m, p) if(p == 2 && e > 1) false else f(q, bsq && (e % 2 == 0)) } } if(a(n) == n) // prime (n & 3) != 3 else f(n, true) && num_steps(n) % 2 == 1 } def solve() = { val M = 1000000 val a = sieve(M) println ((2 to M).filter(n => is_odd_loop_length(n, a)).size) } object Test extends Benchmark { def run() = solve } println (Test.runBenchmark(10))