How to enumerate x^2 + y^2 = z^2 - 1 (with additional constraints)

前端 未结 6 2075
野性不改
野性不改 2021-02-04 09:33

Lets N be a number (10<=N<=10^5).

I have to break it into 3 numbers (x,y,z) such that it validates the following conditions.

6条回答
  •  一生所求
    2021-02-04 10:22

    Here's a method that enumerates the triples, rather than exhaustively testing for them, using number theory as described here: https://mathoverflow.net/questions/29644/enumerating-ways-to-decompose-an-integer-into-the-sum-of-two-squares

    Since the math took me a while to comprehend and a while to implement (gathering some code that's credited above it), and since I don't feel much of an authority on the subject, I'll leave it for the reader to research. This is based on expressing numbers as Gaussian integer conjugates. (a + bi)*(a - bi) = a^2 + b^2. We first factor the number, z^2 - 1, into primes, decompose the primes into Gaussian conjugates and find different expressions that we expand and simplify to get a + bi, which can be then raised, a^2 + b^2.

    A perk of reading about the Sum of Squares Function is discovering that we can rule out any candidate z^2 - 1 that contains a prime of form 4k + 3 with an odd power. Using that check alone, I was able to reduce Prune's loop on 10^5 from 214 seconds to 19 seconds (on repl.it) using the Rosetta prime factoring code below.

    The implementation here is just a demonstration. It does not have handling or optimisation for limiting x and y. Rather, it just enumerates as it goes. Play with it here.

    Python code:

    # https://math.stackexchange.com/questions/5877/efficiently-finding-two-squares-which-sum-to-a-prime
    def mods(a, n):
        if n <= 0:
            return "negative modulus"
        a = a % n
        if (2 * a > n):
            a -= n
        return a
    
    def powmods(a, r, n):
        out = 1
        while r > 0:
            if (r % 2) == 1:
                r -= 1
                out = mods(out * a, n)
            r /= 2
            a = mods(a * a, n)
        return out
    
    def quos(a, n):
        if n <= 0:
            return "negative modulus"
        return (a - mods(a, n))/n
    
    def grem(w, z):
        # remainder in Gaussian integers when dividing w by z
        (w0, w1) = w
        (z0, z1) = z
        n = z0 * z0 + z1 * z1
        if n == 0:
            return "division by zero"
        u0 = quos(w0 * z0 + w1 * z1, n)
        u1 = quos(w1 * z0 - w0 * z1, n)
        return(w0 - z0 * u0 + z1 * u1,
               w1 - z0 * u1 - z1 * u0)
    
    def ggcd(w, z):
        while z != (0,0):
            w, z = z, grem(w, z)
        return w
    
    def root4(p):
        # 4th root of 1 modulo p
        if p <= 1:
            return "too small"
        if (p % 4) != 1:
            return "not congruent to 1"
        k = p/4
        j = 2
        while True:
            a = powmods(j, k, p)
            b = mods(a * a, p)
            if b == -1:
                return a
            if b != 1:
                return "not prime"
            j += 1
    
    def sq2(p):
        if p % 4 != 1:
          return "not congruent to 1 modulo 4"
        a = root4(p)
        return ggcd((p,0),(a,1))
    
    # https://rosettacode.org/wiki/Prime_decomposition#Python:_Using_floating_point
    from math import floor, sqrt
    
    def fac(n):
        step = lambda x: 1 + (x<<2) - ((x>>1)<<1)
        maxq = long(floor(sqrt(n)))
        d = 1
        q = n % 2 == 0 and 2 or 3 
        while q <= maxq and n % q != 0:
            q = step(d)
            d += 1
        return q <= maxq and [q] + fac(n//q) or [n]
    
    # My code...
    # An answer for  https://stackoverflow.com/questions/54110614/
    
    from collections import Counter
    from itertools import product
    from sympy import I, expand, Add
    
    def valid(ps):
      for (p, e) in ps.items():
        if (p % 4 == 3) and (e & 1):
          return False
      return True
    
    def get_sq2(p, e):
      if p == 2:
        if e & 1:
          return [2**(e / 2), 2**(e / 2)]
        else:
          return [2**(e / 2), 0]
      elif p % 4 == 3:
        return [p, 0]
      else:
        a,b = sq2(p)
        return [abs(a), abs(b)]
    
    def get_terms(cs, e):
      if e == 1:
        return [Add(cs[0], cs[1] * I)]
      res = [Add(cs[0], cs[1] * I)**e]
      for t in xrange(1, e / 2 + 1):
        res.append(
          Add(cs[0] + cs[1]*I)**(e-t) * Add(cs[0] - cs[1]*I)**t)
      return res
    
    def get_lists(ps):
      items = ps.items()
      lists = []
      for (p, e) in items:
        if p == 2:
          a,b = get_sq2(2, e)
          lists.append([Add(a, b*I)])
        elif p % 4 == 3:
          a,b = get_sq2(p, e)
          lists.append([Add(a, b*I)**(e / 2)])
        else:
          lists.append(get_terms(get_sq2(p, e), e))
      return lists
    
    
    def f(n):
      for z in xrange(2, n / 2):
        zz = (z + 1) * (z - 1)
        ps = Counter(fac(zz))
        is_valid = valid(ps)
        if is_valid:
          print "valid (does not contain a prime of form\n4k + 3 with an odd power)"
          print "z: %s, primes: %s" % (z, dict(ps))
          lists = get_lists(ps)
          cartesian = product(*lists)
          for element in cartesian:
            print "prime square decomposition: %s" % list(element)
            p = 1
            for item in element:
              p *= item
            print "complex conjugates: %s" % p
            vals = p.expand(complex=True, evaluate=True).as_coefficients_dict().values()
            x, y = vals[0], vals[1] if len(vals) > 1 else 0
            print "x, y, z: %s, %s, %s" % (x, y, z)
            print "x^2 + y^2, z^2-1: %s, %s" % (x**2 + y**2, z**2 - 1)
          print ''
    
    if __name__ == "__main__":
      print f(100)
    

    Output:

    valid (does not contain a prime of form
    4k + 3 with an odd power)
    z: 3, primes: {2: 3}
    prime square decomposition: [2 + 2*I]
    complex conjugates: 2 + 2*I
    x, y, z: 2, 2, 3
    x^2 + y^2, z^2-1: 8, 8
    
    valid (does not contain a prime of form
    4k + 3 with an odd power)
    z: 9, primes: {2: 4, 5: 1}
    prime square decomposition: [4, 2 + I]
    complex conjugates: 8 + 4*I
    x, y, z: 8, 4, 9
    x^2 + y^2, z^2-1: 80, 80
    
    valid (does not contain a prime of form
    4k + 3 with an odd power)
    z: 17, primes: {2: 5, 3: 2}
    prime square decomposition: [4 + 4*I, 3]
    complex conjugates: 12 + 12*I
    x, y, z: 12, 12, 17
    x^2 + y^2, z^2-1: 288, 288
    
    valid (does not contain a prime of form
    4k + 3 with an odd power)
    z: 19, primes: {2: 3, 3: 2, 5: 1}
    prime square decomposition: [2 + 2*I, 3, 2 + I]
    complex conjugates: (2 + I)*(6 + 6*I)
    x, y, z: 6, 18, 19
    x^2 + y^2, z^2-1: 360, 360
    
    valid (does not contain a prime of form
    4k + 3 with an odd power)
    z: 33, primes: {17: 1, 2: 6}
    prime square decomposition: [4 + I, 8]
    complex conjugates: 32 + 8*I
    x, y, z: 32, 8, 33
    x^2 + y^2, z^2-1: 1088, 1088
    
    valid (does not contain a prime of form
    4k + 3 with an odd power)
    z: 35, primes: {17: 1, 2: 3, 3: 2}
    prime square decomposition: [4 + I, 2 + 2*I, 3]
    complex conjugates: 3*(2 + 2*I)*(4 + I)
    x, y, z: 18, 30, 35
    x^2 + y^2, z^2-1: 1224, 1224
    

提交回复
热议问题