Equazioni diofantantiche naturalmente lineari


13

Un lineare diofantea in due variabili è un'equazione della forma ax + by = c , dove un , b e c sono interi costanti e x ed y sono numeri interi variabili.

Per molti naturali diofantee equazioni, x ed y rappresentano quantità che non può essere negativo.

Compito

Scrivere un programma o funzione che accetta i coefficienti un , b e c come input e restituisce una coppia arbitraria di numeri naturali (0, 1, 2, ...) x ed y che verificano l'equazione ax + by = c , se una tale coppia esiste.

Regole aggiuntive

  • Puoi scegliere qualsiasi formato per l'input e l'output che coinvolge solo gli interi desiderati e, facoltativamente, la notazione array / list / matrix / tuple / vector della tua lingua, purché tu non incorpori alcun codice nell'input.

  • Si può supporre che i coefficienti a e b sono entrambi non-zero.

  • Il codice deve funzionare per qualsiasi tripletta di numeri interi compresi tra -2 60 e 2 60 ; deve finire in meno di un minuto sulla mia macchina (Intel i7-3770, 16 GiB RAM).

  • Non è possibile utilizzare alcun built-in che risolva le equazioni di Dihanthant e quindi banalizza questo compito, come Mathematica FindInstanceo FrobeniusSolve.

  • Il codice potrebbe comportarsi come desiderato se non è possibile trovare una soluzione, purché sia ​​conforme al limite di tempo e il suo output non possa essere confuso con una soluzione valida.

  • Si applicano le regole standard del .

Esempi

  1. Gli esempi seguenti illustrano I / O validi per l'equazione 2x + 3y = 11 , che ha esattamente due soluzioni valide ( (x, y) = (4,1) e (x, y) = (1,3) ).

    Input:  2 3 11
    Output: [4 1]
    
    Input:  (11 (2,3))
    Output: [3],(1)
    
  2. L'unica soluzione valida di 2x + 3y = 2 è la coppia (x, y) = (1,0) .

  3. Gli esempi seguenti illustrano I / O validi per l'equazione 2x + 3y = 1 , che non ha soluzioni valide .

    Input:  (2 3 1)
    Output: []
    
    Input:  1 2 3
    Output: -1
    
    Input:  [[2], [3], [1]]
    Output: (2, -1)
    
  4. Per (a, b, c) = (1152921504606846883, -576460752303423433, 1) , tutte le soluzioni corrette (x, y) soddisfano che (x, y) = (135637824071393749 - bn, 271275648142787502 + an) per alcuni numeri interi non negativi n .


Penso che potrebbe essere utile porre un po 'più di enfasi sugli interi non negativi e che il secondo esempio in realtà non ha soluzione.
Sp3000,

intput 1 2 3 ha un output valido anche se ... [1, 1]
Jack Ammo

@JackAmmo: tutti gli esempi nel secondo blocco di codice corrispondono a 2x + 3y = 1 .
Dennis,

In ax + bx = k mi sembra di capire che la soluzione deve essere x> = 0 e y> = 0. Quindi chi sono tali soluzioni x, y> = 0 di 38 * x + 909 * y = 3?
RosLuP,

In tal caso probabilmente devo restituire quella soluzione inesistente ...
RosLuP

Risposte:


6

Pyth, 92 byte

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

È piuttosto un mostro.

Provalo online: dimostrazione . Il formato di input è c\n[a,b]e il formato di output è [x,y].

Nel caso in cui non esista una soluzione intera, non stamperò nulla e nel caso in cui non esista una soluzione intera naturale, stamperò semplicemente una soluzione intera casuale.

Spiegazione (Panoramica approssimativa)

  1. All'inizio troverò una soluzione intera all'equazione ax + by = gcd(a,b)usando l'algoritmo Euclidean esteso.

  2. Quindi modificherò la soluzione (il mio moltiplicando ae bcon c/gcd(a,b)) per ottenere una soluzione intera di ax + by = c. Funziona, se c/gcd(a,b)è un numero intero. Altrimenti non esiste una soluzione.

  3. Tutte le altre soluzioni intere hanno il modulo a(x+n*b/d) + b(y-n*a/d) = c con d = gcd(a,b)for integer n. Utilizzando le due disuguaglianze x+n*b/d >= 0e y-n*a/d >= 0posso determinare 6 possibili valori per n. Proverò tutti e 6 e stampa la soluzione con il coefficiente più basso più alto.

Spiegazione (dettagliata)

Il primo passo è trovare una soluzione intera all'equazione ax' + by' = gcd(a,b). Questo può essere fatto usando l'algoritmo euclideo esteso. Puoi avere un'idea su come funziona su Wikipedia . L'unica differenza è che invece di usare 3 colonne ( r_i s_i t_i) userò 6 colonne ( r_i-1 r_i s_i-1 s_i t_i-1 t_i). In questo modo non devo tenere in memoria le ultime due righe, solo l'ultima.

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. 

Ora voglio trovare una soluzione per ax + by = c. Questo è possibile solo quando c mod gcd(a,b) == 0. Se questa equazione è soddisfatta, sto semplicemente moltiplicando x',y'con 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]

Abbiamo una soluzione intera per ax + by = c. Si noti che x, yo entrambi possono essere negativi. Quindi il nostro obiettivo è di trasformarli in non negativi.

La cosa bella delle equazioni di Dihanthant è che possiamo descrivere tutte le soluzioni usando solo una soluzione iniziale. Se (x,y)è una soluzione, tutte le altre soluzioni sono nella forma (x-n*b/gcd(a,b),y+n*a/gcd(a,b))per nintero.

Pertanto vogliamo trovare un n, dove x-n*b/gcd(a,b) >= 0e y+n*a/gcd(a,b >= 0. Dopo qualche trasformazione finiamo con le due disuguaglianze n >= -x*gcd(a,b)/be n >= y*gcd(a,b)/a. Si noti che il simbolo della disuguaglianza potrebbe guardare nella direzione opposta a causa della divisione con un potenziale negativo ao b. Non mi interessa così tanto, dico semplicemente che un numero di -x*gcd(a,b)/b - 1, -x*gcd(a,b)/b, -x*gcd(a,b)/b + 1soddisfa definitivamente la disuguaglianza 1 e un numero di y*gcd(a,b)/a - 1, y*gcd(a,b)/a, y*gcd(a,b)/a + 1soddisfa la disuguaglianza 2. C'è un n, che soddisfa entrambe le disuguaglianze, anche uno dei 6 numeri.

Quindi calcolo le nuove soluzioni (x-n*b/gcd(a,b),y+n*a/gcd(a,b))per tutti e 6 i possibili valori di n. E stampo la soluzione con il valore più basso più basso.

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

L'ordinamento in base all'ordine ordinato funziona nel modo seguente. Sto usando l'esempio2x + 3y = 11

Ordino ciascuna delle 6 soluzioni (queste sono chiamate chiavi) e le soluzioni originali in base alle loro chiavi:

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]

Questo ordina una soluzione completa non negativa alla fine (se ce n'è una).


1
  • dopo le osservazioni di Dennis, che hanno capovolto la mia idea precedente, ho dovuto cambiare il codice dalle sue origini e mi ha richiesto il debug a lungo termine e mi è costato il doppio del numero di byte: '(.

Matlab (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
  • Bene, so che non è golf, dato che quel tipo di lingue non è adattato per la riduzione della lunghezza del codice, ma posso garantire che la complessità temporale sia al suo meglio.

Spiegazione:

  • il codice accetta tre invarianti a, b, c come input, questi ultimi sono sottomessi a un paio di condizioni prima di procedere al calcolo:

    1- if (a + b> c) e (a, b, c> 0) nessuna soluzione!

    2- if (a + b <c), (a, b, c <0) nessuna soluzione!

    3- se (a, b) hanno segni opposti comuni di c: nessuna soluzione!

    4- se GCD (a, b) non divide c, quindi di nuovo nessuna soluzione! , altrimenti, dividi tutte le varianti per GCD.

  • dopo questo, dobbiamo verificare un'altra condizione, dovrebbe facilitare e spingere la strada verso la soluzione desiderata.

    5- se c divide a o b, soluzione s = (xo y) = (c- [ax, yb]) / [b, a] = C / [b, a] + [ax, yb] / [b , a] = S + [ax, yb] / [b, a] dove S è naturale, quindi ax / b o da / a avrebbe d'ora in poi soluzioni dirette non negative che sono rispettivamente x = b o y = a. (si noti che le soluzioni possono essere solo valori nulli nel caso in cui le precedenti soluzioni arbitrarie siano rivelate negative)

  • quando il programma raggiunge questo stadio, una gamma più ristretta di soluzioni per x = (c-yb) / a viene invece spazzata invece, grazie alla congruenza, da ampie gamme di numeri più ampi, che si presentano ripetutamente attraverso cicli regolari. il campo di ricerca più grande è [xa, x + a] dove a è il divisore.

PROVALO


euuh, problema con numeri grandi, risolverlo (meraviglia quando lol)
Abr001am

Penso che il suo bug ancora minore da correggere, sui numeri interi grandi, non riesco ancora a capire perché la divisione 1152921504606846800.000000 / 576460752303423420.000000 viene fuori con il numero naturale 2, anche se quest'ultimo risultato è arrotondato.
Abr001am

oh mi sono dimenticato di correggere quel bug bug: p grazie per averlo notato @Jakube
Abr001am

0

Assioma, 460 byte

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 e qualche prova

-- 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

Nelle altre "soluzioni" possibili c'era un bug perché cercava di salvare le infinite soluzioni in una Lista; ora viene imposto il limite di 80 soluzioni max

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.