Python (w / PyPy JIT v1.9) ~ 1.9 sn
Çoklu Bir Polinom Kuadratik Elek Kullanma . Bunu kod mücadelesi olarak kabul ettim, bu yüzden herhangi bir harici kütüphaneyi kullanmamayı log
seçtim (sanırım standart işlev dışında ). Zamanlama, zaman PyPy JIT kullanılması gerektiğini, bunun 4-5 kat daha hızlı daha zamanlamaları sonuçlanır olarak CPython .
Güncelleme (2013-07-29):
İlk gönderimden bu yana, genel hızı yaklaşık 2,5 kat artıran birkaç küçük, ancak önemli değişiklikler yaptım.
Güncelleme (2014-08-27):
Bu yayın hala dikkat çekmeye devam ederken, onu my_math.py
kullanan herkes için iki hatayı düzelttim:
isqrt
hatalıydı, bazen mükemmel bir kareye çok yakın değerler için yanlış çıktılar üretiyordu. Bu düzeltildi ve performans daha iyi bir tohum kullanılarak arttırıldı.
is_prime
güncellendi. Mükemmel kare 2 sprps kaldırmak için önceki girişimi, en iyi ihtimalle, yarı yürekli oldu. Test edilen değerin karesiz olmasını sağlamak için Mathmatica tarafından kullanılan bir teknik olan 3-sprp kontrolü ekledim.
Güncelleme (2014-11-24):
Hesaplamanın sonunda önemsiz olmayan eşleşme bulunmazsa, progam şimdi ek polinomlar elde eder. Bu daha önce kodda olarak işaretlendi 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
Örnek 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
Not: Bu --verbose
seçeneği kullanmamak biraz daha iyi zamanlamalar verecektir:
$ pypy mpqs.py 94968915845307373740134800567566911
216366620575959221
time elapsed: 0.630235 seconds
$ pypy mpqs.py 523022617466601111760007224100074291200000001
14029308060317546154181
time elapsed: 1.886068 seconds
Temel konseptler
Genel olarak, ikinci dereceden bir elek aşağıdaki gözlemlere dayanır: Herhangi bir garip bileşik n şu şekilde temsil edilebilir:
Bunu onaylamak çok zor değil. Bu yana , n garip, herhangi iki kofaktörler arasındaki mesafe n da olmalıdır 2d , X , aralarında orta noktasıdır. Dahası, aynı ilişki n'nin herhangi bir katı için de geçerlidir.
Bu tür, eğer Not X ve d bulunabilir, hemen bir (mutlaka asal olmayan) etken neden olur n , çünkü x + d ve d - X , her iki bölme n tanımı aracılığıyla tanımlanabilir. Bu ilişki, potansiyel önemsiz uyumluluklara izin verilmesi sonucunda - aşağıdaki forma daha da zayıflayabilir:
Genel olarak, eğer eşdeğer mod n olan iki mükemmel kareyi bulabilirsek , o zaman doğrudan n a la gcd (x ± d, n) faktörünü üretmemiz oldukça muhtemeldir . Oldukça basit görünüyor, değil mi?
Dışında değil. Mümkün olan tüm x üzerinde kapsamlı bir araştırma yapmak istiyorsak, tüm aralığı tüm denemelerden biraz daha küçük olan fakat aynı zamanda her bir denemede pahalı bir işlem gerektiren [ √ n , √ ( 2n ) ] arasından arama yapmamız gerekir is_square
. d değerini onaylayın . O önceden bilindiği sürece , n çok yakın faktörleri vardır √ n , deneme bölümü büyük olasılıkla daha hızlı olmaktır.
Belki bu ilişkiyi daha da zayıflatabiliriz. Diyelim ki böyle bir x seçtik .
y'nin tam asal çarpanları kolayca bilinmektedir. Yeterince bu tür ilişkiler olsaydı, mümkün olmalıdır inşa yeterli bir d biz bir takım seçerseniz, y kendi ürünlerinin mükemmel bir kare olacak şekilde; yani, tüm ana faktörler eşit sayıda kullanılır. Aslında, içerdikleri toplam benzersiz ana faktör sayısından daha fazla bir y değerine sahipsek, bir çözüm bulunması garanti edilir; Bir lineer denklem sistemi haline gelir. Şimdi soru şu, böyle x'i nasıl seçtik ? Bu noktada eleme devreye giriyor.
Elek
Polinomu düşünün:
Sonra, herhangi bir asal p ve k tamsayıları için, aşağıdaki doğrudur:
Polinom kökleri çözme için bundan sonra bu araçlar mod p olan, bir tespit ettik - x öyle ki y (x) ≡ 0 (mod p) , ergo y bölünemeyen bir p - o zaman sonsuz sayıda bulduk böyle x . Bu şekilde, y'nin küçük asal çarpanlarını belirleyerek , tüm asal çarpan faktörlerinin küçük olduğu bazılarını bularak , bir dizi x'in üzerinde eleme yapabilirsiniz . K-pürüzsüz olarak bilinen bu sayılar , k kullanılan en büyük ana faktördür.
Yine de bu yaklaşımla ilgili birkaç sorun var. Değil tüm değerleri x etrafında, aslında, bunlardan sadece çok az var, yeterli merkezli programlardır √ n . Küçük değerler büyük ölçüde negatif olur ( -n terimi nedeniyle ) ve daha büyük değerler çok büyük olur, öyle ki asal çarpanlara ayırmalarının sadece küçük asal sayılardan oluşması olası değildir. Böyle bir x sayısı olacak , ancak faktoring yaptığınız bileşik çok küçük olmadıkça, bir faktörleşmeyle sonuçlanacak kadar düzgün bulmanız pek olası değildir. Ve böylece, daha büyük n için , verilen bir formdaki çoklu polinomların üzerine elemek gerekir.
Çoklu Polinomlar
Yani elek için daha fazla polinom lazım mı? Buna ne dersin:
Bu işe yarar. Not bu bir ve B tam anlamıyla herhangi bir tamsayı değer olabilir ve matematik hala sahiptir. Tek yapmamız gereken birkaç rastgele değer seçmek, polinomun kökü için çözmek ve sıfıra yakın değerleri elemek. Bu noktada yeterince iyi diyebiliriz: eğer rastgele yönlere yeterince taş atarsanız, pencereyi er ya da geç kırmak zorunda kalırsınız.
Ancak bununla ilgili bir sorun var. Polinomun eğimi x-kesişiminde büyükse, ki nispeten düz olmasa da olur, sadece polinom başına elemek için birkaç uygun değer olacaktır. İşe yarayacak, ancak ihtiyacınız olanı almadan önce çok sayıda polinomun elenmesi ile sonuçlanacak. Daha iyisini yapabilir miyiz?
Daha iyisini yapabiliriz. Montgomery'nin bir sonucu olarak bir gözlem şöyledir: eğer A ve B , C'yi tatmin edecek şekilde seçilmişse
sonra tüm polinom olarak yeniden yazılabilir
Dahası, eğer A mükemmel bir kare olarak seçildiyse, eleme sırasında öncü A terimi ihmal edilebilir, daha küçük değerler ve daha düz bir eğri ile sonuçlanır. Böyle bir çözüm mevcut olduğu için, n, olması gereken bir kuadratik kalıntı mod √ bir hesaplama ile hemen bilinen edilebilir Legendre sembolü :
( n | √A ) = 1 . İçin çözmek için geldiğini hatırlatırız B , tam bir asal çarpanlara √A (modüler karekök almak için bilinmesi gereken √ n (mod √A) neden olduğunu) √A tipik asal olacak şekilde seçilir.
Daha sonra, eğer x all [ -M, M ] ' nin tüm değerleri için ise :
Ve şimdi, nihayet, eleklerimizi uygulamak için gerekli tüm bileşenlere sahibiz. Yoksa biz mi?
Asallerin Faktör Olarak Yetkileri
Yukarıda tarif edildiği gibi eleklerimizin bir ana kusuru vardır. Bu değerleri olan belirleyebilir x bir neden olur y bölünebilen p , ancak bu olup olmadığını tespit edemez y bir bölünemeyen bir güç ve p . Bunu belirlemek için, elenecek değer üzerinde deneme bölünmesi gerçekleştirmemiz gerekecek, p ile bölünemez hale gelene kadar . Biz bir çıkmaza gibiydi: eleğin bütün mesele biz o kadar oldu vermedi bunu yapmak zorunda. Oyun kitabını kontrol etme zamanı.
Bu oldukça kullanışlı görünüyor. Toplamı ise ln küçük asal faktörlerin tümünün y beklenen değere yakın ln (y) , o zaman neredeyse bir göz önüne alındığında bu y başka faktörler vardır. Ayrıca, beklenen değeri biraz düşürürsek, bazı primer güçleri faktörleri olan pürüzsüz olan değerleri de belirleyebiliriz. Bu şekilde elekleri bir 'ön eleme' işlemi olarak kullanabiliriz ve sadece düzgün olması muhtemel değerleri etkiler.
Bunun da birkaç avantajı var. Küçük primerlerin ln toplamına çok az katkıda bulunduğuna dikkat edin , ancak en elek zamanlarını gerektirir. Değeri 3 elenmesi, 11, 13, 17, 19, ve 23 daha fazla zaman gerektirir birleştirildi . Bunun yerine, ilk birkaç primi atlayabiliriz ve belirli bir yüzdesinin geçeceğini varsayarak eşiği aşağıya ayarlayabiliriz.
Bir başka sonuç, çoğunlukla pürüzsüz olan, ancak tek bir büyük kofaktör içeren bir dizi değerin “kaymasına” izin verilmesidir. Sadece bu değerleri kaldırabiliriz, ancak tam olarak aynı kofaktörle birlikte çoğunlukla daha yumuşak bir değer bulduğumuzu varsayalım. Kullanılabilir bir y oluşturmak için bu iki değeri kullanabiliriz ; ürünleri bu büyük kofaktör kare içereceğinden, artık dikkate alınması gerekmez.
Hepsini bir araya koy
Yapmamız gereken en son şey y'nin bu değerlerini yeterli x ve d olarak kullanmak . Diyelim ki sadece y olmayanın kare olmayan faktörlerini , yani tek bir gücün ana faktörlerini göz önünde bulunduruyoruz . Daha sonra, her bir y aşağıdaki şekilde ifade edilebilir:
matris formunda ifade edilebilir:
Daha sonra problem, bir vektör v bulur, öyle ki, vM = ⦳ (mod 2) , ki burada ⦳ null vektördür. Yani, M'nin boş alanı için çözmek . Bu, en basit olanı M T üzerinde Gauss Eliminasyonu gerçekleştirmek ve sıra ekleme işlemini bir sıra xor ile değiştirmek olan birkaç yolla yapılabilir . Bu, herhangi bir kombinasyonu geçerli bir çözüm üretecek olan çok sayıda boş alan tabanlı vektöre neden olacaktır.
İnşaat x oldukça düz ileri. Sadece kullanılan y'nin her biri için Ax + B'nin ürünüdür . İnşaat d biraz daha karmaşıktır. Eğer y'nin ürününü almak istiyorsak, karekökünü bulmamız gereken 100 bin basamak olmasa da 10 binde bir değer elde edeceğiz. Bu hesaplama pratik olarak pahalıdır. Bunun yerine, eleme işlemi sırasında primerlerin eşit güçlerini takip edebilir ve ardından kare kökü yeniden oluşturmak için kare olmayan faktörlerin vektörleri üzerinde ve ve xor işlemlerini kullanabiliriz .
30000 karakter sınırına ulaşmış gibiyim. Ahh, sanırım bu yeterince iyi.