diff --git a/ipython/EulerProblem066.ipynb b/ipython/EulerProblem066.ipynb new file mode 100644 index 0000000..a761e06 --- /dev/null +++ b/ipython/EulerProblem066.ipynb @@ -0,0 +1,276 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Diophantine equation (Euler Problem 66)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "collapsed": true + }, + "source": [ + "Consider quadratic Diophantine equations of the form:\n", + "\n", + "$x^2 – Dy^2 = 1$\n", + "\n", + "For example, when D=13, the minimal solution in x is 6492 – 13×1802 = 1.\n", + "\n", + "It can be assumed that there are no solutions in positive integers when D is square.\n", + "\n", + "By finding minimal solutions in x for D = {2, 3, 5, 6, 7}, we obtain the following:\n", + "\n", + "$3^2 – 2×2^2 = 1$\n", + "\n", + "$2^2 – 3×1^2 = 1$\n", + "\n", + "$9^2 – 5×4^2 = 1$\n", + "\n", + "$5^2 – 6×2^2 = 1$\n", + "\n", + "$8^2 – 7×3^2 = 1$\n", + "\n", + "Hence, by considering minimal solutions in x for D ≤ 7, the largest x is obtained when D=5.\n", + "\n", + "Find the value of D ≤ 1000 in minimal solutions of x for which the largest value of x is obtained.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "import math\n", + "\n", + "def get_floor_sqrt(n):\n", + " return math.floor(math.sqrt(n))\n", + "\n", + "assert(get_floor_sqrt(5) == 2)\n", + "assert(get_floor_sqrt(23) == 4)\n", + "\n", + "def next_expansion_1(current_a, current_nominator, current_denominator, original_number):\n", + " cn = current_nominator\n", + " cd = current_denominator\n", + " cn, cd = cd, (original_number - cd * cd) // cn\n", + " next_a = math.floor((math.sqrt(original_number) + cn) // cd)\n", + " cn = cn - next_a * cd\n", + " cn *= -1\n", + " \n", + " return next_a, cn, cd\n", + "\n", + "def get_continued_fraction_sequence(n):\n", + " \n", + " # If number is a square number we return it.\n", + " floor_sqrt = get_floor_sqrt(n)\n", + " if n == floor_sqrt * floor_sqrt:\n", + " return ((floor_sqrt), [])\n", + " \n", + " # Otherwise, we calculate the next expansion till we\n", + " # encounter a step a second time. \n", + " a = floor_sqrt\n", + " cn = a\n", + " cd = 1\n", + " sequence = []\n", + " previous_steps = []\n", + " \n", + " while not (a, cn, cd) in previous_steps :\n", + " #print(\"a: {} cn: {} cd: {}\".format(a, cn, cd))\n", + " previous_steps.append((a, cn, cd))\n", + " a, cn, cd = next_expansion_1(a, cd, cn, n)\n", + " sequence.append(a)\n", + " sequence.pop()\n", + " return ((floor_sqrt), sequence)\n", + "\n", + "def get_continued_fraction_sequence(n):\n", + " \n", + " # If number is a square number we return it.\n", + " floor_sqrt = get_floor_sqrt(n)\n", + " if n == floor_sqrt * floor_sqrt:\n", + " return ((floor_sqrt), [])\n", + " \n", + " # Otherwise, we calculate the next expansion till we\n", + " # encounter a step a second time. \n", + " a = floor_sqrt\n", + " cn = a\n", + " cd = 1\n", + " sequence = []\n", + " previous_steps = []\n", + " \n", + " while not (a, cn, cd) in previous_steps :\n", + " #print(\"a: {} cn: {} cd: {}\".format(a, cn, cd))\n", + " previous_steps.append((a, cn, cd))\n", + " a, cn, cd = next_expansion_1(a, cd, cn, n)\n", + " sequence.append(a)\n", + " sequence.pop()\n", + " return ((floor_sqrt), sequence)\n", + "\n", + "assert(get_continued_fraction_sequence(1) == ((1), []))\n", + "assert(get_continued_fraction_sequence(4) == ((2), []))\n", + "assert(get_continued_fraction_sequence(25) == ((5), []))\n", + "assert(get_continued_fraction_sequence(2) == ((1), [2]))\n", + "assert(get_continued_fraction_sequence(3) == ((1), [1,2]))\n", + "assert(get_continued_fraction_sequence(5) == ((2), [4]))\n", + "assert(get_continued_fraction_sequence(13) == ((3), [1,1,1,1,6]))" + ] + }, + { + "cell_type": "code", + "execution_count": 43, + "metadata": {}, + "outputs": [], + "source": [ + "def gcd(a, b):\n", + " if b > a:\n", + " a, b = b, a\n", + " while a % b != 0:\n", + " a, b = b, a % b\n", + " return b\n", + "\n", + "def add_fractions(n1, d1, n2, d2):\n", + " d = d1 * d2\n", + " n1 = n1 * (d // d1)\n", + " n2 = n2 * (d // d2)\n", + " n = n1 + n2\n", + " p = gcd(n, d)\n", + " return (n // p, d // p)\n", + "\n", + "def next_expansion_2(previous_numerator, previous_denumerator, value):\n", + " if previous_numerator == 0:\n", + " return (value, 1)\n", + " return add_fractions(previous_denumerator, previous_numerator, value, 1)\n", + " \n", + "def get_fractions(n, x):\n", + " #print(\"get_fractions(n={}, x={})\".format(n, x))\n", + " # Get sequence of a_x\n", + " first_value, sequence = get_continued_fraction_sequence(n)\n", + " sequence = [first_value] + math.ceil(x / len(sequence)) * sequence\n", + " sequence = sequence[:x + 1]\n", + " sequence = sequence[::-1]\n", + " \n", + " n, d = 0, 1\n", + " for s in sequence:\n", + " n, d = next_expansion_2(n, d, s)\n", + " return (n, d)\n", + "\n", + "assert(get_fractions(7, 0) == (2, 1))\n", + "assert(get_fractions(7, 1) == (3, 1))\n", + "assert(get_fractions(7, 2) == (5, 2))\n", + "assert(get_fractions(7, 3) == (8, 3))" + ] + }, + { + "cell_type": "code", + "execution_count": 48, + "metadata": {}, + "outputs": [], + "source": [ + "def get_minimal_solution(d):\n", + " for i in range(0, 100):\n", + " x, y = get_fractions(d, i)\n", + " if x * x - d * y * y == 1:\n", + " return((x, y))\n", + "\n", + "assert(get_minimal_solution(2) == (3, 2))\n", + "assert(get_minimal_solution(3) == (2, 1))\n", + "assert(get_minimal_solution(85) == (285769, 30996))\n", + "assert(get_minimal_solution(109) == (158070671986249, 15140424455100))" + ] + }, + { + "cell_type": "code", + "execution_count": 52, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "True\n" + ] + } + ], + "source": [ + "from math import sqrt\n", + "\n", + "def is_square(n):\n", + " return sqrt(n).is_integer()\n", + "\n", + "print(is_square(4))" + ] + }, + { + "cell_type": "code", + "execution_count": 55, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "d: 661 x: 16421658242965910275055840472270471049\n" + ] + } + ], + "source": [ + "x_max = 0\n", + "d_max = 0\n", + "\n", + "for d in range(2, 1001):\n", + " if is_square(d):\n", + " continue\n", + " \n", + " x, y = get_minimal_solution(d)\n", + " if x > x_max:\n", + " x_max = x\n", + " d_max = d\n", + "\n", + "print(\"d: {} x: {}\".format(d_max, x_max))\n", + "s = d_max\n", + "assert(s == 661)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [] + } + ], + "metadata": { + "completion_date": "Tue, 22 Jan 2019, 05:10", + "kernelspec": { + "display_name": "Python 3", + "language": "python3.6", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.6.5" + }, + "tags": [ + "fractions", + "square roots", + "sequence", + "periodic" + ] + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/ipython/html/EulerProblem066.html b/ipython/html/EulerProblem066.html new file mode 100644 index 0000000..0262e23 --- /dev/null +++ b/ipython/html/EulerProblem066.html @@ -0,0 +1,12021 @@ + + + +
+Consider quadratic Diophantine equations of the form:
+$x^2 – Dy^2 = 1$
+For example, when D=13, the minimal solution in x is 6492 – 13×1802 = 1.
+It can be assumed that there are no solutions in positive integers when D is square.
+By finding minimal solutions in x for D = {2, 3, 5, 6, 7}, we obtain the following:
+$3^2 – 2×2^2 = 1$
+$2^2 – 3×1^2 = 1$
+$9^2 – 5×4^2 = 1$
+$5^2 – 6×2^2 = 1$
+$8^2 – 7×3^2 = 1$
+Hence, by considering minimal solutions in x for D ≤ 7, the largest x is obtained when D=5.
+Find the value of D ≤ 1000 in minimal solutions of x for which the largest value of x is obtained.
+import math
+
+def get_floor_sqrt(n):
+ return math.floor(math.sqrt(n))
+
+assert(get_floor_sqrt(5) == 2)
+assert(get_floor_sqrt(23) == 4)
+
+def next_expansion_1(current_a, current_nominator, current_denominator, original_number):
+ cn = current_nominator
+ cd = current_denominator
+ cn, cd = cd, (original_number - cd * cd) // cn
+ next_a = math.floor((math.sqrt(original_number) + cn) // cd)
+ cn = cn - next_a * cd
+ cn *= -1
+
+ return next_a, cn, cd
+
+def get_continued_fraction_sequence(n):
+
+ # If number is a square number we return it.
+ floor_sqrt = get_floor_sqrt(n)
+ if n == floor_sqrt * floor_sqrt:
+ return ((floor_sqrt), [])
+
+ # Otherwise, we calculate the next expansion till we
+ # encounter a step a second time.
+ a = floor_sqrt
+ cn = a
+ cd = 1
+ sequence = []
+ previous_steps = []
+
+ while not (a, cn, cd) in previous_steps :
+ #print("a: {} cn: {} cd: {}".format(a, cn, cd))
+ previous_steps.append((a, cn, cd))
+ a, cn, cd = next_expansion_1(a, cd, cn, n)
+ sequence.append(a)
+ sequence.pop()
+ return ((floor_sqrt), sequence)
+
+def get_continued_fraction_sequence(n):
+
+ # If number is a square number we return it.
+ floor_sqrt = get_floor_sqrt(n)
+ if n == floor_sqrt * floor_sqrt:
+ return ((floor_sqrt), [])
+
+ # Otherwise, we calculate the next expansion till we
+ # encounter a step a second time.
+ a = floor_sqrt
+ cn = a
+ cd = 1
+ sequence = []
+ previous_steps = []
+
+ while not (a, cn, cd) in previous_steps :
+ #print("a: {} cn: {} cd: {}".format(a, cn, cd))
+ previous_steps.append((a, cn, cd))
+ a, cn, cd = next_expansion_1(a, cd, cn, n)
+ sequence.append(a)
+ sequence.pop()
+ return ((floor_sqrt), sequence)
+
+assert(get_continued_fraction_sequence(1) == ((1), []))
+assert(get_continued_fraction_sequence(4) == ((2), []))
+assert(get_continued_fraction_sequence(25) == ((5), []))
+assert(get_continued_fraction_sequence(2) == ((1), [2]))
+assert(get_continued_fraction_sequence(3) == ((1), [1,2]))
+assert(get_continued_fraction_sequence(5) == ((2), [4]))
+assert(get_continued_fraction_sequence(13) == ((3), [1,1,1,1,6]))
+def gcd(a, b):
+ if b > a:
+ a, b = b, a
+ while a % b != 0:
+ a, b = b, a % b
+ return b
+
+def add_fractions(n1, d1, n2, d2):
+ d = d1 * d2
+ n1 = n1 * (d // d1)
+ n2 = n2 * (d // d2)
+ n = n1 + n2
+ p = gcd(n, d)
+ return (n // p, d // p)
+
+def next_expansion_2(previous_numerator, previous_denumerator, value):
+ if previous_numerator == 0:
+ return (value, 1)
+ return add_fractions(previous_denumerator, previous_numerator, value, 1)
+
+def get_fractions(n, x):
+ #print("get_fractions(n={}, x={})".format(n, x))
+ # Get sequence of a_x
+ first_value, sequence = get_continued_fraction_sequence(n)
+ sequence = [first_value] + math.ceil(x / len(sequence)) * sequence
+ sequence = sequence[:x + 1]
+ sequence = sequence[::-1]
+
+ n, d = 0, 1
+ for s in sequence:
+ n, d = next_expansion_2(n, d, s)
+ return (n, d)
+
+assert(get_fractions(7, 0) == (2, 1))
+assert(get_fractions(7, 1) == (3, 1))
+assert(get_fractions(7, 2) == (5, 2))
+assert(get_fractions(7, 3) == (8, 3))
+def get_minimal_solution(d):
+ for i in range(0, 100):
+ x, y = get_fractions(d, i)
+ if x * x - d * y * y == 1:
+ return((x, y))
+
+assert(get_minimal_solution(2) == (3, 2))
+assert(get_minimal_solution(3) == (2, 1))
+assert(get_minimal_solution(85) == (285769, 30996))
+assert(get_minimal_solution(109) == (158070671986249, 15140424455100))
+from math import sqrt
+
+def is_square(n):
+ return sqrt(n).is_integer()
+
+print(is_square(4))
+x_max = 0
+d_max = 0
+
+for d in range(2, 1001):
+ if is_square(d):
+ continue
+
+ x, y = get_minimal_solution(d)
+ if x > x_max:
+ x_max = x
+ d_max = d
+
+print("d: {} x: {}".format(d_max, x_max))
+s = d_max
+assert(s == 661)
+
+