最短時間で半素数を因数分解するプログラムを作成します。
テストのために、これを使用してください:38!+1(523022617466601111760007224100074291200000001)
等しい:14029308060317546154181×37280713718589679646221
12259243
プログラムの速度をテストするためにそれを使用する場合、結果は非常に小さいため、統計的に有意な差はありません。
最短時間で半素数を因数分解するプログラムを作成します。
テストのために、これを使用してください:38!+1(523022617466601111760007224100074291200000001)
等しい:14029308060317546154181×37280713718589679646221
12259243
プログラムの速度をテストするためにそれを使用する場合、結果は非常に小さいため、統計的に有意な差はありません。
回答:
複数多項式の二次ふるいを使用します。私はこれをコードの挑戦と考えたので、外部ライブラリーを使用しないことを選択しました(標準log
機能以外は、私は推測します)。タイミングを計るときは、PyPy JITを使用する必要があります。これは、cPythonのタイミングの4〜5倍のタイミングが得られるためです。
更新(2013-07-29):
当初の投稿以来、全体的な速度を約2.5倍に高めるいくつかの小さな、しかし重要な変更を加えました。
更新(2014-08-27):
この投稿はまだ注目されているmy_math.py
ため、使用している可能性のある人のために、2つのエラーを修正しました。
isqrt
は不完全で、完全な正方形に非常に近い値に対して誤った出力を生成する場合がありました。これは修正され、より良いシードを使用することでパフォーマンスが向上しました。is_prime
更新されました。完璧な正方形の2個のスプラインを削除しようとする以前の試みは、せいぜい半分でした。テスト済みの値が平方フリーであることを確認するために、Mathmaticaで使用される手法である3-sprpチェックを追加しました。更新(2014-11-24):
計算の最後に自明ではない一致が見つからない場合、プログラムは追加の多項式をふるいにかけます。これは、以前コードでとしてマークされていましたTODO
。
mpqs.py
from my_math import *
from math import log
from time import clock
from argparse import ArgumentParser
# Multiple Polynomial Quadratic Sieve
def mpqs(n, verbose=False):
if verbose:
time1 = clock()
root_n = isqrt(n)
root_2n = isqrt(n+n)
# formula chosen by experimentation
# seems to be close to optimal for n < 10^50
bound = int(5 * log(n, 10)**2)
prime = []
mod_root = []
log_p = []
num_prime = 0
# find a number of small primes for which n is a quadratic residue
p = 2
while p < bound or num_prime < 3:
# legendre (n|p) is only defined for odd p
if p > 2:
leg = legendre(n, p)
else:
leg = n & 1
if leg == 1:
prime += [p]
mod_root += [int(mod_sqrt(n, p))]
log_p += [log(p, 10)]
num_prime += 1
elif leg == 0:
if verbose:
print 'trial division found factors:'
print p, 'x', n/p
return p
p = next_prime(p)
# size of the sieve
x_max = len(prime)*60
# maximum value on the sieved range
m_val = (x_max * root_2n) >> 1
# fudging the threshold down a bit makes it easier to find powers of primes as factors
# as well as partial-partial relationships, but it also makes the smoothness check slower.
# there's a happy medium somewhere, depending on how efficient the smoothness check is
thresh = log(m_val, 10) * 0.735
# skip small primes. they contribute very little to the log sum
# and add a lot of unnecessary entries to the table
# instead, fudge the threshold down a bit, assuming ~1/4 of them pass
min_prime = int(thresh*3)
fudge = sum(log_p[i] for i,p in enumerate(prime) if p < min_prime)/4
thresh -= fudge
if verbose:
print 'smoothness bound:', bound
print 'sieve size:', x_max
print 'log threshold:', thresh
print 'skipping primes less than:', min_prime
smooth = []
used_prime = set()
partial = {}
num_smooth = 0
num_used_prime = 0
num_partial = 0
num_poly = 0
root_A = isqrt(root_2n / x_max)
if verbose:
print 'sieving for smooths...'
while True:
# find an integer value A such that:
# A is =~ sqrt(2*n) / x_max
# A is a perfect square
# sqrt(A) is prime, and n is a quadratic residue mod sqrt(A)
while True:
root_A = next_prime(root_A)
leg = legendre(n, root_A)
if leg == 1:
break
elif leg == 0:
if verbose:
print 'dumb luck found factors:'
print root_A, 'x', n/root_A
return root_A
A = root_A * root_A
# solve for an adequate B
# B*B is a quadratic residue mod n, such that B*B-A*C = n
# this is unsolvable if n is not a quadratic residue mod sqrt(A)
b = mod_sqrt(n, root_A)
B = (b + (n - b*b) * mod_inv(b + b, root_A))%A
# B*B-A*C = n <=> C = (B*B-n)/A
C = (B*B - n) / A
num_poly += 1
# sieve for prime factors
sums = [0.0]*(2*x_max)
i = 0
for p in prime:
if p < min_prime:
i += 1
continue
logp = log_p[i]
inv_A = mod_inv(A, p)
# modular root of the quadratic
a = int(((mod_root[i] - B) * inv_A)%p)
b = int(((p - mod_root[i] - B) * inv_A)%p)
k = 0
while k < x_max:
if k+a < x_max:
sums[k+a] += logp
if k+b < x_max:
sums[k+b] += logp
if k:
sums[k-a+x_max] += logp
sums[k-b+x_max] += logp
k += p
i += 1
# check for smooths
i = 0
for v in sums:
if v > thresh:
x = x_max-i if i > x_max else i
vec = set()
sqr = []
# because B*B-n = A*C
# (A*x+B)^2 - n = A*A*x*x+2*A*B*x + B*B - n
# = A*(A*x*x+2*B*x+C)
# gives the congruency
# (A*x+B)^2 = A*(A*x*x+2*B*x+C) (mod n)
# because A is chosen to be square, it doesn't need to be sieved
val = sieve_val = A*x*x + 2*B*x + C
if sieve_val < 0:
vec = set([-1])
sieve_val = -sieve_val
for p in prime:
while sieve_val%p == 0:
if p in vec:
# keep track of perfect square factors
# to avoid taking the sqrt of a gigantic number at the end
sqr += [p]
vec ^= set([p])
sieve_val = int(sieve_val / p)
if sieve_val == 1:
# smooth
smooth += [(vec, (sqr, (A*x+B), root_A))]
used_prime |= vec
elif sieve_val in partial:
# combine two partials to make a (xor) smooth
# that is, every prime factor with an odd power is in our factor base
pair_vec, pair_vals = partial[sieve_val]
sqr += list(vec & pair_vec) + [sieve_val]
vec ^= pair_vec
smooth += [(vec, (sqr + pair_vals[0], (A*x+B)*pair_vals[1], root_A*pair_vals[2]))]
used_prime |= vec
num_partial += 1
else:
# save partial for later pairing
partial[sieve_val] = (vec, (sqr, A*x+B, root_A))
i += 1
num_smooth = len(smooth)
num_used_prime = len(used_prime)
if verbose:
print 100 * num_smooth / num_prime, 'percent complete\r',
if num_smooth > num_used_prime:
if verbose:
print '%d polynomials sieved (%d values)'%(num_poly, num_poly*x_max*2)
print 'found %d smooths (%d from partials) in %f seconds'%(num_smooth, num_partial, clock()-time1)
print 'solving for non-trivial congruencies...'
used_prime_list = sorted(list(used_prime))
# set up bit fields for gaussian elimination
masks = []
mask = 1
bit_fields = [0]*num_used_prime
for vec, vals in smooth:
masks += [mask]
i = 0
for p in used_prime_list:
if p in vec: bit_fields[i] |= mask
i += 1
mask <<= 1
# row echelon form
col_offset = 0
null_cols = []
for col in xrange(num_smooth):
pivot = col-col_offset == num_used_prime or bit_fields[col-col_offset] & masks[col] == 0
for row in xrange(col+1-col_offset, num_used_prime):
if bit_fields[row] & masks[col]:
if pivot:
bit_fields[col-col_offset], bit_fields[row] = bit_fields[row], bit_fields[col-col_offset]
pivot = False
else:
bit_fields[row] ^= bit_fields[col-col_offset]
if pivot:
null_cols += [col]
col_offset += 1
# reduced row echelon form
for row in xrange(num_used_prime):
# lowest set bit
mask = bit_fields[row] & -bit_fields[row]
for up_row in xrange(row):
if bit_fields[up_row] & mask:
bit_fields[up_row] ^= bit_fields[row]
# check for non-trivial congruencies
for col in null_cols:
all_vec, (lh, rh, rA) = smooth[col]
lhs = lh # sieved values (left hand side)
rhs = [rh] # sieved values - n (right hand side)
rAs = [rA] # root_As (cofactor of lhs)
i = 0
for field in bit_fields:
if field & masks[col]:
vec, (lh, rh, rA) = smooth[i]
lhs += list(all_vec & vec) + lh
all_vec ^= vec
rhs += [rh]
rAs += [rA]
i += 1
factor = gcd(list_prod(rAs)*list_prod(lhs) - list_prod(rhs), n)
if factor != 1 and factor != n:
break
else:
if verbose:
print 'none found.'
continue
break
if verbose:
print 'factors found:'
print factor, 'x', n/factor
print 'time elapsed: %f seconds'%(clock()-time1)
return factor
if __name__ == "__main__":
parser =ArgumentParser(description='Uses a MPQS to factor a composite number')
parser.add_argument('composite', metavar='number_to_factor', type=long,
help='the composite number to factor')
parser.add_argument('--verbose', dest='verbose', action='store_true',
help="enable verbose output")
args = parser.parse_args()
if args.verbose:
mpqs(args.composite, args.verbose)
else:
time1 = clock()
print mpqs(args.composite)
print 'time elapsed: %f seconds'%(clock()-time1)
my_math.py
# divide and conquer list product
def list_prod(a):
size = len(a)
if size == 1:
return a[0]
return list_prod(a[:size>>1]) * list_prod(a[size>>1:])
# greatest common divisor of a and b
def gcd(a, b):
while b:
a, b = b, a%b
return a
# modular inverse of a mod m
def mod_inv(a, m):
a = int(a%m)
x, u = 0, 1
while a:
x, u = u, x - (m/a)*u
m, a = a, m%a
return x
# legendre symbol (a|m)
# note: returns m-1 if a is a non-residue, instead of -1
def legendre(a, m):
return pow(a, (m-1) >> 1, m)
# modular sqrt(n) mod p
# p must be prime
def mod_sqrt(n, p):
a = n%p
if p%4 == 3:
return pow(a, (p+1) >> 2, p)
elif p%8 == 5:
v = pow(a << 1, (p-5) >> 3, p)
i = ((a*v*v << 1) % p) - 1
return (a*v*i)%p
elif p%8 == 1:
# Shank's method
q = p-1
e = 0
while q&1 == 0:
e += 1
q >>= 1
n = 2
while legendre(n, p) != p-1:
n += 1
w = pow(a, q, p)
x = pow(a, (q+1) >> 1, p)
y = pow(n, q, p)
r = e
while True:
if w == 1:
return x
v = w
k = 0
while v != 1 and k+1 < r:
v = (v*v)%p
k += 1
if k == 0:
return x
d = pow(y, 1 << (r-k-1), p)
x = (x*d)%p
y = (d*d)%p
w = (w*y)%p
r = k
else: # p == 2
return a
#integer sqrt of n
def isqrt(n):
c = n*4/3
d = c.bit_length()
a = d>>1
if d&1:
x = 1 << a
y = (x + (n >> a)) >> 1
else:
x = (3 << a) >> 2
y = (x + (c >> a)) >> 1
if x != y:
x = y
y = (x + n/x) >> 1
while y < x:
x = y
y = (x + n/x) >> 1
return x
# strong probable prime
def is_sprp(n, b=2):
if n < 2: return False
d = n-1
s = 0
while d&1 == 0:
s += 1
d >>= 1
x = pow(b, d, n)
if x == 1 or x == n-1:
return True
for r in xrange(1, s):
x = (x * x)%n
if x == 1:
return False
elif x == n-1:
return True
return False
# lucas probable prime
# assumes D = 1 (mod 4), (D|n) = -1
def is_lucas_prp(n, D):
P = 1
Q = (1-D) >> 2
# n+1 = 2**r*s where s is odd
s = n+1
r = 0
while s&1 == 0:
r += 1
s >>= 1
# calculate the bit reversal of (odd) s
# e.g. 19 (10011) <=> 25 (11001)
t = 0
while s:
if s&1:
t += 1
s -= 1
else:
t <<= 1
s >>= 1
# use the same bit reversal process to calculate the sth Lucas number
# keep track of q = Q**n as we go
U = 0
V = 2
q = 1
# mod_inv(2, n)
inv_2 = (n+1) >> 1
while t:
if t&1:
# U, V of n+1
U, V = ((U + V) * inv_2)%n, ((D*U + V) * inv_2)%n
q = (q * Q)%n
t -= 1
else:
# U, V of n*2
U, V = (U * V)%n, (V * V - 2 * q)%n
q = (q * q)%n
t >>= 1
# double s until we have the 2**r*sth Lucas number
while r:
U, V = (U * V)%n, (V * V - 2 * q)%n
q = (q * q)%n
r -= 1
# primality check
# if n is prime, n divides the n+1st Lucas number, given the assumptions
return U == 0
# primes less than 212
small_primes = set([
2, 3, 5, 7, 11, 13, 17, 19, 23, 29,
31, 37, 41, 43, 47, 53, 59, 61, 67, 71,
73, 79, 83, 89, 97,101,103,107,109,113,
127,131,137,139,149,151,157,163,167,173,
179,181,191,193,197,199,211])
# pre-calced sieve of eratosthenes for n = 2, 3, 5, 7
indices = [
1, 11, 13, 17, 19, 23, 29, 31, 37, 41,
43, 47, 53, 59, 61, 67, 71, 73, 79, 83,
89, 97,101,103,107,109,113,121,127,131,
137,139,143,149,151,157,163,167,169,173,
179,181,187,191,193,197,199,209]
# distances between sieve values
offsets = [
10, 2, 4, 2, 4, 6, 2, 6, 4, 2, 4, 6,
6, 2, 6, 4, 2, 6, 4, 6, 8, 4, 2, 4,
2, 4, 8, 6, 4, 6, 2, 4, 6, 2, 6, 6,
4, 2, 4, 6, 2, 6, 4, 2, 4, 2,10, 2]
max_int = 2147483647
# an 'almost certain' primality check
def is_prime(n):
if n < 212:
return n in small_primes
for p in small_primes:
if n%p == 0:
return False
# if n is a 32-bit integer, perform full trial division
if n <= max_int:
i = 211
while i*i < n:
for o in offsets:
i += o
if n%i == 0:
return False
return True
# Baillie-PSW
# this is technically a probabalistic test, but there are no known pseudoprimes
if not is_sprp(n, 2): return False
# idea shamelessly stolen from Mathmatica
# if n is a 2-sprp and a 3-sprp, n is necessarily square-free
if not is_sprp(n, 3): return False
a = 5
s = 2
# if n is a perfect square, this will never terminate
while legendre(a, n) != n-1:
s = -s
a = s-a
return is_lucas_prp(n, a)
# next prime strictly larger than n
def next_prime(n):
if n < 2:
return 2
# first odd larger than n
n = (n + 1) | 1
if n < 212:
while True:
if n in small_primes:
return n
n += 2
# find our position in the sieve rotation via binary search
x = int(n%210)
s = 0
e = 47
m = 24
while m != e:
if indices[m] < x:
s = m
m = (s + e + 1) >> 1
else:
e = m
m = (s + e) >> 1
i = int(n + (indices[m] - x))
# adjust offsets
offs = offsets[m:] + offsets[:m]
while True:
for o in offs:
if is_prime(i):
return i
i += o
サンプルI / O:
$ pypy mpqs.py --verbose 94968915845307373740134800567566911
smoothness bound: 6117
sieve size: 24360
log threshold: 14.3081031579
skipping primes less than: 47
sieving for smooths...
144 polynomials sieved (7015680 values)
found 405 smooths (168 from partials) in 0.513794 seconds
solving for non-trivial congruencies...
factors found:
216366620575959221 x 438925910071081891
time elapsed: 0.685765 seconds
$ pypy mpqs.py --verbose 523022617466601111760007224100074291200000001
smoothness bound: 9998
sieve size: 37440
log threshold: 15.2376302725
skipping primes less than: 59
sieving for smooths...
428 polynomials sieved (32048640 values)
found 617 smooths (272 from partials) in 1.912131 seconds
solving for non-trivial congruencies...
factors found:
14029308060317546154181 x 37280713718589679646221
time elapsed: 2.064387 seconds
注:この--verbose
オプションを使用しないと、タイミングが若干向上します。
$ pypy mpqs.py 94968915845307373740134800567566911
216366620575959221
time elapsed: 0.630235 seconds
$ pypy mpqs.py 523022617466601111760007224100074291200000001
14029308060317546154181
time elapsed: 1.886068 seconds
一般に、二次ふるいは次の観察に基づいています:奇数の複合nは次のように表されます:
これを確認するのはそれほど難しくありません。以来、nが奇数である、のいずれか2つの補助因子との間の距離nが偶数でなければならない2D、xは、それらの間の中間点です。さらに、nの倍数でも同じ関係が成り立ちます
そのようなxとdが見つかった場合、x + dとx-dはどちらも定義によってnを分割するため、nの(必ずしも素数ではない)因子がすぐに得られることに注意してください。この関係は、潜在的な些細な一致を許容する結果として、次の形式にさらに弱めることができます。
したがって、一般にmod nに等しい2つの完全な正方形を見つけることができる場合、n a 係数gcd(x±d、n)を直接生成できる可能性が非常に高くなります。かなり簡単そうですよね?
そうでないことを除いて。我々は、すべての可能なオーバー全数探索行うことを意図している場合、Xが、我々は[から全範囲を検索する必要がある√ N、√(2N) ]、完全な試行除算よりもわずかに小さいだけでなく、高価で必要とするis_square
動作を各反復にdの値を確認します。それがいることを事前に知られていない限り、nが非常に近い要素を持っている√ nは、トライアル部門はおそらく速くなることです。
おそらく、この関係をさらに弱めることができます。xを選択したとします。
yの完全な素因数分解は容易に知られています。そのような関係が十分にある場合、その積が完全な正方形になるようにyの数を選択すれば、適切なdを構築できるはずです。つまり、すべての素因数が偶数回使用されます。実際、それらに含まれる一意の素因数の総数よりも多くのyがある場合、解の存在が保証されます。それは線形方程式のシステムになります。問題は、どのようにしてこのようなxを選択するかです。それがふるいの出番です。
多項式を考えます:
次に、任意の素数pおよび整数kについて、次のことが当てはまります。
これは、多項式mod pの根を解いた後-つまり、y(x)≡0(mod p)であるxを見つけ、エルゴyがpで割り切れる-無限数を見つけたことを意味しますそのようなxの。この方法で、xの範囲をふるいにかけ、yの小さな素因数を特定し、すべての素因数が小さいものを見つけることができます。このような数値はk-smoothとして知られています。ここで、kは使用される最大の素因数です。
ただし、このアプローチにはいくつかの問題があります。すべての値ではないxが適切である、実際には、それらのごく少数が、ありますを中心√ N。小さい値は(-n項により)大きく負になり、大きい値は大きくなりすぎて、素因数分解が小さい素数のみで構成されることはほとんどありません。このようなxは多数ありますが、ファクタリングするコンポジットが非常に小さい場合を除き、ファクタリングを行うのに十分な平滑化が見つかる可能性は非常に低いです。したがって、nが大きくなると、特定の形式の複数の多項式をふるいにかける必要が生じます。
ふるいにかける多項式がもっと必要ですか?これはどう:
動作します。AとBは文字通り任意の整数値である可能性があることに注意してください、そして数学はまだ成り立ちます。必要なことは、いくつかのランダムな値を選択し、多項式の根を解き、値をゼロに近づけることです。この時点で、それを十分に良いと呼ぶことができます:ランダムな方向に十分な石を投げると、遅かれ早かれ窓を壊すことになります。
ただし、それにも問題があります。多項式の勾配がx切片で大きい場合(比較的平坦でない場合)、多項式ごとにふるいにかけるのに適した値はわずかです。それは機能しますが、必要なものを得る前に、多くの多項式をふるいにかけることになります。もっと良くできますか?
もっとうまくやれる。観察の結果として、モンゴメリーは以下のとおりである場合、AとBがいくつか存在するように選択されるC満たすが
多項式全体を次のように書き直すことができます
さらに、Aが完全な正方形になるように選択された場合、ふるい分け中に先頭のA項が無視される可能性があり、その結果、値がはるかに小さくなり、曲線がより平坦になります。そのような解決策が存在するためには、Nでなければならない平方剰余 MOD √ A計算することにより即座に知ることができる、ルジャンドル記号:
(N |√A)= 1。解くためにそのノートBの完全な素因数分解√Aは(モジュラ平方根を取るために知る必要がある√N(MOD√A)理由で、)√Aは、典型的に素数であるように選択されます。
なお、その場合に示すことができ、その後のすべての値についてのx ∈[ -M、M ]:
そして今、最後に、ふるいを実装するために必要なすべてのコンポーネントがあります。それとも私たち?
上記のように、私たちのふるいには1つの大きな欠陥があります。xのどの値がpで割り切れるyになるかを識別できますが、このyがpの累乗で割り切れるかどうかは識別できません。それを決定するために、pで割り切れなくなるまで、ふるいにかける値の試行除算を実行する必要があります。ふるいに達したように見えました。ふるいの要点は、そうする必要がないようにしたことです。プレイブックを確認する時間。
それは非常に便利に見えます。yのすべての小さな素因数のlnの合計がln(y)の期待値に近い場合、yには他の因子がないことがほぼ与えられます。さらに、期待値を少し下に調整すると、いくつかの素数のべき乗を因子として持つ滑らかな値も特定できます。このようにして、ふるいを「事前スクリーニング」プロセスとして使用し、滑らかになる可能性が高い値のみを因数分解できます。
これには他にもいくつかの利点があります。小さい素数はln合計にほとんど寄与しませんが、最もふるいの時間を必要とすることに注意してください。値3をふるいにかけるには、11、13、17、19、および23を組み合わせたよりも長い時間が必要です。代わりに、最初のいくつかの素数をスキップして、それらの一定の割合が合格したと仮定して、それに応じてしきい値を下げることができます。
もう1つの結果は、多くの値が「すり抜ける」ことを許可されることです。これは、ほとんど滑らかですが、単一の大きな補因子を含みます。これらの値を破棄することはできますが、まったく同じ補因子を持つ別のほとんど滑らかな値が見つかったと仮定します。次に、これら2つの値を使用して、使用可能なyを作成できます。製品にはこの大きなコファクターの2乗が含まれるため、考慮する必要はありません。
最後に行う必要があるのは、これらのyの値を使用して適切なxとdを構築することです。yの非二乗因子、つまり奇数乗の素因数のみを考慮すると仮定します。次に、各yは次のように表現できます。
行列形式で表現できます:
問題は、vM = ⦳ (mod 2)のようなベクトルvを見つけることです。ここで、⦳はnullベクトルです。つまり、Mの左ヌル空間を解きます。これはさまざまな方法で実行できますが、最も簡単な方法は、M Tでガウス消去を実行し、行の加算演算を行xorに置き換えることです。これにより、多数のヌル空間基底ベクトルが生成され、これらの組み合わせにより有効なソリューションが生成されます。
xの構成はかなり簡単です。これは、使用される各yに対するAx + Bの積にすぎません。dの構成は少し複雑です。すべてのyの積を取得する場合、数十万桁ではないにしても、数万桁の値になり、平方根を見つける必要があります。この計算は、実用的ではありません。代わりに、ふるい分けプロセス中に素数の偶数のべき乗を追跡し、非平方因子のベクトルでandおよびxor演算を使用して平方根を再構築できます。
30000文字の制限に達したようです。ああ、それで十分だと思う。
さて、あなたの38!+1は私のphpスクリプトを壊しました。理由はわかりません。実際、16桁を超えるセミプライムはスクリプトを中断します。
ただし、8980935344490257(86028157 * 104395301)を使用すると、自宅のコンピューターでスクリプトが25.963 秒の時間を管理しました(2.61GHz AMD Phenom 9950)。2.93GHz Core 2 Duoでほぼ31秒だった私の仕事用コンピューターよりもはるかに高速です。
php-757文字を含む 新しい行
<?php
function getTime() {
$t = explode( ' ', microtime() );
$t = $t[1] + $t[0];
return $t;
}
function isDecimal($val){ return is_numeric($val) && floor($val) != $val;}
$start = getTime();
$semi_prime = 8980935344490257;
$slice = round(strlen($semi_prime)/2);
$max = (pow(10, ($slice))-1);
$i = 3;
echo "\nFactoring the semi-prime:\n$semi_prime\n\n";
while ($i < $max) {
$sec_factor = ($semi_prime/$i);
if (isDecimal($sec_factor) != 1) {
$mod_f = bcmod($i, 1);
$mod_s = bcmod($sec_factor, 1);
if ($mod_f == 0 && $mod_s == 0) {
echo "First factor = $i\n";
echo "Second factor = $sec_factor\n";
$end=getTime();
$xtime=round($end-$start,4).' seconds';
echo "\n$xtime\n";
exit();
}
}
$i += 2;
}
?>
私はこの同じアルゴリズムをcまたは他のコンパイル済み言語で見たいと思っています。
lcm(2, 3, 5, 7) == 210
、これらの要因によって排除された数字のパターンは210の数字ごとに繰り返され、48だけが残るからです。そのようにして、オッズのみをとることで、50%の代わりに、試行分割からすべての数値の77%を排除できます。