Matematiikassa modulo-operaation tulos on aritmeettisen jaon jäännös. Kuten hyvin tiedetään, kahden kokonaisluvun aritmeettinen jako tuottaa osamäärän ja jäännöksen.
Muutkin sopimukset ovat kuitenkin mahdollisia. Tietokoneissa ja laskimissa on erilaisia tapoja tallentaa ja esittää numeroita. Niiden modulo-operaation määritelmä riippuu ohjelmointikielestä ja/tai taustalla olevasta laitteistosta.
Määritelmä ja merkintä
Yksinkertaisimmillaan modulo-operaatio a mod n tarkoittaa lukujen a ja n jakamista siten, että saadaan kaksi kokonaislukua q (osamäärä) ja r (jäännös) siten, että
a = n·q + r,
ja yleensä vaaditaan 0 ≤ r < |n|. Tällöin r on aritmeettinen jäännös ja sen merkintä on esimerkiksi a mod n tai pelkkä r. Matemaattisessa teoriassa puhutaan usein kongruenssista: a ≡ b (mod n) tarkoittaa, että n jakaa luvun a−b.
Jäännöksen ominaisuudet
- Rajaehdot: modulo:n vakio n ei saa olla nolla (jakaminen nollalla on määrittelemätöntä).
- Operaation säilyvyys: (a + b) mod n = ( (a mod n) + (b mod n) ) mod n ja vastaavasti kertolaskulle (a·b) mod n = ( (a mod n)·(b mod n) ) mod n.
- Kongruenssit: a ≡ b (mod n) toimii ekvivalenssirelaationa ja jakaa kokonaisluvut n yhtä suuriin luokkiin (jäännösluokkiin).
- Käänteinen: luvulla a on modulo n mukaansaottava multiplikatiivinen käänteinen vain jos gcd(a,n)=1.
Negatiiviset luvut ja ohjelmointikielien erot
Keskeinen ero käytännöissä syntyy siitä, miten jaon osamäärä q määritellään, kun a tai n on negatiivinen. On kaksi yleistä tapaa:
- Trunkkaus nollaan (truncate toward zero): q valitaan siten, että se on a/n pyöristetty nollaa kohti. Tästä seuraava r voi olla negatiivinen tai positiivinen, ja jäännöksen etumerkki vastaa jaettavan (dividend) etumerkkiä. Tämän käytännön käyttävät mm. C, C++ ja Java. Esimerkiksi -17 % 5 antaa usein -2.
- Euclidinen (floor) jako: q = floor(a/n), jolloin 0 ≤ r < |n| (kun n>0) ja jäännös on aina ei-negatiivinen, jos modulus on positiivinen. Python käyttää tätä käyttäytymistä niin, että a % b antaa tuloksen, jonka etumerkki vastaa jakajan (divisor) etumerkkiä; esimerkiksi -17 % 5 = 3.
Tämän vuoksi on tärkeää tarkistaa, miten käyttämäsi ohjelmointikieli määrittelee modulo- tai remainder-operaation. Lisäksi liukuluvuille (float) on omat funktionsa, esimerkiksi C-kirjaston fmod, joka palauttaa liukuluvun jäännöksen ja käyttäytyy usein kuten trunc-tyyppinen remainder.
Esimerkkejä
- 17 mod 5 = 2, koska 17 = 5·3 + 2.
- -17 mod 5:
- Trunc-tyyli (C/Java): -17 = 5·(-3) + (-2) → remainder = -2.
- Euclidinen/floor-tyyli (Python): -17 = 5·(-4) + 3 → remainder = 3.
- 17 mod -5 Pythonissa: 17 % -5 = -3 (floor(17/−5) = −4 → 17 = (−5)·(−4) + (−3)).
Sovelluksia ja käytännön huomioita
Modulo-operaatiota käytetään laajasti tietojenkäsittelyssä ja matematiikassa: kellonajan laskemiseen, ringeissä ja jäännösluokissa, hajautustauluissa (hashing), satunnaislukugeneraattoreissa, kryptografiassa (esim. moduloilla toimivat eksponentoinnit) sekä kiertävissä puskurirakenteissa.
Kun ohjelmoit modulo-operaatioita, huomioi seuraavat seikat:
- Modulo nolla on määrittelemätöntä — vältä jakamista nollalla.
- Tarkista, mitä käyttäytymistä kieli noudattaa negatiivisilla luvuilla, erityisesti jos algoritmisi edellyttää aina ei-negatiivista jäännöstä.
- Liukuluvuilla työskentely vaatii sopivia funktioita (esim. fmod), ja tarkkuus- ja pyöristyskäytännöt vaikuttavat tuloksiin.
Yhteenvetona: modulo-operaation perusajatus on yksinkertainen — se tuottaa jakojäännöksen — mutta käytännön toteutus vaihtelee. Matemaattisessa teoriassa suositaan yleensä Euclidista määritelmää (ei-negatiivinen jäännös), kun taas ohjelmointikielet voivat käyttää eri konventioita.