A tucat mint mértékegység – azaz 12 darab valamiből – történelmi és gyakorlati okokból alakult ki. Nagyon jól működött piacokon, vásárokban, mert mivel sok osztója van, könnyű volt részekre bontani. Például egy tucat tojást könnyű felezni (6), harmadolni (4), negyedelni (3) vagy akár hatodolni (2). Mindenképp praktikusabb volt, mint például a 10 (csak 2 és 5 az osztói), amit nehezebb harmadolni vagy negyedelni.
Továbbá, egy tucat nem túl sok, de nem is túl kevés, és közel van a 10-hez. Tehát könnyen átlátható, felfogható egység, ezért ideális volt az áruk csoportosításához vagy csomagolásához. Ráadásul szó szerint kéznél volt még akkor is, ha egyik kézben a tele szatyor volt, mert a másik, szabad kézen a hüvelykujj segítségével a négy másik ujj ujjpercein lehetett számlálni.
Hasonló, azaz mind praktikus, mind matematikai okokra vezethető vissza az, hogy a kör középponti szöge miért 360 fok. A babiloni és sumér csillagászok a kör felosztásához valószínűleg a naptári ciklusokat vették alapul. Egy év nagyjából 360 napból áll, és bár tudták, hogy ez nem pontosan annyi, ez a szám kényelmes alapot szolgáltatott a kör felosztásához, mivel egyrészt illeszkedett az általuk használt 60-as számrendszerhez, másrészt közel van a napok számához, és ahogy a 60, úgy a 360 is sok osztóval rendelkezik. Ez pedig azt jelenti, hogy a kört könnyen fel lehet osztani egyenlő részekre anélkül, hogy törtszámokkal kellene bajlódni. Például, ha egy kört felezünk, harmadolunk, negyedelünk, vagy akár nyolcadolunk, az eredmény mindig egész fokszám lesz. Továbbá, sok alapvető geometriai alakzat (pl. háromszög, négyzet, hatszög) szögei könnyen kifejezhetők a 360 fokos rendszerben (pl. egyenlő oldalú háromszög belső szöge 60 fok, négyzeté 90 fok, szabályos ötszögé 108 fok).
Valószínűleg szintén az osztók nagy száma az oka, hogy 24 óra van egy napban.
Tehát annak, hogy egy egész számnak hány osztója van, nem csupán elméleti matematikai kérdés, hanem gyakorlati jelentőséggel is bír.
Ezért készítsünk egy függvényt, ami visszaadja az argumentumként megadott egész szám osztóinak számát. Ezt a matematikában osztószámfüggvénynek (divisor function) hívják. A számelméletben egy természetes szám osztóinak számát általában a prímtényezői kitevői alapján határozzák meg. Ez viszont azt igényli, hogy először elvégezzük a prímtényezőkre bontást és a kitevők meghatározását, majd azokkal való számolást. Programozási szempontból ennél egyszerűbb, ha sorba vesszük az egész számokat egészen az adott számig és ezekkel osztjuk az adott számot és vizsgáljuk az osztás maradékát. Az osztható eseteket pedig összeszámoljuk. Ez egyetlen sorban kódolható, ahogy azt a number_of_divisors függvény definíciójában látható alább.
|
1 2 3 4 5 |
def number_of_divisors(integer: int) -> int: """Osztószámfüggvény, amely visszaadja az argumentumként megadott egész összes osztójának számát.""" return sum(1 for divisor in range(1, integer + 1) if integer % divisor == 0) |
Használjuk fel ezt az osztószámfüggvényt egy olyan másik függvény előállításához, amely megadott kezdő- és végértékkel meghatározott egész számok tartományában megkeresi azt a számot, amelynek a legtöbb osztója van, majd ezzel az egésszel és osztóinak számával tér vissza. E függvény definíciója a következő:
|
1 2 3 4 5 6 7 8 9 |
def integer_with_most_divisors(start: int, end: int) -> tuple[int, int]: """A megadott kezdő- és végértékkel meghatározott egész számok tartományában megkeresi azt a számot, amelynek a legtöbb osztója van. Visszatérési értéke egy tuple, amelynek elemei a legtöbb osztóval rendelkező szám és osztóinak száma. """ dd = {k: divs for k in range(start, end) if (divs := number_of_divisors(k)) > 2} return max(dd.items(), key=lambda t: t[1]) |
A számelmélet erősen összetett számnak (highly composite number) nevezi az olyan pozitív egész számot, melynek több osztója van bármelyik nála kisebb pozitív egésznél. Készítsünk egy ilyen függvényt is. Hamar rájöhetünk, hogy e függvény az előző azon speciális változata, amikor a tartomány kezdőértéke mindig 2. E felismerés eszünkbe juttathatja, hogy olyan esetekben, amikor egy függvényt egy másikból úgy kapunk, hogy egy vagy több paraméter értéket rögzítjük, akkor az eredeti parciális függvényét kapjuk. Ennek létrehozásához használhatjuk a szabványos könyvtár functools moduljának partial() függvényét. Ezt alkalmazva az előző, integer_with_most_divisors függvénnyel kapjuk az alábbi highly_composite_number nevű hívható objektumot.
|
1 2 3 4 5 6 |
from functools import partial # A következő függvény visszaadja az argumentumként megadott számig a legerősebben összetett számot. highly_composite_number = partial(integer_with_most_divisors, 2) |
Nézzük, hogyan működnek a függvények.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
# TESZT print(integer_with_most_divisors(2, 10)) # (6, 4) print(integer_with_most_divisors(10, 15)) # (12, 6) print(integer_with_most_divisors(15, 30)) # (24, 8) print(integer_with_most_divisors(30, 100)) # (60, 12) print(integer_with_most_divisors(100, 700)) # (360, 24) print(highly_composite_number(10)) # (6, 4) print(highly_composite_number(15)) # (12, 6) print(highly_composite_number(30)) # (24, 8) print(highly_composite_number(100)) # (60, 12) print(highly_composite_number(700)) # (360, 24) |
A teszteredményekből nem csak az látszik, hogy a függvények az elvárások szerint működnek, hanem az is, hogy valóban, a 12 a legtöbb osztóval rendelkezik a tízhez közel, és a 360 is elég nagy tartományban bizonyul a legerősebben összetett számnak.
A bemutatott programkódokban beépített függvényeket, valamint a functools modul partial() függvényét használtuk. Ezekről a Python tudásépítés lépésről lépésre című e-könyv „Beépített függvények”, illetve a „Készétel fogyasztás – a szabványos könyvtár moduljainak használata” fejezet „Parciális hívható objektumok” című alfejezete foglalkozik.