Skip to content

Instantly share code, notes, and snippets.

@jksuom
Created August 4, 2015 08:04
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jksuom/695024ec938a1a49d893 to your computer and use it in GitHub Desktop.
Save jksuom/695024ec938a1a49d893 to your computer and use it in GitHub Desktop.
Linear diophantine equations

Linear diophantine equations

A general linear nonhomogeneous diophantine equation is of the form


a0x0 + a1x1 + ⋯ + anxn = c

where the coefficients ai and the constant c are integers. The components xi of its solutions are also expected to be integers.

Solutions

The possible values of the left hand side of the equation form a subgroup of the additive group of integers. It consists of the multiples of its least positive element d, which is the greatest common divisor of the coefficients ai.

Hence the equation is solvable if and only if c is divisible by d. Moreover, if (x0, …, xn) is a solution with the right hand side d, then a solution for a multiple c = kd is given by (kx0, …, kxn).

The difference of two solutions of the equation is a solution of the homogeneous diophantine equation whose right hand side is zero. Conversely, the sum of a particular solution of the equation and a solution of the homogeneous equation is a solution of the nonhomogeneous equation.

The solutions of the homogeneous equation form a subgroup of the group Zn + 1 of all families (x0, …, xn) of integers. Assuming the coefficients ai non-zero, as we may, the group of solutions is a free group of rank n. Hence its elements can be represented by n parameters taking integral values.

Implementation

For the complete solution of the nonhomogeneous linear diophantine equation it suffices to find

  • the greatest common divisor d of the coefficients ai,
  • a particular solution of the equation with the right hand side d,
  • a general solution (l0, …, ln) of the homogeneous equation where the li are linear combinations of n parameters t0, …, tn − 1 with integer coefficients.

These can be computed recursively as follows. For any integers xi we have


a0x0 + a1x1 + ⋯ + anxn = a0x0 + gy,

where g = gcd (a1, …, an) and y is an integer. Then d = gcd (a0, g) and by means of the extended Euclidean algorithm we can find integers x and y such that


a0x + gy = d.

Now, if a1y1 + ⋯ + anyn = g is a particular solution of the equation with n variables, we obtain a new particular solution for n + 1 variables


a0x + a1y1y + ⋯ + anyny = d.

If (x0, x1, …, xn) is a solution of the homogeneous equation, then


a0x0 + gy = 0

for some integer y. This is equivalent to


ax0 + by = 0,

where a = a0/d and b = g/d are relatively prime. Hence x0 = bt0 for some integer t0. Conversely, if this is true, then y =  − at0 satisfies the equation, and we obtain


a0bt0 − a(a1y1 + ⋯ + anyn)t0 = 0,

so (bt0,  − ay1t0, …,  − aynt0) is a solution of the homogeneous equation.

The difference of any two solutions of the homogeneous equation having the same first component bt0 is a solution of the homogeneneous equation of n variables


a1x1 + … + anxn = 0.

Hence, if (l1, …, ln) is a general solution of this equation in terms of the parameters t1, …, tn − 1,


(bt0, l1 − ay1t0, …, ln − aynt0)

is a general solution of the homogeneous equation of n + 1 variables.

This recursive procedure can be coded as follows.

def _linear_diop(coeffs, params):
  """Helper function for _diop_linear.

  Recursively solve a linear diophantine equation.

  Parameters
  ==========
  coeffs: Coefficients of a homogeneous linear equation.
          List of n + 1 non-zero integers.
  params: Parameters for the general homogeneous solution.
          List of n symbols.

  Returns
  =======
  d : int
      GCD of the coefficients.
  part : list of int
      Particular solution of the equation with right hand side ``d``.
  gen : list of Expr
      General solution of the homogeneous equation in terms of
      ``params``.
  """
  if len(coeffs) == 1:
      return abs(coeffs[0]), [sign(coeffs[0])], [0]

  g, part, gen = _linear_diop(coeffs[1:], params[1:])
  x, y, d = extended_euclid(coeffs[0], g)
  a, b = coeffs[0]//d, g//d
  t = params[0]

  return (d, [x] + [c*y for c in part],
          [b*t] + [-a*c*t + f for c, f in zip(part, gen)])

SymPy's _diop_linear may then be given the following form.

def _diop_linear(var, coeff, param):
  if not var:
      return ()

  coeffs = [coeff[v] for v in var]
  params = list(symbols(param.name +':' + str(len(var) - 1), integer=True))
  d, part, gen = _linear_diop(coeffs, params)

  if Integer(1) not in coeff:
      return tuple(gen)

  c = -coeff[Integer(1)]
  if c%d != 0:
      return tuple([None]*len(var))
  c //= d
  return tuple([c*x + f for x, f in zip(part, gen)])
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment