[edit. 29.9. Lisäsin while-silmukan koodin loppuun]
Selvitän tässä miten Python koodini toimii. Sen avulla voi laskea yksikkömurtolukujen jaksojen pituuksia. Esitän alussa miten käytin GeoGebraa koodaamisen apuna. Tein tämän koodinpätkän sekä for-silmukall että while-silmukalla.
Jakokulma
Minä kun olen sen verran kokenut, niin olen oppinut perinteisen jakokulman jo kansa- tai ainakin keskikoulussa. Jakokulmassa näki jaksollisuuden, kun jakojäännös toistui. Alla olevassa taulukossa solussa B1 on murtoluvun 1/7 osoittaja (murtoviivan yläpuolella oleva luku) ja C1 nimittäjä (se alapuoli). Solussa B2 on kokonaisosa ensimmäisestä jakolaskusta, solussa C2 ensimmäisen jakolaskun jakojäännös. Niiden alapuolella kokonaisosa 10* edellisestä kokonaisosasta ja jakojäännös.
Kun loin tämän esimerkin, niin käytin englanninkielisiä komentoja Div ja Mod, ne kun ovat lyhyempiä kirjoittaa. GeoGebrassa voi kirjoittaa suomenkielisessäkin versiossa englannikielisiä komentoja, toki ohjelma kääntää komennon suomenkielelle.
Alla on ½.
Seuraavassa kuvassa on ⅓.
Alla on tuotettu 1/7 = 0.142857142857… Kun tuota jaksoa tutkii, niin silloin saattaa nähdä, että jakso alkaa toistua, kun C-sarakkeelle eli jakojäännökseen tulee sama luku. Mikäli jakojäännökseen tulee nolla, niin desimaali on päättyvä. Niinpä eri vaihtoehtoja jakojäännökselle on vai nimittäjä – 1:n verran. Näin ollen luvun 1/n pisin mahdollinen jakso on 1 – n. Palaan tulevaisuudessa näihin syklisiin lukuihin. Ne ovat tällä hetkellä päällimmäisenä mielessäni.
Oheinen taulukko antaa aika hyvän mallin, miten selvittää jakson pituus. Pieni ongelma tulee desimaalin alussa olevien nollien kanssa. Esimerkiksi 1/35 = 0.0285714285714… Tässä jakso alkaa vasta kun jakojäännös 10 toistuu; ensimmäinen jakojäännösykkönen ei esiinny jakojäännöksissä ensiesiintymisensä jälkeen.
Tämän ongelman saa ratkaistua esimerkiksi kertomalla osoittaja kymmenellä, sadalla, … siten että kolmannella rivillä ei ole nollaa. Alla olevassa kuvassa solussa B2 on
=10^ceil(lg(C1))
Huomaa, että Pythonkoodissani en käytä logaritmia, sillä sen käyttö voi aiheuttaa pyöristysvirheitä isojen lukujen yhteydessä. Vähennän yhden nollan osoittajasta, se yksinkertaistaa algoritmiani.
Kun aloin koodaamaan tätä GeoGebralla, päädyin aika pitkiin komentoriveihin, en pystynyt enää hahmottamaan virheitä, niinpä luovutin. Saatan palata tähän mikäli saan kirkastettua algoritmini mielessäni GeoGebraan sopivaksi.
python koodi
Tutkin tässä vain jakson pituuksia, jätän yksinkertaisuuden vuoksi desimaalien jakson tuonnemmaksi. Pythonissa // laskee jakolaskun kokonaisosan ja % jakojäännöksen. Laitan koodiin print-funktioita, jotta koodin etenemistä voi seurata helpommin. Tämä olisi voinut olla hieman yksinkertaisempi while-silmukalla, päätin tehdä tämän kuitenkin for-silmukalla.
Pythonilla jakson pituuden etsiminen on suhteellisen suoraviivaista, käytän ensimmäisessä esimerkissä nimittäjän arvoa 6 rivillä 1.
Rivillä 3 lasketaan ensin nimittäjän numeroiden lukumäärä, str() muuttaa luvun 6 merkkijonoksi ”6” ja len() laskee sen pituuden. Lopulta ensimmäiseksi osoittajaksi määritellään 10^(numeroiden lukumäärä – 1). Kuutosen tapauksessa se on 10^0 = 1. Ideana on saada osoittajaan sellainen kymmenen potenssi, että siinä on yhtä monta nollaa kuin nimittäjässä on numeroita.
Rivillä 6 on ensimmäinen jakojäännös, eli tässä tapauksessa 1//6 = 1 ja rivillä 7 luodaan jakojäännöksien lista ja lisätään sinne ensimmäinen jakojäännös eli tässä tapauksessa ykkönen.
For-silmukassa rivillä tulostetaan seuraavan jakolaskun osoittaja ja nimittäjä, ensimmäisellä kierroksella 10 ja 6.
Rivillä 12 lasketaan uusi jakojäännös esimerkkitapauksessa (10*1)//6 = 10//6 = 4.
Riveillä 14 – 17 tutkitaan onko jakojäännös nolla. Mikäli se on, niin koko for-silmukka lopetetaan break-rivillä, ja samalla tiedetään, että kyseessä on päättyvä desimaalikehitelmä.
Riveillä 16-21 elifin avulla selvitetään onko jakojäännös jaannokset listassa, ensimmäisellä kierroksella se ei ole sillä listassa [1] ei ole nelosta.
Riveillä 22 – 23 lisätään jakojäännös jaannokset-listaan, mikäli edelliset ehdot eivät ole voimassa. Tässä vaiheessa 4 lisätään jaannokset-listaan. Jakojäännöslista on nyt [1, 4]
Seuraavassa for-silmukassa jakojäännös on 40//6 = 4 eli rivi 18 elif-ehto on voimassa. Jakson pituudeksi saadaan jaannokset-listan pituus – se paikka (Pythonin logiikalla) missä luku 4 on eli 2 – 1 = 1.
Muutamia esimerkkejä koodista. Tässä nimittäjä on 2.
Nimittäjän arvolla 7 saadaan pidempi jakso.
13
666
Tätä kirjoittaessani tajusin, että ensimmäinen jakojäännös on tietysti aina tuon ensimmäinen kymmenen potenssi eli 10**(len(str(nimittaja))-1).
python ohjelma
Siistin koodia ja teen python funktion, joka tulostaa jakson pituuden. Tuotan listan, jossa on vaikkapa 50 ensimmäisen yksikkömurtoluvun jaksojen pituudet. Samalla laitan jaksojen pituudet kuvaajaan.
100 ensimmäistä: [0, 0, 1, 0, 0, 1, 6, 0, 1, 0, 2, 1, 6, 6, 1, 0, 16, 1, 18, 0, 6, 2, 22, 1, 0, 6, 3, 6, 28, 1, 15, 0, 2, 16, 6, 1, 3, 18, 6, 0, 5, 6, 21, 2, 1, 22, 46, 1, 42, 0]
Python koodi löytyy Colabista https://colab.research.google.com/drive/1PzbK6xjohZq5KS80DHMqYREZ1cC0cGXR?usp=sharing
tarkistus
Pieni pelko tällaisen laskennan tuottamisessa on, että koodi laskee väärin. Edellisessä tarinassani kerroin T.D. Doen sivusta, jossa on lueteltu 10000 ensimmäisen yksikkömurtoluvun jaksojen pituudet. Loin luvuista listan nimeltä oikeat. Vaikuttaa siltä, että koodini laskee oikein.
Noiden 10000 luvun tuottamiseen kului Google Colabissa reilut 4 minuuttia. 20000 jakson pituuden laskemiseen kului noin 31 min. Vaikuttaa siltä, että laskenta-ajan kasvu on tyyppiä n^3.
while-silmukka
Koodin idea on lähes sama kuin edellisen luvun for-silmukassakin. Tämäkin koodi laskee jakson pituuden ja käyttää sitä apuna kun lopullinen jaksolista tuotetaan.
Oheinen jakso-funktio tulostaa yksikkömurtoluvun 1/n desimaalikehitelmän jakson listana ja jakson pituuden. Poiketen for-silmukka-koodista, niin aloitan desimaalien keräämisen suoraan 1//n:n jakamisella rivillä 2. Rivin 6 while-silmukka pyörii niin kauan kuin jakojäännöstä ei ole jaannokset-listassa.
Kun jakojäännös on jakojäännöslistassa, niin rivillä 11 päätellään jakson pituus. Lisäksi riveillä 12-14 muutetaan tuloste, jos jakojäännös on nolla. Lopulta rivillä 15 määritetään jaksolliset desimaalit ja jakson pituus.
Riveillä 21-24 tuotetaan 100 ensimmäistä jaksoa ja niiden pituudet listaksi.
Ajallisesti tämä koodi vaikuttaa yhtä vikkelältä kuin for-silmukkakin. 10000 jakson tuottamisen Colabissa kului reilut 4 min.
Nyt kun minulla on helppo tapa tuottaa jakson pituuksia ja itse jaksoja, voin tulevissa tarinoissa keskittyä matematiikkaan.
lähteet
Edellinen artikkelini aiheesta
https://mikkorahikka.blog/2022/09/20/yksikkomurtolukujen-desimaalikehitelmien-jakson-pituuksista-1-pohdittavaa-ongelmia/
Koodi löytyy Colabissa
https://colab.research.google.com/drive/1PzbK6xjohZq5KS80DHMqYREZ1cC0cGXR?usp=sharing
Repeating decimal Wikipediassa
https://en.wikipedia.org/wiki/Repeating_decimal
Cyclic number MathWorldissa
https://mathworld.wolfram.com/CyclicNumber.html
2 Replies to “Yksikkömurtolukujen desimaalikehitelmien jakson pituuksista 2 – jakson pituus Pythonilla”