En hızlı yarı yarıyıl çarpanlara ayırma


28

En kısa sürede yarı asal sayıyı çarpanlara ayırmak için bir program yazın.

Test amacıyla, şunu kullanın: 38! +1 (523022617466601111760007224100074291200000001)

Eşittir: 14029308060317546154181 × 37280713718589679646221


2
"En hızlı" bit'i sevdiğim halde, C gibi dillere tipik kodlama dillerine göre avantaj sağladığı için, sonuçları nasıl test edeceğinizi merak ediyorum?
Bay Lister,

1
Bunu demek durumunda 12259243programlardır kadar hızlı testte kullanılacak, sonuçlar istatistiksel olarak anlamlı farklılıklar almazsınız o kadar küçüktür ki olacaktır.
Peter Taylor

Daha büyük bir sayı ekledim, kafalar için teşekkürler.
Soham Chowdhury

@ Bay Lister, kendi bilgisayarımda test edeceğim.
Soham Chowdhury

5
inb4 birisi 400 exabyte arama tablosu yazmak için önceden işlemci kötüye kullanımı kullanıyor.
Wug

Yanıtlar:


59

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ı logseç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.pykullanan herkes için iki hatayı düzelttim:

  • isqrthatalı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_primegü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 --verboseseç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ı modbir 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.


5
Ben lisede hiç cebirden geçmedim (aslında birinci sınıfın ilk döneminde bıraktım), ancak bir programcının bakış açısından anlaşılmasını kolaylaştırıyorsunuz. Uygulamaya koymadan tam anlamış gibi davranmayacağım, ama sizi alkışlıyorum. Bu gönderiyi site dışına genişletmeyi ve ciddiye almayı düşünmelisiniz!
jdstankosky

2
Katılıyorum. Harika bir açıklama ile mükemmel cevap. +1
Soham Chowdhury

1
@primo Buradaki birden fazla soruya verdiğiniz cevaplar inanılmaz derecede ayrıntılı ve ilginçti. Çok takdir!
Paul Walls,

4
Son bir açıklama olarak, bu soruya +100 ödül verdiğiniz için Will Ness'e şükranlarımı sunarım. Kelimenin tam anlamıyla onun itibarıydı.
primo

2
@StepHen öyle. Ne yazık ki, 2012'deki orijinal sürümü, hız iyileştirmeleri olmadan ve gauss elemesinde bir hatayla (son sütun bir özet sütunu olduğunda yapılan hatalar) kullanıyor. Bir süre önce yazarla iletişim kurmaya çalıştım ancak yanıt alamadım.
primo

2

38! + 1'iniz php betiğimi kırdı, nedenini bilmiyorum. Aslında, 16 basamağın üzerinde herhangi bir yarı asal uzun zaman senaryoyu kırar.

Ancak, 8980935344490257 (86028157 * 104395301) kullanarak komut dosyam ev bilgisayarımda (2.61GHz AMD Phenom 9950) 25.963 saniyelik bir süreyi yönetti . 2.93GHz Core 2 Duo'da neredeyse 31 saniye olan iş bilgisayarımdan çok daha hızlı.

php - 757 karakter dahil yeni hatlar

<?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;
}
?>

Aynı algoritmayı c veya başka bir derlenmiş dilde görmek isterim.


PHP'nin sayıları yalnızca 53 bit hassasiyete sahiptir, kabaca 16 ondalık basamak
kopyalayın

3
64 bit tamsayıları kullanarak aynı algoritmayı C ++ 'a uygulamak bilgisayarımda sadece 1.8 saniye sürdü. Bu yaklaşımla ilgili birkaç sorun var: 1. Yeterince büyük rakamlarla baş edemiyor. 2. Tüm sayıları kabul etse ve kabul etse bile, uzunluk ne olursa olsun, deneme bölünmesi için aynı süreyi kullandıysa, her büyüklük artış sırası eşdeğer bir zaman artışına neden olacaktır. İlk faktörünüz, verilen ilk faktörden yaklaşık 14 büyüklükten daha küçük olduğundan, bu algoritmanın belirli bir yarıyıl süresini etkilemesi 9 milyon yıl sürer.
CasaDeRobison

Matematiğin en iyisi değilim, kuşkusuz, ama çok büyük sayılar için, yarı-primerlerin standart faktoring yöntemleri bildiğim kadarıyla (bir elips kullanarak, vb.) İşe yaramayacaktır. Bunu akılda tutarak, algoritmanın kendisi nasıl geliştirilebilir?
jdstankosky

2
Eratosthenes Elek elek tam yalnızca asal sayılardır sonra kalan nedir vs, o zaman, sayı listesiyle başlar 2'nin katlarını kaldırır ve daha sonra 3, sonra 5 ve sonra 7. Bu elek, belli sayıda faktör için “önceden hesaplanabilir”. Çünkü lcm(2, 3, 5, 7) == 210, bu faktörler tarafından elimine edilen sayıların paterni her 210 sayı için tekrar eder ve sadece 48 tanesi kalır. Bu şekilde, tüm oranların% 77'sini yalnızca oranlarını alarak% 50 yerine deneme bölümünden çıkarabilirsiniz.
primo

1
@primo Meraktan, buna ne kadar zaman ayırdınız? Bu şeyleri düşünmek beni yaşlandırırdı. Bunu yazdığımda sadece asal sayıların her zaman nasıl tuhaf olduğunu düşünüyordum. Bunun ötesine geçmeye ve asal olmayan olasılıkları da ortadan kaldırmaya çalışmadım. Geçmişe bakıldığında çok basit görünüyor.
jdstankosky
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.