2013:12 Veriering av forskningsresultat gällande antal lösningar för linjära kongruenssystem Uno Sörensson Master Thesis in Mathematics, 30 credits Dec 2013 Blekinge Institute of Technology School of Engineering Deparment of Mathematics and Science Supervisor: Robert Nyqvist Innehåll 1 Uppdrag och bakgrund 2 1.1 Sammanfattning . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2 1.2 Introduktion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2 1.3 Linjära kongruenser . . . . . . . . . . . . . . . . . . . . . . . . . . . 6 1.4 System av linjära kongruenser . . . . . . . . . . . . . . . . . . . . . 8 2 Genomförande 9 2.1 Inledning . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.2 Arbetsgång 2.3 Bestämning av antal lösningar 2.4 Lösning av linjära kongruenssystem med två och tre obekanta . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . η. . . . . . . . . . . . . . . . . . . . . . . 3 Avslutning 3.1 3.2 3.3 9 9 10 13 21 Sammanfattning och resultat av genomförda test . . . . . . . . . . 21 3.1.1 Sammanfattning av test . . . . . . . . . . . . . . . . . . . . 21 3.1.2 Resultat . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21 Program . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22 3.2.1 CountSolutions.java . . . . . . . . . . . . . . . . . . . . . . . 22 3.2.2 OneSolution.java 28 3.2.3 CountWithGamma.java 3.2.4 CountSolCorr.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32 . . . . . . . . . . . . . . . . . . . . . . . 41 . . . . . . . . . . . . . . . . . . . . 53 3.3.1 Exempel på programkörningar. ResCountSolutions.txt . . . . . . . . . . . . . . . . . . . . . 53 3.3.2 ResOneSolution.txt . . . . . . . . . . . . . . . . . . . . . . . 54 3.3.3 ResCountWithGamma.txt . . . . . . . . . . . . . . . . . . . 56 3.3.4 ResCountSolCorr.txt . . . . . . . . . . . . . . . . . . . . . . 56 Litteraturförteckning 58 1 1 Uppdrag och bakgrund 1.1 Sammanfattning Kongruenser har använts praktiskt sedan lång tid, men det var först på 1800talet som matematiker som Gauss, Fermat och Euler systematiskt studerade och utvecklade kongruensteorin. Sedan dess har kongruensteori varit en viktig del av talteorin. Nilsson och Nyqvist [1] har teoretiskt visat att för system av linjära kongruenser med n obekanta Ax ≡ b (mod pk ) n × n-matris med heltalselement samt x och b vektorer med n help ett primtal och k ett heltal, gäller att antalet lösningar är nk η = sgd(det(A), p ), under förutsättning att p inte delar det(A). Vår uppgift har varit att för den homogena ekvationen (b = 0) veriera formeln för η för n = 2 och n = 3. Vi har gjort detta med hjälp av dator och program i programspråket där A är en talskomponenter, Java. 1.2 Introduktion Teorin för kongruenser utvecklades i början av artonhundratalet av den tyske matematikern Karl Friedrich Gauss, en av historiens mest berömda matematiker. Även andra matematiker, som Fermat och Euler har arbetat med kongruensteori. Kongruenser förekommer i vardagslivet. Klockan t.ex arbetar modulo för timmar och modulo 60 12 eller 24 7 för minuter och sekunder. Almanackan har modulo för veckodagarna och modulo 12 för månaderna. Denition: Låt m vara ett positivt heltal. Om a och b är heltal, säger vi att a är kongruent med kongruent med b modulo m om m delar (a − b), b modulo m, skriver vi vilket skrivs m | (a − b). Om a är a ≡ b (mod m). Exempel 1. 22 ≡ 4 (mod 9), för 9 | (22 − 4). På samma sätt gäller att 3 ≡ −6 (mod 9), eftersom 9 | (3 − (−6)), men det gäller att 13 6≡ 5 (mod 9), eftersom 9 - (13 − 5). Det gäller att 2 När vi arbetar med kongruenser, behöver vi ibland översätta dem till likheter. Följande sats hjälper oss att göra detta. Sats 1. Om ett heltal k, a och så att b är heltal, så a = b + km. är a ≡ b (mod m) om och endast om det nns a ≡ b (mod m), så gäller att m | (a − b). Detta betyder att det nns k med km = a − b, så att a = b + km. Omvänt gäller att om det nns k med a = b + km, så gäller att km = a − b, och följaktligen, a ≡ b Bevis. Om ett heltal ett heltal (mod m). Sats 2. Låt m vara ett heltal. Kongruenser modulo m har följande egenskaper. 1. Reexiv: Om a är ett heltal så gäller att 2. Symmetrisk: Om a och b a ≡ a (mod m). är heltal sådana att a ≡ b (mod m), så gäller att b ≡ a (mod m). a, b, och c är heltal a ≡ c (mod m). 3. Transitiv: Om så gäller att med a ≡ b (mod m) och b ≡ c (mod m), Bevis. 1. Vi ser att a ≡ a (mod m), eftersom a−a=0 och m | 0. a ≡ b (mod m), så gäller att m | (a − b). Detta betyder att det nns ett heltal k med km = a − b. Detta visar att (−km) = b − a, så att m | (b − a). Följaktligen gäller att b ≡ a (mod m). 2. Om a ≡ b (mod m) och b ≡ c (mod m), så gäller att m | (a − b) och m | (b − c). Då nns det heltal k och l sådana att km = a − b och lm = b − c. Av detta fås a − c = (a − b) + (b − c) = km + lm = (k + l)m, vilket ger att m|(a − c) och a ≡ c (mod m). 3. Om Sats 2 medför att heltalen delas upp i m disjunkta delmängder (partitioner), ek- vivalensklasser. För kongruens är benämningen kongruensklasser. 3 Låt m vara ett positivt heltal. Enligt divisionsalgoritmen kan alla heltal a skrivas på formen a ≡ mq +r där 0 <= r < m och där kvoten q och r är entydigt bestämda. Exempel 2. Om t.ex. m ≡ 4, så tillhör varje heltal a exakt en av de fyra kongru- ensklasserna modulo 4 · · · ≡ −8 ≡ −4 ≡ 0 ≡ 4 ≡ 8 ≡ · · · (mod 4) · · · ≡ −7 ≡ −3 ≡ 1 ≡ 5 ≡ 9 ≡ · · · (mod 4) · · · ≡ −6 ≡ −2 ≡ 2 ≡ 6 ≡ 10 ≡ · · · (mod 4) · · · ≡ −5 ≡ −1 ≡ 3 ≡ 7 ≡ 11 ≡ · · · (mod 4). Man utför ofta aritmetik med kongruenser. Detta kallas moduloaritmetik. Kongruenser har många av de egenskaper som likheter har. Sats 3. Addition, subtraktion och multiplikation bevarar kongruensen. Om c är heltal, med och m m > 0, 1. a + c ≡ b + c (mod m) 2. a − c ≡ b − c (mod m) 3. ac ≡ bc (mod m). sådana att a ≡ b (mod m), a, b , så gäller att Bevis. 1. a ≡ b (mod m) ger som vi vet att m | (a − b). (a − b), innebär att m | ((a + c) − (b + c)). 2. följer av att 3. Identiteten (a + c) − (b + c) = (a − c) − (b − c) = a − b. ac − bc = c(a − b) och eftersom m | (a − b), föjer att m | (c(a − b)) och ac ≡ bc (mod m). Denition: Låt a och b vara heltal, varav minst ett av dem är skilt från 0. Det största heltal som delar både b, och betecknas sgd(a, b). a och b kallas största gemensamma delare till a och Vi denierar sgd(0, 0) = 0. 4 Följande sats, som är mer generell än föregående sats, är också användbar Sats 4. Om (mod m), a, b , c , d och m är heltal, med m > 0, a ≡ b (mod m), och c≡d så gäller att 1. a + c ≡ b + d (mod m) 2. a − c ≡ b − d (mod m) 3. ac ≡ bd (mod m). Bevis. 1. a ≡ b (mod m) c ≡ d (mod m), ger att m | (a − b) och m | (c − d). Då nns det heltal k och l med km = (a − b) och lm = (c − d). Detta ger (a + c) − (b + d) = (a − b) + (c − d) = km + lm = (k + l)m. Därför gäller att m | [((a + c) − (b + d)], det vill säga (a + c) ≡ (b + d) (mod m). 2. (a − c) − (b − d) = (a − b) − (c − d) = km − lm = (k − l)m, m | [((a − c) − (b − d)], så att (a − c) ≡ (b − d) (mod m). 3. ac − bd = ac − bc + bc − bd = c(a − b) + b(c − d) = ckm + blm = m(ck + bl). Därför gäller att m | (ac − bd) och ac ≡ bd (mod m). och därför gäller att Följande sats och lemma behövs i den följande framställningen Sats 5. Om a, b, m och n är heltal, och om c | a och c | b, så gäller att c | (ma+nb). c | a och c | b, så nns det heltal e och f sådana att a = ce b = cf . Följaktligen, ma + nb = mce + ncf = c(me + nf ). Alltså gäller att c | (ma + nb). Bevis. Eftersom och Lemma 1. Om a, b , c är positiva heltal och sgd(a, b) = 1 och a | bc så gäller att a | c. sgd(a, b) = 1, så nns det heltal x och y sådana att ax + by = 1. Enligt Sats 5 gäller att a | (acx + bcy) eftersom acx + bcy är en linjär kombination av a och bc, vilka båda delas av a. Alltså gäller att a | c. Bevis. Eftersom 5 För att kongruensen skall bevaras vid division, måste följande villkor uppfyllas. Sats 6 . Om a, b, c och m är heltal sådana ac ≡ bc (mod m), så gäller att a ≡ b (mod m/d). (Divisionsregeln) sgd(c, m) och att m > 0, d = ac ≡ bc (mod m), vet vi att m | (ac − bc) = c(a − b).Därför nns k sådant att c(a − b) = km. Om vi dividerar båda sidor med d, får vi (c/d)(a − b) = k(m/d). Eftersom sgd(m/d, c/d) = 1, fås enligt Lemma 1, att m/d | (a − b). Därför gäller att a ≡ b (mod m/d). Bevis. Om det ett heltal Denition: Det minsta positiva heltal m sådant att a|m och b|m kallas för minsta gemensamma multipel för a och b, där a och b är heltal, betecknas mgm(a, b). Sats 7. positiva. Om a, b, m1 , m2 , . . . , mk vara heltal med m1 , m2 , · · · , mk a ≡ b (mod m1 ),a ≡ b (mod m2 ),. . . , a ≡ b (mod mk ),så gäller att Låt a ≡ b (mod [m1 , m2 , · · · , mk ]) m = [m1 , m2 , . . . , mk ], det vill säga minsta gemensamma multipeln för heltalen m1 , m2 , . . . , mk där Denition: Två heltal a och b sägs vara relativt prima om det gäller att sgd(a, b) = 1. Följdsats 1. är heltal och a ≡ b (mod m1 ), a ≡ b (mod m2 ),. . . ,a ≡ b (mod mk ), där a,b m1 , m2 , . . . , mk är parvis relativt prima positiva heltal, så gäller att Om a≡b eftersom 1.3 Låt (mod m1 m2 . . . mk ) [m1 , m2 , . . . , mk ] = m1 m2 . . . mk i detta fall. Linjära kongruenser a, b och m vara heltal, där m > 1. En kongruens på formen ax ≡ b (mod m) kallas en linjär kongruens i en obekant x. Att studera linjära kongruenser är nära förknippat med studier av linjära diofantiska ekvationer med två variabler. Vi x = x0 är en lösning till kongruensen ax ≡ b (mod m), och om x1 ≡ x0 (mod m), så är ax1 ≡ ax0 ≡ b (mod m), dvs x1 är också en lösning. Följaktligen, om en medlem av en kongruensklass modulo m är en lösning, så är noterar först att om alla medlemmar i denna klass också lösningar. Därför kan vi fråga hur många av 6 de m kongruensklasserna modulo m som ger lösningar. Detta är detsamma som att fråga hur många inkongruenta lösningar modulo m det nns. Följande sats talar om för oss när en linjär kongruens i en variabel har lösningar, och om så är fallet, ger den oss exakt antalet inkongruenta lösningar modulo Sats 8. m. a, b och m vara heltal, sådana att m > 0 och sgd(a, m) = d. Om d inte delar b, så har ax ≡ b (mod m) inga lösningar. Om d | b, så har ax ≡ b (mod m) exakt d inkongruenta lösningar modulo m. Om x0 är en lösning, så nns det exakt sgd(a, m) inkongruenta lösningar och dessa ges av Låt x = x0 + där m ·k sgd(a, m) k = 0, 1, . . . , sgd(a, m) − 1 ax ≡ b (mod m)är ekvivalent med den linjära diofantiska ekvationen i två variabler ax − my = b. Heltalet x är en lösning till ax ≡ b (mod m) om och endast om det nns ett heltal y sådant att ax − my = b. Man kan visa att om d = sgd(a, m) inte delar b så nns inga lösningar, men om d|b har ax − my = b oändligt många lösningar. Dessa lösningar ges av samtliga kongruensklasser modulo d. För varje kongruensklass gäller att x = x0 + (m/d)t, t = 0, 1, . . . , sgd(a, m) − 1. För det fullständiga beviset se Rosen [2]. Bevisskiss. Den linjära kongruensen Följdsats 2. Om a och m är relativt prima heltal med så gäller att den linjära kongruensen ax ≡ b (mod m) m>0 och b är ett heltal, har exakt en lösning. sgd(a, m) = 1, vet vi att sgd(a, m) | b. Av sats 8 följer då ax ≡ b (mod m) har exakt sgd(a, m) = 1 inkongruenta lösningar modulo m. Bevis. Eftersom att Exempel 3. Vi vill nna alla lösningar till 9x ≡ 12 (mod 15). Eftersom sgd(9, 15) = 3 och 3 | 12 nns det exakt tre inkongruenta lösningar. Vi kan nna dessa lösningar genom att först nna en speciell lösning och sedan addera lämpliga multiplar av 15/3 = 5. För att nna en speciell lösning, betraktar vi den linjära diofantiska ekvationen 9x − 15y = 12. Euklides algoritm visar att 15 = 9 · 1 + 6 9=6·1+3 6=3·2+0 3 = 9 − 6 · 1 = 9 − (15 − 9 · 1) · 1 = 9 · 2 − 15. Härav följer att 9 · 8 − 15 · 4 = 12, och en speciell lösning ges av x0 = 8 och y0 = 4. Totala antalet inkongruenta lösningar är tre. Dessa är x = x0 ≡ 8 (mod 15), x = x0 + 5 ≡ 13 (mod 15), och x = x0 + 5 · 2 ≡ 18 ≡ 3 (mod 15). så att 7 Denition: ax ≡ 1 (mod m) har exakt en lösning, om sgd(a, m) = 1. Lösningen betecknas a−1 och kallas för den multiplikativa inversen för a är den multiplikativa inversen ax ≡ b (mod m), ges av x ≡ a−1 · b (mod m). nitionen följer att till 1.4 modulo a modulo m. Av dem till a−1 . Lösningen System av linjära kongruenser System av linjära kongruenser är system med mer än en kongruens, och som innehåller samma antal obekanta som antalet kongruenser. Alla kongruenser har samma modulus. Låt m och n vara positiva heltal, med m > 1 och n > 1. Ett system av linjära kongruenser har det generella utseendet a11 x1 + a12 x2 + . . . + a1n xn ≡ b1 (mod m) a21 x1 + a22 x2 + . . . + a2n xn ≡ b2 (mod m) . . . a x + a x + . . . + a x ≡ b (mod m) n1 1 n2 2 nn n n där alla aij och bi är heltal. I litteraturen beskrivs olika metoder för att lösa system av linjära kongruenser. Dessa metoder påminner om metoder som används för att lösa linjära ekvationssystem inom linjär algebra, som Gauss elimineringsmetod och Cramers regel. (Jfr.: Kinesiska restklassatsen med vars hjälp man under vissa villkor kan lösa system av kongruenser med olika modulus.) 8 2 Genomförande 2.1 Inledning Nilsson och Nyqvist [1] har i inledningen till sin forskningsrapport Number of Solutions of Linear Congruence Systems angett syftet med sitt arbete och tidigare forskning inom området. Författarna har bland annat teoretiskt visat att antalet lösningar η för ett system av linjära kongruenser med n obekanta Ax ≡ 0 (mod m) det(A) och m. under vissa förutsättningar är en funktion av Vår uppgift har varit att manuellt och med hjälp av datorprogram veriera denna del av resultaten i rapporten. I rapporten behandlas modulus som utgörs av k primtal p, eller potenser av primtal p , där k är ett godtyckligt positivt heltal. Detta eftersom varje heltal i > 2, som inte är ett primtal, kan delas upp i primtalsfaktorer. I rapporten behandlas både homogena och inhomogena linjära kongruenser med n obekanta, med n > 1. I vårt uppdrag ingår endast att undersöka homo- gena linjära system av kongruenser med n = 2 och = sgd(det(A), pk ), skriver att om ηp k att n = 2 och n = 3. Nilsson och Nyqvist p gäller nk att ηpnk = sgd(det(A), p ). nk−γ i rapporten, ηγ = p , där någon koecient i A är relativt prima med för övriga system gäller Ytterligare en formel för beräkning av η presenters γ = l0 + l1 + l2 . γ beräknas med hjälp av Gauss-eliminering. Nilsson och Nyqvist nk−γ beskriver formeln ηγ = p för det allmänna fallet i avsnitt 3 i sin rapport. En detaljerad beskrivning av beräkning av η för n = 2 och n = 3 ges i avsnitt 2.3. All programmering har skett i programspråket Java. 2.2 Arbetsgång Vi inleder arbetet med att ta fram alla lösningar för ett system med n=2 n = 3 Resultaten och samtidigt räkna lösningarna, samt beräknar ηp k och ηpnk . eller sparas på en textl. Program: CountSolutions.java. Detta program är dock så krävande avseende datorkraft och minne att man endast kan välja relativt små värden (mindre än 10) på p och k för att inte orimligt långa exekveringstider skall erhållas. För att kunna testa med större värden på lösningar η för ett specikt system för p och k räknar vi antalet n = 2 eller n = 3 och beräknar ηpk och ηpnk . ηγ på ett specikt system med n = 3. Program: OneSolution.java. Vi testar sedan Program: CountWithGamma.java. Slutligen genererar vi slumpmässigt ett valfritt antal system och för varje system räknas antalet lösningar ηγ η, och ηpk , ηpnk och beräknas. Antalet rätta värden för vardera formeln summeras och presenteras tillsammans med siran på antalet räknade lösningar (= rätt antal lösningar). Alla resultat sparas på en textl. Program: CountSolCorr.java. 9 2.3 Bestämning av antal lösningar η. Algoritm. η. A. Icke-selektiv algoritm för att räkna antal lösningar 1. Läs in ett värde på antal obekanta 2. Läs in värde på p och n, 2 eller 3. k. 3. Öppna en textl. Zpk = {0, 1, . . . , pk − 1} 4. För varje matris A med element ur (a) Sätt räknare för lösningar η=0 x i Znpk k Om Ax ≡ 0 (mod p ), sätt η = η + 1. Skriv ut koecienterna för A samt x (b) För varje i. ii. (c) Beräkna det(A), ηpk = sgd(det(A), pk ) (d) Skriv ut η , det(A), ηpk och ηpnk och ηpnk = sgd(det(A), pnk ) på textlen. 5. Stäng textlen när alla matriser A har behandlats. Programmet för att icke-selektivt räkna antal lösningar, CountSolutions.java nns i avsnitt 3.1.1. B. Selektiv algoritm för att räkna antal lösningar 1. Läs in ett värde på 2. Läs in värde på p n och (antal obekanta: 2 eller 3). k. 3. Läs in värden på koecienterna 4. Beräkna ai,j i n × n-matrisen (a) Sätt räknare för lösningar Zpk = {0, 1, . . . , pk − 1}. η=0 x i Znpk k Om Ax ≡ 0 (mod p ), sätt η = η + 1. (b) För varje (c) Beräkna antalet lösningar 6. Skriv ut A. det(A). 5. För den inlästa matrisen A med element ur i. η. det(A),η , ηpk , och ηp k och ηpnk . 10 ηpnk . Programmet för att selektivt räkna antal lösningar, OneSolution.java nns i avsnitt 3.1.2. C. Selektiv algoritm för att räkna antal lösningar med 1. Läs in en 3 × 3-matris A, samt värden på 2. Om samtliga element är delbara med e från A och reducera modulo med p 0 . 3. Beräkna l0 4. Sätt pe 0 p ηγ . och k. , där e0 Algoritm för n = 3. är ett heltal, bryt ut pe0 = k − e0 . γ = l0 . 5. Finn en koecient aij , sådan att sgd(aij , pk ) = 1. 6. Placera denna koecient i rad och kolonn 1 i matrisen, med eventuella rad- och kolonnbyten. 7. Bestäm den multiplikativa inversen a−1 ij till aij . −1 8. Multiplicera rad 1 med aij och ai,1 samt addera till rad −1 k gäller att aij · aij ≡ 1 (mod p ). 9. Vi kan nu betrakta de fyra koecienterna på rad 3 som en 2×2 3 och där i = 2, 3. Det 2 och samt kolonn -matris, låt oss kalla den R. 10. Om samtliga element rij i R är delbara med e e ut p 1 från R och reducera modulo med p 1 . 11. Beräkna l1 2 i, pe1 , där e1 är ett heltal, bryt = l0 − e1 . 12. Finn en koecient rij , sådan att sgd(rij , pk ) = 1. 13. Placera denna koecient i rad och kolonn 1 i matrisen, med eventuella rad- och kolonnbyten. 14. Bestäm den multiplikativa inversen −1 15. Multiplicera rad 1 med rij och −1 k gäller att rij · rij ≡ 1 (mod p ). −1 rij ri,1 till rij . samt addera till rad 16. Efter omformningen av matrisen R, så har vi i rad 1 skild från 0, låt oss kalla den r22 . 17. Skapa l2 på e2 = 0. 1 r22 : 1 r22 = 0, sätt e2 = k − l0 − l1 . 1 1 delar r22 , sätt e2 = r22 /p. Om Om Om p 11 2 i, där i = 2. Det endast en koecient Om 1 sgd(r22 , p) = 1, sätt 18. Bilda γ = l0 + l1 + l2 . 19. Beräkna antal lösningar = pnk−γ . Programmet för att beräkna antalet lösningar för linjära kongruenssystem med tre obekanta, med ηγ CountWithGamma.java nns i avsnitt 3.1.3. ηpk , ηpnk (n = 3). D. Algoritm för att testa formlerna för beräkning av antal system med slumpgenererade koecienter och ηγ på ett bestämt 1. Läs in ett värde på antal system som skall genereras 2. Läs in värde på p och k. 3. Öppna en textl. (a) Räkna antal system som genereras. (b) För varje genererad matris A med element ur det(A). ηγ (Se algoritmen C). η , ηpk och ηpnk (Se algoritmen Zpk = {0, 1, . . . , pk − 1} i. Beräkna ii. Bestäm iii. Bestäm B). iv. Skriv ut värdena på textlen (c) Summera antal korrekta värden för ηpk , ηpnk och ηγ . 4. Skriv ut de summerade värdena på textlen. 5. Stäng textlen. Programmet för att testa formlerna för beräkning av ηpk , ηpnk och ηγ på ett bestämt antal system med slumpgenererade koecienter CountSolCorr.java nns i avsnitt 3.1.4. 12 2.4 Lösning av linjära kongruenssystem med två och tre obekanta n = 2 och n = 3 görs Nedan beskriver vi en metod för att lösa linjära kongruenssystem med n = 3. Metoden bygger på Gausseliminering från linjär algebra. För en reducering av systemet till n = 2, lösande av detta system, samt återgång till n = 3, där lösningen av systemet med n = 2 används för att få fram värden på den tredje obekanta variabeln. Vid lösning med dator används en algoritm som följer denna metod. Vi önskar lösa följande system av linjära kongruenser: a11 x1 + a12 x2 + a13 x3 ≡ 0 a21 x1 + a22 x2 + a23 x3 ≡ 0 a31 x1 + a32 x2 + a33 x3 ≡ 0 där ai,j och k är heltal, samt (mod pk ) (mod pk ) (mod pk ), p är ett godtyckligt primtal. I stället för att arbeta i t systemet direkt arbetar vi med det i matrisform. Sätt A = (ai,j ), = (x1 , x2 , x3 ) t och = (0, 0, 0). Då kan systemet skrivas i matrisform på följande sätt: x 0 Ax ≡ 0 (mod pk ) där a11 a12 a13 A = a21 a22 a23 . a31 a32 a33 13 A. Metod för lösning för hand. n = 3, 1. För ett givet system med 2. Vi kontrollerar om sgd(aij , pk ) skriver vi upp matrisen A. = 1 aij . för något element för vilket detta gäller måste vi bryta ut påverkar lösningen av systemet. Se Exempel (Om vi inte nner något pl , där 0 < l < k. Detta 6). 3. Genom omyttningar, som vi dokumenterar, byter vi eventuellt a11 mot det funna elementet. −1 4. Vi tar nu fram den multiplikativa inversen a11 till vårt nya k plicerar första raden med denna modulo p . a11 , och multi- 5. Efter omyttningar och bearbetningar betecknar vi matriselementen varablerna 0 0 x1 , x2 och 0 x3 vi i matrisens andra rad har eliminerat 0 0 a22 pk , så att och i rad tre har eliminerat 0 a31 a32 . 7. I rad tre i systemet har vi nu 0 k 8. Om det gäller att sgd(a33 , p ) 0 k sgd(a33 , p ) lösningar. 0 0 a33 x3 ≡ 0 (mod pk ). = 1, har 0 x3 9. Genom att lösa den diofantiska ekvationen lösning på 0 x3 , nämligen 10. Övriga lösningar är sats och . 6. Vi tillämpar nu Gausseliminering med hänsyn tagen till modulus och 0 aij 0 endast lösningen 0 a33 x3 − (pk )y = 0 annars nns får vi fram en x3 = x0 . 0 x3 = x0 + 6. pk 0 k k, k = 0, 1, . . . , sgd(a 0 33 , p ) − 1. sgd(a33 , pk ) 11. Insättning av det funna värdet/värdena på eller era värden på 0 0, 0 0 x3 Se i rad två i systemet ger ett x2 . 12. Insättning av dessa värden i rad ett i systemet och omyttning till ursprungsordningen och bestämning av x1 ger slutresultatet. 14 Exempel 5. Vi vill lösa följande linjära kongruenssystem (n = 3, p = 3, k = 3). 3 3x1 + 26x2 + 2x3 ≡ 0 (mod 3 ) 15x1 + 23x2 + 1x3 ≡ 0 (mod 33 ) 24x1 + 17x2 + 4x3 ≡ 0 (mod 33 ). Vi skriver om systemet på formen x ≡ 0 (mod pk ) A och får 3 26 2 A = 15 23 1 24 17 4 Vi låter kolonn 1 och 3 byta plats för att matrisen blir 2 är relativt prima med 27. Den nya 2 26 3 B = 1 23 15 4 17 24 Vi tar nu fram den multiplikativa inversen till 2x ≡ 1 Vi multiplicerar rad 1 med (mod 27) 14 ger 2 x ≡ 14 (mod 27). och får matrisen 1 13 15 C = 1 23 15 4 17 24 1 från rad 2 och rad 3 så att matrisen får följande 1 13 15 0 D = 0 10 0 19 18 Vi subtraherar multiplar av rad utseende Vi får nu ett specialfall, eftersom andra koecienten i rad lösa ut 3 är 0. Vi kan direkt x2 . 10x2 ≡ 0 Rad 1 (mod 27) och sgd(10, 27) = 1 ger att x2 ≡ 0 ger 18x1 ≡ 0 (mod 27) och 15 η = sgd(18, 27) = 9. (mod 27). 1 och 3. x1 ≡ 0 (mod 27), resterande lösningar ges av 27 k, k = 0, 1, 2, . . . , sgd(18, 27) − 1. x1 = 0 + sgd(18, 27) rad 1 i matrisen A ger Observera att vi bytte plats på kolonn En lösning är Insättning i 2x3 ≡ −3x1 x3 ≡ 14 · 24x1 Insättning av lösningarna för x1 3x1 + 0 + 2x3 ≡ 0 (mod 27) (mod 27) ≡ 24x1 (mod 27) (mod 27) ≡ 12x1 (mod 27) ger x3 ≡ 12 · 0 x3 ≡ 12 · 3 (mod 27) ≡ 36 x3 ≡ 12 · 6 (mod 27) ≡ 72 x3 ≡ 12 · 9 (mod 27) ≡ 108 x3 ≡ 12 · 12 (mod 27) ≡ 144 x3 ≡ 12 · 15 (mod 27) ≡ 180 x3 ≡ 12 · 18 (mod 27) ≡ 216 x3 ≡ 12 · 21 (mod 27) ≡ 252 x3 ≡ 12 · 24 (mod 27) ≡ 288 Lösningarna för x3 (mod 27) ≡ 0 (mod 27) ≡ 9 (mod 27) ≡ 18 (mod 27) ≡ 0 (mod 27) ≡ 9 (mod 27) ≡ 18 (mod 27) ≡ 0 (mod 27) ≡ 9 (mod 27) ≡ 18 är: x3 ≡ 0 (mod 27) x3 ≡ 9 (mod 27) x3 ≡ 18 (mod 27) Lösningarna för det givna systemet är: x1 x2 x3 0 0 0 3 0 9 6 0 18 9 0 0 12 0 9 15 0 18 18 0 0 21 0 9 24 0 18 16 (mod (mod (mod (mod (mod (mod (mod (mod (mod 27) 27) 27) 27) 27) 27) 27) 27) 27) Exempel 6. Vi vill lösa följande linjära kongruenssystem (n = 3, p = 3, k = 2). 6x1 + 6x2 + 3x3 ≡ 0 3x1 + 6x2 + 3x3 ≡ 0 3x1 + 3x2 + 3x3 ≡ 0 (mod 32 ) (mod 32 ) (mod 32 ). Vi skriver om systemet på formen x ≡ 0 (mod pk ) A och får 6 6 3 A = 3 6 3 3 3 3 Vi ser genast att ingen koecient är relativt prima med 9. Vi bryter ut 3, och får en ny matris 2 2 1 B = 1 2 1 1 1 1 Vi låter rad 1 och rad och en gång till rad 3 3 byta plats, samt adderar nya rad 1 två gånger till rad 2 och får matrisen 1 1 1 0 C = 0 1 0 0 22 Motsvarande kongruenssystem är x 1 + x 2 + x 3 x2 2x3 ≡0 ≡0 ≡0 (mod 3) (mod 3) (mod 3). Vi får x2 = 0 ∈ Z3 och x3 = 0 ∈ Z3 , ef tersom sgd(1, 3) = 1 och sgd(2, 3) = 1. Vi måste nu lyfta dessa värden till värdena 0, 3 och 6 i Z9 . Z9 . 17 Det gäller att 0 i Z3 svarar mot de tre Möjliga lösningar (., 0, 0), (., 0, 3), (., 0, 6) (., 3, 0), (., 3, 3), (., 3, 6) (., 6, 0), (., 6, 3), (., 6, 6) där punkten svarar mot värden på x1 , som vi ska nna. Vi skriver om första raden i det ursprungliga kongruenssystemet 6x1 ≡ −6x2 − 3x3 Vi har att sgd(6, 9) = 3, (mod 32 ) ≡ 3x2 + 6x3 det vill säga att varje parentes i Möjliga lösningar ovan ger upphov till tre lösningar. (., 0, 0) ger 6x1 ≡ 3 · 0 + 6 · 0 (mod 32 ) ≡ 0 (mod 32 ) 6 · 0 ≡ (mod 32 )ger(0, 0, 0) 6 · 3 ≡ (mod 32 )ger(3, 0, 0) 6 · 6 ≡ (mod 32 )ger(6, 0, 0) (., 3, 0) ger (mod 32 ). 6x1 ≡ 3 · 3 + 6 · 0 (mod 32 ) ≡ 0 (mod 32 ) 6 · 0 ≡ (mod 32 )ger(0, 3, 0) 6 · 3 ≡ (mod 32 )ger(3, 3, 0) 6 · 6 ≡ (mod 32 )ger(6, 3, 0) På samma sätt fås de övriga lösningarna. 18 Lösningarna för det givna systemet är: x1 x2 x3 0 0 0 0 0 3 0 0 6 0 3 0 0 3 3 0 3 6 0 6 0 0 6 3 0 6 6 3 0 0 3 0 3 3 0 6 3 3 0 3 3 3 3 3 6 3 6 0 3 6 3 3 6 6 6 0 0 6 0 3 6 0 6 6 3 0 6 3 3 6 3 6 6 6 0 6 6 3 6 6 6 19 B. Programmering och testkörning I projektet ingår att ta reda på och visa rätt värde på antalet lösningar, samt att testa formler för beräkning av värden på antalet lösningar. I programmen CountSolutions.java och OneSolution.java visas också lösningarna. I 3.3 visas exempel på resultat från testkörningar. 20 3 Avslutning 3.1 Sammanfattning och resultat av genomförda test 3.1.1 Sammanfattning av test Fyra olika test har genomförts med hjälp av datorprogram. 1. För samtliga linjära kongruenssystem med n=2 och n=3 har vi räknat antal lösningar där både koecienter och värden på de obekanta tillåts anta k alla värden i mängden {0, 1, . . . , p − 1}. För varje system beräknas antalet k nk lösningar med formlerna ηpk = sgd(det(A), p ) och ηpnk = sgd(det(A), p ). Alla resultat sparas på en textl. På grund av att antalet data som testas snabbt ökar med stigande värde på för små värden på p och k p och k, kan metoden endast användas (mindre än10). Program: CountSolutions.java. 2. För att kunna testa med större värden på p och k görs ett test där man räknar antalet lösningar för ett linjärt kongruenssystem med två eller tre obekanta (valbart i programmet), där de obekanta tillåts anta alla värden i mängden {0, 1, . . . , pk −1} för en uppsättning koecienter i mängden {0, 1, . . . , pk −1}, som läses in från tangentbordet. Program: OneSolution.java. 3. Beräkning av antalet lösningar för ett linjärt kongruenssystem med tre obenk−γ kanta med ηγ = p , där γ = l0 + l1 + l2 . γ beräknas med hjälp av Gaussk eliminering. Systemets koecienter som tillåts ligga i mängden {0, 1, . . . , p − 1} samt p och k läses in från tangentbordet. Program: CountWithGam- ma.java. 4. I programmet CountSolCorr.java slumpgenererar vi matriser för tre obekanta. m m stycken koecient- läses in från tangentbordet. Vi bestämmer för varje matris rätt antal lösningar och beräknar sedan antalet lösningar med k nk nk−γ formlerna ηpk = sgd(det(A), p ), ηpnk = sgd(det(A), p ), samt ηγ = p , där γ = l0 + l1 + l2 . Resultatet läggs i en textl. γ beräknas med hjälp av Gauss-eliminering. Program: CountSolCorr.java. 3.1.2 Resultat 1. När det(A) 6= 0 ger både 2. När det(A) = 0 blir ηγ ηpk , ηpnk rätt, ηpnk och ηγ fel, och 21 rätt värde på antal lösningar. ηpk blir rätt när sgd(A, p) = 1. 3.2 Program 3.2.1 CountSolutions.java import java.util.*; import java.io.*; import java.lang.*; public class CountSolutions { // Program:CountSolutions.java /* För varje system (matris A), testar vi alla kombinationer av x och y och räknar antalet lösningar(count_solutions). För varje matris som ger lösning skriver vi ut matrisen A (n=2: koeff. a,b,c,d; n=3: koeff. abcdefgho) och värdena på variablerna(x,y),determinanten för A och count_solutions = rätt antal lösningar för A samt de beräknade värdena för antalet lösningar eta_pk = sgd(detA,(int)Math.pow(p,k)) och eta_pnk = sgd(detA,(int)Math.pow(p,(n*k))) på en textfil respk.txt. Testen kan göras för n = 2 och n = 3. */ public static void main(String[] args) { Scanner keyboard = new Scanner(System.in); int n = 0; //systemstorlek int p = 0; //primtal int k = 0; //exponent -"22 int int int int int int int int x = 0; //systemvariabel y = 0; // -"z = 0; // -"_ detA = 0; eta_pk = 0; eta_pnk = 0; count_solutions = 0; //antal lösningar för ett system total_solutions = 0; //totalt antal lösningar System.out.print("Ange storleken n (2 eller 3)"); System.out.println(" på system-matrisen A: "); n = keyboard.nextInt(); int[][] a = new int[n][n]; System.out.print("Skriv ett primtal p: "); p = keyboard.nextInt(); System.out.print("Skriv ett heltal k: "); k = keyboard.nextInt(); keyboard.nextLine(); //töm bufferten System.out.println ("Skriv ett filnamn: "); String fileName = keyboard.nextLine(); if(n == 2) { try { PrintWriter out = new PrintWriter(fileName); //rubrik på textfilen resnpk.txt out.println("Resultatfil resnpk.txt, skapad"); out.println("med n = "+n+", p = "+p+" och k = "+k); out.println("Program: CountSolutions.java"); out.println(); 23 out.println(" a, b, c, d, x, y"); out.println("_____________________________"); out.println(); for(a[0][0]=0;a[0][0]<(int)Math.pow(p,k);a[0][0]++) for(a[0][1]=0;a[0][1]<(int)Math.pow(p,k);a[0][1]++) for(a[1][0]=0;a[1][0]<(int)Math.pow(p,k);a[1][0]++) for(a[1][1]=0;a[1][1]<(int)Math.pow(p,k);a[1][1]++) { count_solutions = 0; detA = det_a(a,n); eta_pk = sgd(detA,(int)Math.pow(p,k)); eta_pnk = sgd(detA,(int)Math.pow(p,(n*k))); for( x = 0; x < Math.pow(p,k); x++) for( y = 0; y < Math.pow(p,k); y++) if((((a[0][0]*x +a[0][1] *y) % Math.pow(p,k))== 0) && (((a[1][0]*x + a[1][1]*y)% Math.pow(p,k))== 0)) { count_solutions++; String param = ("a= "+a[0][0]+" b= "+a[0][1] +" c= "+a[1][0]+" d= "+a[1][1] +" x= "+x+" y= "+y); out.println(param); } String result = ("Antal lösningar = " +count_solutions+", det(A)= "+detA +", eta_pk = "+eta_pk+", eta_pnk = "+eta_pnk); out.println(result); } total_solutions = total_solutions + count_solutions; out.println("Totalt antal lösningar för p = "+p +" och k = "+k+": "+total_solutions); 24 //stäng filen out.close(); } catch(Exception w) { System.out.println("Kunde inte spara filen"); w.printStackTrace(); } } if(n == 3) { try { PrintWriter out = new PrintWriter(fileName); //rubrik på textfilen resnpk.txt out.println("Resultatfil resnpk.txt, skapad"); out.println("med n = "+n+", p = "+p+" och k = "+k); out.println("Program: CountSolutions.java"); out.println(); out.println(" a, b, c, d, e, f, g, h, o, x, y"); out.println("__________________________________________________________"); out.println(); for(a[0][0] = 0;a[0][0]<(int)Math.pow(p,k);a[0][0]++) for(a[0][1]=0;a[0][1]<(int)Math.pow(p,k);a[0][1]++) for(a[0][2]=0;a[0][2]<(int)Math.pow(p,k);a[0][2]++) for(a[1][0]=0;a[1][0]<(int)Math.pow(p,k);a[1][0]++) for(a[1][1]=0;a[1][1]<(int)Math.pow(p,k);a[1][1]++) for(a[1][2]=0;a[1][2]<(int)Math.pow(p,k);a[1][2]++) for(a[2][0]=0;a[2][0]<(int)Math.pow(p,k);a[2][0]++) for(a[2][1]=0;a[2][1]<(int)Math.pow(p,k);a[2][1]++) for(a[2][2]=0;a[2][2]<(int)Math.pow(p,k);a[2][2]++) { count_solutions = 0; 25 detA = det_a(a,n); eta_pk = sgd(detA,(int)Math.pow(p,k)); eta_pnk = sgd(detA,(int)Math.pow(p,(n*k))); for( x = 0; x < Math.pow(p,k); x++) for( y = 0; y < Math.pow(p,k); y++) for( z = 0; z < Math.pow(p,k); z++) if((((a[0][0]*x +a[0][1] *y + a[0][2]*z) % Math.pow(p,k))== 0) && (((a[1][0]*x + a[1][1]*y + a[1][2]*z)% Math.pow(p,k))== 0) && (((a[2][0]*x + a[2][1]*y + a[2][2]*z)% Math.pow(p,k))== 0)) { count_solutions++; String param = ("a= "+a[0][0]+" b= "+a[0][1]+" c= "+a[0][2] +" d= "+a[1][0]+" e= "+a[1][1]+" f= "+a[1][2]+" g= "+a[2][0] +" h= "+a[2][1]+" o= "+a[2][2]+" x= "+x+" y= "+y+" z= "+z); out.println(param); } String result = ("Antal lösningar = " +count_solutions+", det(A)= "+detA +", eta_pk = "+eta_pk+", eta_pnk = "+eta_pnk); out.println(result); } total_solutions = total_solutions + count_solutions; out.println("Totalt antal lösningar för p = "+p +" och k = "+k+": "+total_solutions); //stäng filen out.close(); } catch(Exception e) { System.out.println("Kunde inte spara filen"); e.printStackTrace(); 26 } } } public static int det_a(int [][] m, int q) { int s = 0;; if(q == 2) { s = (m[0][0]*m[1][1] - m[1][0]*m[0][1]); } if(q == 3) { s = (m[0][0]*m[1][1]*m[2][2]+m[1][0]*m[2][1]*m[0][2] +m[2][0]*m[0][1]*m[1][2]-m[2][0]*m[1][1]*m[0][2] -m[0][0]*m[2][1]*m[1][2]-m[1][0]*m[0][1]*m[2][2]); } return s; } public static int sgd(int m, int n) { if (m == 0) { return n; } else { m = Math.abs(m); while (m != n) { 27 if (m > n) { m -= n; } else { n -= m; } } } return m; } } 3.2.2 OneSolution.java import java.util.*; import java.io.*; import java.lang.*; public class OneSolution { // Program: OneSolution.java /*Program som ger antalet lösningar för ett homogent kongruenssystem med n = 2 eller 3 variabler om p och k samt en motsvarande system-matris A(modulo p upphöjt till k) matas in. Formlerna eta_pk och eta_pnk för att beräkna antalet lösningar testas. */ public static void main(String[] args) { Scanner keyboard = new Scanner(System.in); 28 int int int int int int int int int n = 0; p = 0; k = 0; s = 0; t = 0; antlosn = 0; count_solutions = 0; eta_pk = 0; //beräknat värde på antal lösn. eta_pnk = 0;// -"- System.out.print("Ange storleken n (2 eller 3)"); System.out.println(" på system-matrisen A: "); n = keyboard.nextInt(); int[][] a = new int[n][n]; System.out.print("Skriv ett primtal p: "); p = keyboard.nextInt(); System.out.print("Skriv en exponent k: "); k = keyboard.nextInt(); for(int i = 0; i < n; i++) for(int j = 0; j < n; j++) { System.out.println("Skriv ett heltal < "+ (int)Math.pow(p,k) + " : "); a[i][j] = keyboard.nextInt(); } keyboard.nextLine(); //töm bufferten int detA = det_a(a,n); System.out.println("det(A) = "+ detA); 29 if (n == 2) { for(int x = 0; x < Math.pow(p,k); x++) for(int y = 0; y < Math.pow(p,k); y++) if(((a[0][0]*x +a[0][1] *y) % Math.pow(p,k)== 0) && ((a[1][0]*x + a[1][1]*y)% Math.pow(p,k)== 0)) { count_solutions++; } } if(n == 3) { for(int x = 0; x < Math.pow(p,k); x++) for(int y = 0; y < Math.pow(p,k); y++) for(int z = 0; z < Math.pow(p,k); z++) if((((a[0][0]*x +a[0][1] *y + a[0][2]*z) % Math.pow(p,k))== 0) && (((a[1][0]*x + a[1][1]*y + a[1][2]*z)% Math.pow(p,k))== 0) && (((a[2][0]*x + a[2][1]*y + a[2][2]*z)% Math.pow(p,k))== 0)) { count_solutions++; } } eta_pk = sgd(detA, (int)Math.pow(p,k)); eta_pnk = sgd(detA, (int)Math.pow(p,(n*k))); System.out.println("Antal lösningar(progr) = "+count_solutions); System.out.print("Antal lösningar m formeln "); System.out.println("sgd(detA,(int)Math.pow(p,k))= " + eta_pk ); System.out.print("Antal lösningar m formeln "); System.out.println("sgd(detA,(int)Math.pow(p,(n*k)))= "+ eta_pnk); public static int det_a(int [][] m, int q) { 30 int s = 0;; if(q == 2) { s = (m[0][0]*m[1][1] - m[1][0]*m[0][1]); } if(q == 3) { s = (m[0][0]*m[1][1]*m[2][2]+m[1][0]*m[2][1]*m[0][2] +m[2][0]*m[0][1]*m[1][2]-m[2][0]*m[1][1]*m[0][2] -m[0][0]*m[2][1]*m[1][2]-m[1][0]*m[0][1]*m[2][2]); } return s; } public static int sgd(int m, int n) { if (m == 0) { return n; } else { m = Math.abs(m); while (m != n) { if (m > n) { m -= n; } else { n -= m; } } } return m; 31 } } 3.2.3 CountWithGamma.java import java.util.*; import java.io.*; import java.lang.*; public class CountWithGamma { public static void main(String[] args) { //Program för beräkning av antal lösningar för ett homogent linjärt //kongruenssystem med 3 variabler med "gammaformeln". //Antal lösningar = p^(n*k-gamma), gamma = l_0 + l_1 + l_2, se nedan. //Program: CountWithfGamma.java Scanner keyboard = new Scanner(System.in); int i = 0; int j = 0; int n = 0; int p = 0; int k = 0; int l_0 = 0; int l_1 = 0; int l_2 = 0; int antlosn = 0; int e_0 = 0; int e_1 = 0; int e_2 = 0; 32 int int int int int int int int int d_0 = 0; d_1 = 0; d_2 = 0; t = 0; r = 0; s = 0; u = 0; v = 0; gamma = 0; System.out.println("Storleken n på system-matrisen A är 3"); //programmet är skrivet för n=3 n = 3; //matrisen F = kopia av A-matrisen //B = R-matrisen int[][] a = new int[n][n]; //int[][] d = new int[n][n]; int[][] f = new int[n][n]; int[][] b = new int[n-1][n-1]; int[][] temp1 = new int[n][n]; int[][] temp2 = new int[n-1][n-1]; //c används för att spara ordningen mellan variablerna int[]c = new int[n]; for( i = 0; i < n; i++) { c[i] = i+1; //c[0]=1,c[1]=2,c[3]=3; } System.out.print("Skriv ett primtal p: "); p = keyboard.nextInt(); System.out.print("Skriv en exponent k: "); k = keyboard.nextInt(); //inläsning av matrisen for( i = 0; i < n; i++) 33 for( j = 0; j < n; j++) { System.out.println("Skriv ett heltal < " + (int)Math.pow(p,k) + " : "); a[i][j] = keyboard.nextInt(); } keyboard.nextLine(); //töm bufferten int detA = det_a(a,n); System.out.print("Testar gamma-formeln på inlästa system med n = 3, p = "+p); System.out.println(" och k = "+k); System.out.println(); System.out.println("det(A) = "+ detA); System.out.println("Ursprunglig "+ n +"x"+n+"-matris."); printMatr(a,n); //beräkning av d0 och gamma för den ursprungliga matrisen. e_0 = create_e(a,p,k,n); d_0 = (int)Math.pow(p,e_0); l_0 = k - e_0; gamma = l_0; copyFromAToF(d_0,a,f,n); //här bryter man ut p^(e_0) changeColAndRow(d_0, f, c, p, k, n, n); findAndUseInv(d_0,f,p,k,n); //omforma hela nxn-matrisen //R-matrisen en del av A-matrisen copyFromFToB(f,b,n); 34 // utbrytning, samt beräkning av d1 och gamma för B e_1 = create_e(b,p,k,n-1); d_1 = (int)Math.pow(p,e_1); l_1 = l_0 - e_1; gamma = l_0 + l_1; //här bryter man ut p^e_1 for( i = 0; i < (n-1); i++) for( j = 0; j < (n-1); j++) { temp2[i][j] = b[i][j]/(int)Math.pow(p,e_1); b[i][j] = temp2[i][j]; } changeColAndRow(d_0*d_1, b, c, p, k, n, n-1); //omforma hela n-1xn-1-matrisen findAndUseInv(d_0*d_1,b, p, k, n-1); //beräkning av d_2 och gamma för r[1][1] = b[1][1]]. e_2 = create_e2(e_0, e_1, b[1][1], p, k); d_2 = (int)Math.pow(p,e_2); l_2 = l_1 - e_2; gamma = gamma + l_2; 35 System.out.println("Gammaformeln ger antalet lösningar = " +(int)Math.pow(p,(n*k-gamma))); public static int det_a(int [][] m, int q) { int s = 0;; if(q == 2) { s = (m[0][0]*m[1][1] - m[1][0]*m[0][1]); } if(q == 3) { s = (m[0][0]*m[1][1]*m[2][2]+m[1][0]*m[2][1]*m[0][2] +m[2][0]*m[0][1]*m[1][2]-m[2][0]*m[1][1]*m[0][2] -m[0][0]*m[2][1]*m[1][2]-m[1][0]*m[0][1]*m[2][2]); } return s; } public static int sgd(int m, int n) { if (m == 0) { return n; } else { m = Math.abs(m); while (m != n) { if (m > n) { m -= n; } else 36 { } } n -= m; } return m; } public static void changeColAndRow(int d, int [][]m, int [] n,int p, int k,int t,int q) { int i = 0; int j = 0; int temp = 0; int v = 0; int g = 1000; while(g > 1 && i < q) { for(j = 0; j < q; j++) { g = sgd(m[i][j],(int)Math.pow(p,k)/d); if(g == 1) break; } if(g == 1 || i == q-1) break; else i++; } for(int r = 0; r < q; r++)//rad[0] och rad[i] byter plats { temp = m[i][r]; m[i][r] = m[0][r]; m[0][r] = temp; } if(sgd(m[0][0],(int)Math.pow(p,k)/d) != 1) for( int r = 0; r < q; r++)//kol[0] och kol[j] byter plats { temp = m[r][j]; m[r][j] = m[r][0]; m[r][0] = temp; 37 } } public static void findAndUseInv(int d, int [][]m, int p, int k,int q) //anpassat till om utbrytning skett eller ej { int i,j,r,s = 0; int ainv = findInv(d,m,p,k); for( j = 0; j < q; j++) { //mul. 1:a raden m. ainv r = (int)Math.pow(p,k); s = (int)r/d; m[0][j] = (m[0][j]*ainv)% s; } for( i = 1; i < q; i++) { int temp = m[i][0]; for( j = 0; j < q; j++) { m[i][j] = (m[i][j]- temp*m[0][j])% s; if(m[i][j] < 0) m[i][j] = m[i][j] + s; } } } public { static int findInv(int d, int [][]m, int p, int k)//anpassat t.utbrytn int r,s = 0; int j = 0; r = (int)Math.pow(p,k); s = (int)(r/d); for(int i = 0; i < ((int)Math.pow(p,k)); i++) { if(((m[0][0]*i) % s) == 1) { 38 j = i; } if(j != 0) break; } return j; } public static void copyFromFToB(int[][]m,int[][]p,int q) { for(int i = 1; i < q; i++) for(int j = 1; j < q; j++) { p[i-1][j-1] = m[i][j]; } } public static void copyFromAToF(int d, int[][]m,int[][]p,int q) { for(int i = 0; i < q; i++) for(int j = 0; j < q; j++) { p[i][j] = m[i][j]/d; } } public static void printIndex(int []u, int q) { for(int i = 0; i < q ; i++) System.out.print(u[i]+" "); System.out.println(); } public static void printMatr(int [][]m, int q) { int i,j = 0; 39 for( i = 0; i < q; i++) { for( j = 0; j < q; j++) { System.out.print(m[i][j]+" "); } System.out.println(); } System.out.println(); } public static int create_e(int m[][],int p,int k,int q) { int e = 0; int l = 0;int i = 0; int j = 0; int[][] temp = new int[q][q]; for( i=0; i<q;i++) { for( j=0;j<q;j++) { temp[i][j] = m[i][j]; } } for( i=0; i<q;i++) { for( j=0;j<q;j++) { if(temp[i][j]%p == 0) l++; } } if(l%(int)Math.pow(q,2)== 0) e = (int)l/(int)Math.pow(q,2); else e = 0; return e; } public static int create_e2(int u, int v, int t, int p, int k) 40 { int e = 0; int s = t; if(s == 0) e = k - u - v; if(sgd(s,p) == 1) e = 0; if((s % p == 0)&& (s > 0)) { do { e++; s = (int)s/p; } while((s % p== 0)&& (s > 0)); } return e; } } 3.2.4 CountSolCorr.java import java.util.*; import java.io.*; import java.lang.*; public class CountSolCorr { public static void main(String[] args) { /* * Programmet ger värdet på antalet lösningar för ett antal(läses in) slumpmässigt skapade matriser. Antalet lösningar beräknas sedan 41 * * med de tre formlerna(eta_pk, eta_pnk och eta_gamma) varefter dessa värden jämförs med de värden som programmet gett (eta_prog = rätt värde). n = 3. Program: CountSolCorr.java */ Scanner int i = int j = int k = int l = int m = int n = int p = int q = int int int int int int int int int int int int int int int int int int int int int int int keyboard = new Scanner(System.in); 0; 0; 0; 0; 0; 0; 0; 0; l_0 = 0; l_1 = 0; l_2 = 0; antlosn = 0; e_0 = 0; e_1 = 0; e_2 = 0; d_0 = 0; d_1 = 0; d_2 = 0; t = 0; eta_pnk = 0; r = 0; s = 0; eta_pk = 0; u = 0; v = 0; ant_syst = 0; //antal testade system gamma = 0; eta_gamma = 0; count_solutions = 0; pk = 0; pnk = 0; 42 int nk_gamma = 0; String param = ""; System.out.println("Storleken n på system-matrisen A är 3"); System.out.println(); //programmet är skrivet för n=3 n = 3; System.out.println("Ange hur många system vill du testa: "); ant_syst = keyboard.nextInt(); //matrisen F = kopia av A-matrisen //B = R-matrisen int[][] a = new int[n][n]; int[][] d = new int[n][n]; int[][] f = new int[n][n]; int[][] b = new int[n-1][n-1]; int[][] temp1 = new int[n][n]; int[][] temp2 = new int[n-1][n-1]; System.out.print("Skriv ett primtal p: "); p = keyboard.nextInt(); System.out.print("Skriv en exponent k: "); k = keyboard.nextInt(); keyboard.nextLine(); System.out.println("Skriv ett filnamn: "); String fileName = keyboard.nextLine(); try { PrintWriter out = new PrintWriter(fileName); //a_11 osv matriselement, detA = determinanten för A, //eta_prog = rätt antal lösningar(fås av programmet) //eta_pk = sgd(detA,(int)Math.pow(p,k)), //eta_pnk = sgd(detA,(int)Math.pow(p,n*k)) 43 //eta_gamma = sgd(detA,(int)Math.pow(p,n*k-gamma)) //rubrik på textfilen resnpk.txt out.println("Resultatfil respk.txt, skapad"); out.println("med n = "+n+", p = "+p+" och k = "+k); out.println("Program: CountSolCorr.java"); out.println(); out.print("a_11,a_12,a_13,a_21,a_22,a_23,a_31,a_32,a_33"); out.println(",detA,eta_prog,eta_pk,eta_pnk,eta_gamma"); //Vi skapar och testar ant_syst system for(l = 1;l < ant_syst+1; l++ ) { //c används för att spara ordningen mellan variablerna int[]c = new int[n]; for( i = 0; i < n; i++) { c[i] = i+1; //c[0]=1,c[1]=2,c[3]=3; } //Matrisen A slumpgenereras for( i = 0; i < n; i++) for( j = 0; j < n; j++) { q = (int)(Math.random()* (int)Math.pow(p,k) ); a[i][j] = q; } //Determinanten för A beräknas och skrivs ut int detA = det_a(a,n); System.out.println("Den slumpgenererade "+ n +"x"+n+"-matrisen."); printMatr(a,n); 44 copyFromAToD(a,d,n); //beräkning av d0 och gamma för den utsprungliga matrisen. e_0 = create_e(a,p,k,n); d_0 = (int)Math.pow(p,e_0); l_0 = k - e_0; gamma = l_0; copyFromAToF(d_0,a,f,n); changeColAndRow(d_0, f, c, p, k, n, n); findAndUseInv(d_0,f,p,k,n); //omforma hela nxn-matrisen //R-matrisen en del av A-matrisen copyFromFToB(f,b,n); e_1 = create_e(b,p,k,n-1); d_1 = (int)Math.pow(p,e_1); l_1 = l_0 - e_1; gamma = l_0 + l_1; for( i = 0; i < (n-1); i++) for( j = 0; j < (n-1); j++) { temp2[i][j] = b[i][j]/(int)Math.pow(p,e_1); b[i][j] = temp2[i][j]; 45 } changeColAndRow( d_1, b, c, p, k, n, n-1); findAndUseInv(d_1,b, p, k, n-1); e_2 = create_e2(e_0, e_1, b[1][1], p, k); d_2 = (int)Math.pow(p,e_2); l_2 = l_1 - e_2; gamma = gamma + l_2; eta_pk = sgd(detA, (int)Math.pow(p,k)); eta_pnk = sgd(detA, (int)Math.pow(p,(n*k))); eta_gamma = (int)Math.pow(p,(n*k-gamma)); for(int x = 0; x < Math.pow(p,k); x++) for(int y = 0; y < Math.pow(p,k); y++) for(int z = 0; z < Math.pow(p,k); z++) if((((d[0][0]*x +d[0][1] *y + d[0][2]*z) % Math.pow(p,k))== 0) && (((d[1][0]*x + d[1][1]*y + d[1][2]*z)% Math.pow(p,k))== 0) && (((d[2][0]*x + d[2][1]*y + d[2][2]*z)% Math.pow(p,k))== 0)) { count_solutions++; param = (d[0][0]+" "+d[0][1]+" "+d[0][2]+" "+d[1][0]+" "+d[1][1] +" "+d[1][2]+" "+d[2][0]+" "+d[2][1]+" "+d[2][2]+" " +detA+" "+count_solutions+" "+eta_pk+" "+eta_pnk +" "+eta_gamma); } out.println(param); if(eta_pk == count_solutions) 46 pk++; if(eta_pnk == count_solutions) pnk++; if(eta_gamma == count_solutions) nk_gamma++; count_solutions = 0; } out.println(); out.println("n = "+n+" p = "+p+" k = "+k); out.println("Antal testade system = "+ant_syst); out.print("Antal rätta lösningar m formeln "); out.println("sgd(detA,(int)Math.pow(p,k))= " + pk ); out.print("Antal rätta lösningar m formeln "); out.println("sgd(detA,(int)Math.pow(p,n*k))= "+ pnk); out.println("Antal rätta lösningar med gammaformeln = "+nk_gamma); out.close(); } catch(Exception e) { System.out.println("Kunde inte spara filen"); e.printStackTrace(); } System.out.println("n = "+n+" p = "+p+" k = "+k); System.out.println("Antal testade system = "+ant_syst); System.out.print("Antal rätta lösningar m formeln "); System.out.println("sgd(detA,(int)Math.pow(p,k))= " + pk ); System.out.print("Antal rätta lösningar m formeln "); System.out.println("sgd(detA,(int)Math.pow(p,(n*k)))= "+ pnk); System.out.println("Antal rätta lösningar med gammaformeln = "+nk_gamma); } public static int det_a(int [][] m, int q) { 47 int s = 0;; if(q == 2) { s = (m[0][0]*m[1][1] - m[1][0]*m[0][1]); } if(q == 3) { s = (m[0][0]*m[1][1]*m[2][2]+m[1][0]*m[2][1]*m[0][2] +m[2][0]*m[0][1]*m[1][2]-m[2][0]*m[1][1]*m[0][2] -m[0][0]*m[2][1]*m[1][2]-m[1][0]*m[0][1]*m[2][2]); } return s; } public static int sgd(int m, int n) { if (m == 0) { return n; } else { m = Math.abs(m); while (m != n) { if (m > n) { m -= n; } else { n -= m; } } } return m; 48 } //p en kopia av den inlästa matrisen m public static void copyFromAToD(int[][]m,int[][]p,int q) { for(int i = 0; i < q; i++) for(int j = 0; j < q; j++) { p[i][j] = m[i][j]; } } public static void changeColAndRow(int d, int [][]m, int [] n,int p, int k,int t,int q) { int i = 0; int j = 0; int temp = 0; int v = 0; int g = 1000; while(g > 1 && i < q) { for(j = 0; j < q; j++) { g = sgd(m[i][j],(int)Math.pow(p,k)/d); if(g == 1) break; } if(g == 1 || i == q-1) break; else i++; } for(int r = 0; r < q; r++)//rad[0] och rad[i] byter plats { temp = m[i][r]; m[i][r] = m[0][r]; m[0][r] = temp; } if(sgd(m[0][0],(int)Math.pow(p,k)/d) != 1) for( int r = 0; r < q; r++)//kol[0] och kol[j] byter plats { temp = m[r][j]; 49 m[r][j] = m[r][0]; m[r][0] = temp; } } public static void findAndUseInv(int d, int [][]m, int p, int k,int q) //anpassat till om utbrytning skett eller ej { int i,j,r,s = 0; int ainv = findInv(d,m,p,k); for( j = 0; j < q; j++) { //mul. 1:a raden m. ainv r = (int)Math.pow(p,k); s = (int)r/d; m[0][j] = (m[0][j]*ainv)% s; } for( i = 1; i < q; i++) { int temp = m[i][0]; for( j = 0; j < q; j++) { m[i][j] = (m[i][j]- temp*m[0][j])% s; if(m[i][j] < 0) m[i][j] = m[i][j] + s; } } } public { static int findInv(int d, int [][]m, int p, int k)//anpassat t. utbrytn int r,s = 0; int j = 0; r = (int)Math.pow(p,k); s = (int)(r/d); for(int i = 0; i < ((int)Math.pow(p,k)); i++) 50 { if(((m[0][0]*i) % s) == 1) { j = i; } if(j != 0) break; } return j; } public static void copyFromFToB(int[][]m,int[][]p,int q) { for(int i = 1; i < q; i++) for(int j = 1; j < q; j++) { p[i-1][j-1] = m[i][j]; } } //p en kopia av den inlästa matrisen m,d>1 om vi i föreg.steg brutit ut mult.av p public static void copyFromAToF(int d, int[][]m,int[][]p,int q) { for(int i = 0; i < q; i++) for(int j = 0; j < q; j++) { p[i][j] = m[i][j]/d; } } public static void printMatr(int [][]m, int q) { int i,j = 0; for( i = 0; i < q; i++) { for( j = 0; j < q; j++) { System.out.print(m[i][j]+" "); 51 } System.out.println(); } System.out.println(); } public static int create_e(int m[][],int p,int k,int q) { int e = 0; int l = 0;int i = 0; int j = 0; int[][] temp = new int[q][q]; for( i=0; i<q; i++) { for( j=0; j<q; j++) { temp[i][j] = m[i][j]; } } for( i=0; i<q; i++) { for( j=0; j<q; j++) { if(temp[i][j]%p == 0) { l++; } } } if(l%(int)Math.pow(q,2)== 0) e = (int)l/(int)Math.pow(q,2); else e = 0; return e; } public static int create_e2(int u, int v, int t, int p, int k) { int e = 0; int s = t; 52 if(s == 0) e = k - u - v; if(sgd(s,p) == 1) e = 0; if((s % p == 0)&& (s > 0)) { do { e++; s = (int)s/p; } while((s % p== 0)&& (s > 0)); } return e; } } 3.3 Exempel på programkörningar. 3.3.1 ResCountSolutions.txt Resultatfil resnpk.txt, skapad med n = 2, p = 2 och k = 1 Program: CountSolutions.java a, b, c, d, x, y _____________________________ a= 0 b= 0 c= 0 d= a= 0 b= 0 c= 0 d= a= 0 b= 0 c= 0 d= a= 0 b= 0 c= 0 d= Antal lösningar = a= 0 b= 0 c= 0 d= a= 0 b= 0 c= 0 d= 0 x= 0 y= 0 0 x= 0 y= 1 0 x= 1 y= 0 0 x= 1 y= 1 4, det(A)= 0, eta_pk = 2, eta_pnk = 4 1 x= 0 y= 0 1 x= 1 y= 0 53 Antal lösningar = 2, det(A)= 0, eta_pk = 2, eta_pnk = 4 a= 0 b= 0 c= 1 d= 0 x= 0 y= 0 a= 0 b= 0 c= 1 d= 0 x= 0 y= 1 Antal lösningar = 2, det(A)= 0, eta_pk = 2, eta_pnk = 4 a= 0 b= 0 c= 1 d= 1 x= 0 y= 0 a= 0 b= 0 c= 1 d= 1 x= 1 y= 1 Antal lösningar = 2, det(A)= 0, eta_pk = 2, eta_pnk = 4 a= 0 b= 1 c= 0 d= 0 x= 0 y= 0 a= 0 b= 1 c= 0 d= 0 x= 1 y= 0 Antal lösningar = 2, det(A)= 0, eta_pk = 2, eta_pnk = 4 a= 0 b= 1 c= 0 d= 1 x= 0 y= 0 a= 0 b= 1 c= 0 d= 1 x= 1 y= 0 Antal lösningar = 2, det(A)= 0, eta_pk = 2, eta_pnk = 4 a= 0 b= 1 c= 1 d= 0 x= 0 y= 0 Antal lösningar = 1, det(A)= -1, eta_pk = 1, eta_pnk = 1 a= 0 b= 1 c= 1 d= 1 x= 0 y= 0 Antal lösningar = 1, det(A)= -1, eta_pk = 1, eta_pnk = 1 a= 1 b= 0 c= 0 d= 0 x= 0 y= 0 a= 1 b= 0 c= 0 d= 0 x= 0 y= 1 Antal lösningar = 2, det(A)= 0, eta_pk = 2, eta_pnk = 4 a= 1 b= 0 c= 0 d= 1 x= 0 y= 0 Antal lösningar = 1, det(A)= 1, eta_pk = 1, eta_pnk = 1 a= 1 b= 0 c= 1 d= 0 x= 0 y= 0 a= 1 b= 0 c= 1 d= 0 x= 0 y= 1 Antal lösningar = 2, det(A)= 0, eta_pk = 2, eta_pnk = 4 a= 1 b= 0 c= 1 d= 1 x= 0 y= 0 Antal lösningar = 1, det(A)= 1, eta_pk = 1, eta_pnk = 1 a= 1 b= 1 c= 0 d= 0 x= 0 y= 0 a= 1 b= 1 c= 0 d= 0 x= 1 y= 1 Antal lösningar = 2, det(A)= 0, eta_pk = 2, eta_pnk = 4 a= 1 b= 1 c= 0 d= 1 x= 0 y= 0 Antal lösningar = 1, det(A)= 1, eta_pk = 1, eta_pnk = 1 a= 1 b= 1 c= 1 d= 0 x= 0 y= 0 Antal lösningar = 1, det(A)= -1, eta_pk = 1, eta_pnk = 1 a= 1 b= 1 c= 1 d= 1 x= 0 y= 0 a= 1 b= 1 c= 1 d= 1 x= 1 y= 1 Antal lösningar = 2, det(A)= 0, eta_pk = 2, eta_pnk = 4 Totalt antal lösningar för p = 2 och k = 1: 28 3.3.2 ResOneSolution.txt 54 Körning med OneSolution.java : Ange storleken n (2 eller 3) på system-matrisen A: 3 Skriv ett primtal p: 3 Skriv en exponent k: 3 Skriv ett heltal < 27 : 3 Skriv ett heltal < 27 : 26 Skriv ett heltal < 27 : 2 Skriv ett heltal < 27 : 15 Skriv ett heltal < 27 : 23 Skriv ett heltal < 27 : 1 Skriv ett heltal < 27 : 24 Skriv ett heltal < 27 : 17 Skriv ett heltal < 27 : 4 Matrisen A = 3 26 2 15 23 1 24 17 4 det(A) = -1305 Lösningarna: x= 0 y= 0 z= 0 x= 3 y= 0 z= 9 x= 6 y= 0 z= 18 x= 9 y= 0 z= 0 x= 12 y= 0 z= 9 x= 15 y= 0 z= 18 x= 18 y= 0 z= 0 x= 21 y= 0 z= 9 55 x= 24 y= 0 z= 18 Rätt antal lösningar = 9 Antal lösningar m formeln sgd(detA,(int)Math.pow(p,k))= 9 Antal lösningar m formeln sgd(detA,(int)Math.pow(p,(n*k)))= 9 3.3.3 ResCountWithGamma.txt Körning med CountWithGamma.java : Storleken n på system-matrisen A är 3 Skriv ett primtal p: 3 Skriv en exponent k: 3 Skriv ett heltal < 27 : 3 Skriv ett heltal < 27 : 26 Skriv ett heltal < 27 : 2 Skriv ett heltal < 27 : 15 Skriv ett heltal < 27 : 23 Skriv ett heltal < 27 : 1 Skriv ett heltal < 27 : 24 Skriv ett heltal < 27 : 17 Skriv ett heltal < 27 : 4 Testar gamma-formeln på inlästa system med n = 3, p = 3 och k = 3 det(A) = -1305 Ursprunglig 3x3-matris. 3 26 2 15 23 1 24 17 4 Gammaformeln ger antalet lösningar = 9 3.3.4 ResCountSolCorr.txt 56 Resultatfil respk.txt, skapad med n = 3, p = 3 och k = 3 Program: CountSolCorr.java a_11,a_12,a_13,a_21,a_22,a_23,a_31,a_32,a_33,detA,eta_prog,eta_pk,eta_pnk,eta_gamma 9 14 3 11 11 11 17 13 22 -11 1 1 1 1 17 3 9 24 10 21 16 6 4 -886 1 1 1 1 9 20 3 15 2 9 2 16 3 -1074 3 3 3 3 23 4 26 16 3 5 19 9 11 1662 3 3 3 3 16 15 4 2 10 15 20 18 17 1734 3 3 3 3 5 4 11 20 8 23 25 25 18 2005 1 1 1 1 0 11 13 3 23 7 14 4 4 -3084 3 3 3 3 0 18 25 24 23 21 19 19 14 1609 1 1 1 1 7 21 25 11 7 5 25 18 12 386 1 1 1 1 25 21 26 25 9 19 10 12 5 2250 9 9 9 9 n = 3 Antal Antal Antal Antal p = 3 k = 3 testade system = 10 rätta lösningar m formeln sgd(detA,(int)Math.pow(p,k))= 10 rätta lösningar m formeln sgd(detA,(int)Math.pow(p,n*k))= 10 rätta lösningar med gammaformeln = 10 57 Litteraturförteckning [1] Marcus Nilsson och Robert Nyqvist, Number of Solutions of Linear Congruence Systems, Forskningsrapport, BTH Karlskrona och Linnéuniversitetet Växjö, 2012-08-24. [2] Ivan Niven, Herbert S. Zuckerman och Hugh l. Montgomery, An Introduction to the Theory of numbers, New York: John Wiley & Sons, 5th ed., 1991. [3] Robert Nyqvist, Kryptering och talteori, MA1119, kompendium, BTH Karlskrona, 2010. [4] Lars Nystedt, På tal om tal. En läsebok i matematik, Lars Nystedt och INSTANT MATHEMATICS, Uppsala 1993. Andra genomsedda upplagan 1995. [5] Oystein Ore, Invitation to Number Theory, The Mathematical Association of America, 6th printing, 1975. [6] Kenneth H. Rosen, Elementary Number Theory And Its Applications, Boston: Pearson Addison Wesley, 6th ed., 2011. 58