Природно лінійні рівняння Діофантіна


13

Лінійне рівняння Діофантіна у двох змінних - це рівняння виду ax + by = c , де a , b і c є постійними цілими числами, а x і y - цілими змінними.

Для багатьох рівнянь Діофантіна, що зустрічаються в природі, x і y представляють величини, які не можуть бути від'ємними.

Завдання

Напишіть програму або функцію, яка приймає коефіцієнти a , b і c як вхідні дані і повертає довільну пару натуральних чисел (0, 1, 2,…) x і y, що перевіряють рівняння ax + by = c , якщо така пара існує.

Додаткові правила

  • Ви можете вибрати будь-який формат для введення та виводу, який включає лише бажані цілі числа та, за бажанням, масив / список / матриця / кортеж / векторні позначення вашої мови, доки ви не вставите жодний код у вхід.

  • Можна припустити, що коефіцієнти a і b обидва не нульові.

  • Ваш код повинен працювати для будь-якого триплета цілих чисел від -2 60 до 2 60 ; він повинен закінчити менше хвилини на моїй машині (Intel i7-3770, 16 Гб оперативної пам’яті).

  • Ви не можете використовувати будь-які вбудовані модулі, які вирішують рівняння Діофантіна і тим самим тривілізують це завдання, як-от Mathematica FindInstanceабо FrobeniusSolve.

  • Ваш код може вести себе як слід, якщо не знайти рішення, якщо він відповідає часовому обмеженню і його вихід не можна змішувати з дійсним рішенням.

  • Діють стандартні правила .

Приклади

  1. Наведені нижче приклади ілюструють дійсне введення / виведення для рівняння 2x + 3y = 11 , в якому є рівно два дійсних рішення ( (x, y) = (4,1) і (x, y) = (1,3) ).

    Input:  2 3 11
    Output: [4 1]
    
    Input:  (11 (2,3))
    Output: [3],(1)
    
  2. Єдиним правильним рішенням 2x + 3y = 2 є пара (x, y) = (1,0) .

  3. Наведені нижче приклади ілюструють дійсне введення / виведення для рівняння 2x + 3y = 1 , яке не має дійсних рішень .

    Input:  (2 3 1)
    Output: []
    
    Input:  1 2 3
    Output: -1
    
    Input:  [[2], [3], [1]]
    Output: (2, -1)
    
  4. Для (a, b, c) = (1152921504606846883, -576460752303423433, 1) всі правильні рішення (x, y) задовольняють, що (x, y) = (135637824071393749 - bn, 271275648142787502 + an) для деякого негативного цілого n .


Я думаю, що було б добре зробити більше акценту на невід’ємні цілі числа, і що другий приклад насправді не має рішення.
Sp3000

intput 1 2 3 має дійсний вихід, хоча ... [1, 1]
Jack Ammo

@JackAmmo: Усі приклади у другому блоці коду відповідають 2x + 3y = 1 .
Денніс

У ax + bx = k мені здається зрозуміти, що рішення має бути x> = 0 і y> = 0. То хто такі x, y> = 0 розв’язки 38 * x + 909 * y = 3?
RosLuP

У такому випадку, мабуть, мені доведеться повернути те, що не існує рішення ...
RosLuP

Відповіді:


6

Pyth, 92 байти

I!%vzhK%2u?sm,ed-hd*ed/F<G2cG2@G1G+~Q,hQ_eQj9 2)J*L/vzhKtKeoSNm-VJ/RhK_*LdQsm+LdtM3/V*LhK_JQ

Це зовсім чудовисько.

Спробуйте в Інтернеті: Демонстрація . Формат вводу є, c\n[a,b]а вихідний формат [x,y].

У випадку, якщо не існує цілого рішення, я нічого не надрукую, а якщо не існує натурального цілого рішення, я просто надрукую випадкове ціле рішення.

Пояснення (приблизний огляд)

  1. Спочатку я знайду ціле рішення рівняння ax + by = gcd(a,b)за допомогою розширеного алгоритму Евкліда.

  2. Тоді я модифікую рішення (моє множення aі bс c/gcd(a,b)), щоб отримати ціле рішення ax + by = c. Це працює, якщо c/gcd(a,b)це ціле число. Інакше рішення не існує.

  3. Усі інші цілі рішення мають форму a(x+n*b/d) + b(y-n*a/d) = c з d = gcd(a,b)для цілого числа n. Використовуючи два нерівності x+n*b/d >= 0і y-n*a/d >= 0я можу визначити , 6 можливих значень n. Я спробую всі 6 з них і надрукую рішення з найвищим найнижчим коефіцієнтом.

Пояснення (докладно)

Першим кроком є ​​пошук цілого рішення рівняння ax' + by' = gcd(a,b). Це можна зробити за допомогою розширеного алгоритму Евкліда. Ви можете отримати уявлення про те, як це працює у Вікіпедії . Різниця лише в тому, що замість 3-х стовпців ( r_i s_i t_i) я буду використовувати 6 стовпців ( r_i-1 r_i s_i-1 s_i t_i-1 t_i). Таким чином, мені не потрібно зберігати в пам’яті останні два ряди, лише останній.

K%2u?sm,ed-hd*ed/F<G2cG2@G1G+~Q,hQ_eQj9 2)   implicit: Q = [a,b] (from input)
                                     j9 2    convert 9 to base 2: [1,0,0,1]
                            + Q              add to Q => [a,b,1,0,0,1]
                                             this is the initial row
   u                                     )   start with G = ^ and update G repeatedly
                                             by the following expression, until
                                             the value of G doesn't change anymore
    ?                   @G1                    if G[1] != 0:
                     cG2                         split G into parts of 2
      m                                          map the parts d to:
       ,                                           the pair 
        ed                                           d[1]
          -hd*ed/F<G2                                d[0]-d[1]*G[0]/G[1]
     s                                           unfold
                                               else:
                           G                     G (don't change it, stop criterion for u)
 %2                                          take every second element
                                             we get the list [gcd(a,b),x',y']
K                                            store this list in K
                             ~Q,hQ_eQ        afterwards change Q to [Q[0],-Q[1]] = [a,-b]
                                             This will be important for the other parts. 

Тепер я хочу знайти рішення ax + by = c. Це можливо лише тоді, коли c mod gcd(a,b) == 0. Якщо це рівняння задовольняється, я просто помноживши x',y'з c/gcd(a,b).

I!%vzhK...J*L/vzhKtK   implicit: z = c in string format (from input)
  %vzhK                evaluated(z) mod K[0] (=gcd(a,b))
I!                     if not ^ than: 
             /vzhK        c/K[0]
           *L     tK      multipy ^ to each element in K[1:] (=[x',y'])
          J               and store the result in J, this is now [x,y]

У нас є ціле рішення для ax + by = c. Зверніть увагу, що x, yабо обидва можуть бути негативними. Отже наша мета - перетворити їх на негативні.

Приємне в рівняннях Діофантіна полягає в тому, що ми можемо описати все рішення, використовуючи лише одне початкове рішення. Якщо (x,y)це рішення, то всі інші рішення мають форму (x-n*b/gcd(a,b),y+n*a/gcd(a,b))для nцілого числа.

Тому ми хочемо знайти n, де x-n*b/gcd(a,b) >= 0і де y+n*a/gcd(a,b >= 0. Після деякої трансформації ми закінчуємо двома нерівностями n >= -x*gcd(a,b)/bі n >= y*gcd(a,b)/a. Зауважте, що символ нерівності може виглядати в іншому напрямку через поділ з потенційним мінусом aабо b. Мене це мало цікавить, я просто кажу, що одне число однозначно -x*gcd(a,b)/b - 1, -x*gcd(a,b)/b, -x*gcd(a,b)/b + 1задовольняє нерівність 1, а одне число y*gcd(a,b)/a - 1, y*gcd(a,b)/a, y*gcd(a,b)/a + 1задовольняє нерівність 2. Це є n, яке задовольняє обидві нерівності, одне з 6 чисел також робить.

Тоді я обчислюю нові рішення (x-n*b/gcd(a,b),y+n*a/gcd(a,b))для всіх 6 можливих значень n. І я роздруковую рішення з найвищим найнижчим значенням.

eoSNm-VJ/RhK_*LdQsm+LdtM3/V*LhK_JQ
                               _J    reverse J => [y,x]
                           *LhK      multiply each value with K[0] => [y*gcd,x*gcd]
                         /V      Q   vectorized division => [y*gcd/a,-x*gcd/b]
                  m                  map each d of ^ to:
                      tM3              [-1,0,1]
                   +Ld                 add d to each ^
                 s                   unfold
                                     these are the possible values for n
    m                                map each d (actually n) of ^ to:
             *LdQ                      multiply d to Q => [a*n,-b*n]
            _                          reverse => [-b*n,a*n]
        /RhK                           divide by K[0] => [-b*n/gcd,a*n/gcd]
     -VJ                               vectorized subtraction with J
                                       => [x+b*n/gcd,y-a*n/gcd]
 oSN                                 order the solutions by their sorted order
e                                    print the last one

Сортування за їх упорядкованим порядком працює наступним чином. Я використовую приклад2x + 3y = 11

Я сортую кожне із шести рішень (це називаються ключами) та сортую оригінальні рішення за їх ключами:

solutions: [1, 3], [4, 1], [7, -1], [-5, 7], [-2, 5], [1, 3]
keys:      [1, 3], [1, 4], [-1, 7], [-5, 7], [-2, 5], [1, 3]
sort by key:
solutions: [-5, 7], [-2, 5], [7, -1], [1, 3], [1, 3], [4, 1]
keys:      [-5, 7], [-2, 5], [-1, 7], [1, 3], [1, 3], [1, 4]

Це сортує повне негативне рішення до кінця (якщо є).


1
  • після зауважень Денніса, що зробило мою попередню ідею перевернутою, мені довелося змінити код з його коренів, і це знадобило мені довгострокову налагодження, і коштувало мені в два рази більше байтів: '(.

Матлаб (660)

a=input('');b=input('');c=input('');if((min(a*c,b*c)>c*c)&&a*c>0&&b*c>0)||(a*c<0&&b*c<0),-1,return,end,g=abs(gcd(a,b));c=c/g;a=a/g;b=b/g;if(c~=floor(c)),-1,return,end,if(c/a==floor(c/a)&&c/a>0),e=c/a-b;if(e>0),e,a,return,else,c/a,0,return,end,end,if(c/b==floor(c/b)&&c/b>0),e=c/b-a;if(e>0),b,e,return,else,0,c/b,return,end,end,f=max(abs(a),abs(b));if f==abs(a),f=b;b=a;a=f;g=0.5;end,e=(c-b)/a;f=(c-2*b)/a;if(e<0&&f<e),-1,elseif(e<0&&f>e),for(i=abs(c*a):abs((c+1)*a)),e=(c-i*b);if(mod(e,a)==0)if(g==0.5),i,e/a;else,e/a,i,end,return,end,end,else for(i=1:abs(a)),e=(c-i*b);if(e/a<0),-1,elseif(mod(e,a)==0),if(g==0.5),i,e/a,else,e/a,i,end,return,end,end,end,-1
  • Ну, я знаю, що це не гольф, оскільки цей тип мов не пристосований для зменшення довжини коду, але я можу гарантувати, що складність у часі є найкращою.

Пояснення:

  • код бере три інваріанти a, b, c як вхідні дані, ці останні піддаються парам умов, перш ніж приступити до обчислення:

    1- якщо (a + b> c) і (a, b, c> 0) немає рішення!

    2- якщо (a + b <c), (a, b, c <0) немає рішення!

    3- якщо (a, b) мають спільні протилежні ознаки c: немає рішення!

    4- якщо GCD (a, b) не розділить c, то рішення не буде знову! , в іншому випадку розділіть всі варіанти за GCD.

  • після цього ми повинні перевірити ще одну умову, вона повинна полегшити та скоротити шлях до потрібного рішення.

    5- якщо c розділити a або b, рішення s = (x або y) = (c- [ax, yb]) / [b, a] = C / [b, a] + [ax, yb] / [b , a] = S + [ax, yb] / [b, a], де S природний, тому ax / b або by / a матиме відтепер негативні прямі рішення, що відповідно x = b або y = a. (зауважте, що рішення можуть бути лише нульовими значеннями, якщо попередні довільні рішення виявляють негативи)

  • Коли програма досягає цієї стадії, замість конгруентності зміщується більш широкий діапазон рішень для x = (c-yb) / a, що змінюється більшими діапазонами чисел, що повторюється регулярними циклами. найбільше поле пошуку - [xa, x + a], де a - дільник.

СПРОБУЙ ЦЕ


euuh, велика кількість випусків, вирішу це (
дивуйся

Я думаю, що це все ще незначна помилка, яку потрібно виправити, щодо великих цілих чисел, я все одно не розумію, чому поділ 1152921504606846800.000000 / 576460752303423420.000000 виходить із натуральним числом 2, хоча цей останній результат округлюється.
Abr001am

ой. я забув виправити цю помилку: p спасибі за те, що помітив це @Jakube
Abr001am

0

Аксіома, 460 байт

w(a,b,x,u)==(a=0=>[b,x];w(b rem a,a,u,x-u*(b quo a)))
d(a,b,k)==(o:List List INT:=[];a=0 and b=0=>(k=0=>[1,1];[]);a=0=>(k=0=>[[1,0]];k rem b=0=>[1,k quo b];[]);b=0=>(k=0=>[[0,1]];k rem a=0=>[k quo a,1];[]);r:=w(a,b,0,1);q:=k quo r.1;(y,x,u,v):=(q*(r.1-r.2*a)quo b,q*r.2,b quo r.1,a quo r.1);m:=min(80,4+abs(k)quo min(abs(a),abs(b)));l:=y quo v;x:=x+l*u;y:=y-l*v;for n in -m..m repeat(t:=x+n*u;z:=y-n*v;t>=0 and z>=0 and t*a+z*b=k=>(o:=cons([t,z],o)));sort(o))

ungolf і деякий тест

-- input a b and k for equation a*x+b*y=k
-- result one List of List of elments [x,y] of solution of  
-- that equation with x and y NNI (not negative integers) 
-- or Void list [] for no solution
diopanto(a,b,k)==
  o:List List INT:=[]
  a=0 and b=0=>(k=0=>[1,1];[])
  a=0=>(k=0=>[[1,0]];k rem b=0=>[1,k quo b];[])
  b=0=>(k=0=>[[0,1]];k rem a=0=>[k quo a,1];[])
  r:=w(a,b,0,1)
  q:=k quo r.1
  (y,x,u,v):=(q*(r.1-r.2*a)quo b,q*r.2,b quo r.1,a quo r.1)
  m:=min(80,4+abs(k)quo min(abs(a),abs(b)))
  l:=y quo v           -- center the interval
  x:=x+l*u; y:=y-l*v
  for n in -m..m repeat
     t:=x+n*u;z:=y-n*v
     t>=0 and z>=0 and t*a+z*b=k=>(o:=cons([t,z],o))
  sort(o)

 ------------------------------------------------------
(4) -> d(0,-9,0)
   (4)  [[1,0]]
                                                  Type: List List Integer
(5) -> d(2,3,11)
   (5)  [[4,1],[1,3]]
                                                  Type: List List Integer
(6) -> d(2,3,2)
   (6)  [[1,0]]
                                                  Type: List List Integer
(7) -> d(2,3,1)
   (7)  []
                                                  Type: List List Integer
(8) -> d(1152921504606846883,-576460752303423433,1)
   (8)
   [[135637824071393749,271275648142787502],
    [712098576374817182,1424197152749634385],
    [1288559328678240615,2577118657356481268],
    [1865020080981664048,3730040161963328151],
    [2441480833285087481,4882961666570175034]]
                                                  Type: List List Integer

В інших можливих "рішеннях" була помилка, оскільки вона намагалася зберегти нескінченні рішення в одному Списку; тепер накладено ліміт 80 рішень макс

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.