time factor 60000000000000003100000000000000033 60000000000000003100000000000000033: 200000000000000003 300000000000000011 factor 60000000000000003100000000000000033 38.38s user 0.00s system 99% cpu 38.383 total
可以看到分解仅 35 位的合数就用了 38 s 这显然是不能接受的 (实际上 Linux 的跑的相当快,我写的丢人 Pollard’s Rho 跑了两分钟还没跑出来
而我们完全可以用 Fermat 的想法做到更快的分解速度
Dixon’s 与 连分数 (CFRAC)
为了方便的叙述先定义两个概念
因子基
一个因子基意味前几个素数的集合{p1,p2,p3,…}
L记号
L 记号同样是衡量时间复杂度的,Ln[a,c]=exp{(c+o(1)(lnn)a(lnlnn)1−a)}
其中 c>0,a∈[0,1]
主要用来数论中算法的复杂度
Dixon’s
Dixon’s 的主要思想是:
选定一个大小为 b 的因子基 B,选取一些数字xi,使得 xi2modn=∏piki 的所有 pi∈B (显然不会所有的ki都为偶数,否则就找到了一个a2≡b2)
然后通过奥妙重重的方法选取一系列xi使得 ∏xi≡(∏piki)2(modn)
这样我们就得到了一个a2≡b2(modn)
那我们怎么才能寻找到这个奥妙重重的方法呢?
考虑 ∏xi=∏piai,1+ai,2+ai,3+…(modn)
令 ai=(ai,1mod2,ai,2mod2,…,ai,kmod2)∈(Z2)k
可以用高斯消元解出来向量 a=(0,0,0,…,0)∈(Z2)k
把对应的数字乘起来即可
而 xi 的选取则是完全随机的(
大概分析下复杂度
对于确定的 b=n−α 而言 找到一个合适的 x 的概率大概是是 a−α (这实际上Dickman function ρ 的一个近似,之后我们会在椭圆曲线那里讨论)
而对于这个 b 我们仅需找到 π(b) 个 x 即可总共随机约 exp{(α+1)lnα+αlnn−lnlnn} 次数 每次需要 π(b) 次试除
defEulerSieve(n): vis = [True] * (n + 1) primes = [] for i inrange(2, n + 1): if vis[i]: primes.append(i) for j in primes: if i * j > n: break vis[i * j] = False if i % j == 0: break return primes
defexgcd(a,b): if b == 0: return1, 0, a q, r = divmod(a, b) x, y, d = exgcd(b, r) return y, x - q * y, d
defelliptic_add(p, q, a, b, m): if p[2] == 0: return q if q[2] == 0: return p if p[0] == q[0]: if (p[1] + q[1]) % m == 0: return0, 1, 0 num = (3 * p[0] * p[0] + a) % m denom = (2 * p[1]) % m else: num = (q[1] - p[1]) % m denom = (q[0] - p[0]) % m inv, _, g = modular_inv(denom, m) if g > 1: return0, 0, denom z = (num * inv * num * inv - p[0] - q[0]) % m return z, (num * inv * (p[0] - z) - p[1]) % m, 1
defelliptic_mul(k, p, a, b, m): res = (0, 1, 0) while k > 0: if p[2] > 1: return p if k % 2 == 1: res = elliptic_add(p, res, a, b, m) k = k // 2 p = elliptic_add(p, p, a, b, m) return res
deflenstra(n, limit): g = n while g == n: q = randint(0, n - 1), randint(0, n - 1), 1 a = randint(0, n - 1) b = (q[1] * q[1] - q[0] * q[0] * q[0] - a * q[0]) % n g = gcd(4 * a * a * a + 27 * b * b, n) if g > 1: return g primes=EulerSieve(limit) for p in primes: pp = p while pp < limit: q = elliptic_mul(p, q, a, b, n) if q[2] > 1: return gcd(q[2], n) pp = p * pp returnFalse
defmain(): n=998244354 B=7000 curve_count=200 for i inrange(curve_count): res=lenstra(n, B) if res != False: print(res) break if __name__ == '__main__': main()