Problemų sprendimo automatizavimas: Skirtumas tarp puslapio versijų

Iš Pitonas.
(Naujas puslapis: === Problemų sprendimo automatizavimas === Žmonės nuo senų laikų susiduria su įvairiais sudėtingais uždaviniais. Iki atsirandant kompiuteriams, uždaviniams, kurių sprendimui gauti reikėdavo atlikti daug skaičiavimo operacijų XIX ir XX a. pirmoje pusėje būdavo surenkamaos visos komandos skaičiuotojų, kurie turėdavo atlikti reikiamus skaičiavimus. Tokiu būdu būdavo atliekami sudėtingi skaičiavimai astronomijoje, apskaičiuojamos balistinių raketų traj...)
 
Nėra keitimo santraukos
 
(nerodoma 17 tarpinių versijų, sukurtų 2 naudotojų)
1 eilutė: 1 eilutė:
=== Problemų sprendimo automatizavimas ===
=== Problemų sprendimo automatizavimas ===
Žmonės nuo senų laikų susiduria su įvairiais sudėtingais uždaviniais. Iki atsirandant kompiuteriams, uždaviniams, kurių sprendimui gauti reikėdavo atlikti daug skaičiavimo operacijų XIX ir XX a. pirmoje pusėje būdavo surenkamaos visos komandos skaičiuotojų, kurie turėdavo atlikti reikiamus skaičiavimus. Tokiu būdu būdavo atliekami sudėtingi skaičiavimai astronomijoje, apskaičiuojamos balistinių raketų trajektorijos, atliekami skaičivimai orų prognozėms. Šis darbas būdavo ilgas ir gan nuobodus. Šiais laikais šias problemas galima automaitzuoti panaudojant kompiuterius.
Žmonės nuo senų laikų susiduria su įvairiais sudėtingais uždaviniais. Iki atsirandant kompiuteriams, uždaviniams, kurių sprendimui gauti reikėdavo atlikti daug skaičiavimo operacijų XIX ir XX a. pirmoje pusėje būdavo surenkamos visos komandos skaičiuotojų, kurie turėdavo atlikti reikiamus skaičiavimus. Tokiu būdu būdavo atliekami sudėtingi skaičiavimai astronomijoje, apskaičiuojamos balistinių raketų trajektorijos, atliekami skaičiavimai orų prognozėms. Šis darbas būdavo ilgas ir gan nuobodus. Šiais laikais šias problemas galima automatizuoti panaudojant kompiuterius.


=== Automatizavimo pavyzdžiai ===
=== Automatizavimo pavyzdžiai ===
17 eilutė: 17 eilutė:


==== Didelio kiekio duomenų apdorojimas ====
==== Didelio kiekio duomenų apdorojimas ====
Vienas iš dažniausių automatizavimo pritaikymų - didelio kiekio duomenų apdorojimas. Kompiuteriu galima greitai apdoroti didelį kiekį duomenų, kuriuos apdoroti žmogui užtruktų daug laiko. Įsivaizduokime kad turime failą, kur surašytos kiekvienos metų dienos vidutinė temperatūra farenheito skalėje. Mūsų tikslas suskaičiuoti vidutinę metų temperatūrą celsijaus skalėje. Be kompiuterio tektų ilgai ir nuobodžiai atlikti daug veiksmų. Nesunku kažkur ir klaidą padaryti. Tuo tarpu galime viską automatizuoti parašius tokį kodą:
Vienas iš dažniausių automatizavimo pritaikymų - didelio kiekio duomenų apdorojimas. Kompiuteriu galima greitai apdoroti didelį kiekį duomenų, kuriuos apdoroti žmogui užtruktų daug laiko. Įsivaizduokime kad turime failą, kur surašytos kiekvienos metų dienos vidutinė temperatūra Farenheito skalėje. Mūsų tikslas suskaičiuoti vidutinę metų temperatūrą Celsijaus skalėje. Be kompiuterio tektų ilgai ir nuobodžiai atlikti daug veiksmų. Nesunku kažkur ir klaidą padaryti. Tuo tarpu galime viską automatizuoti parašius tokį kodą:


<syntaxhighlight lang="python">
<syntaxhighlight lang="python">
def paversk_fahrenheitus_į_celsijų(temperatūra_farenheitais):
def paversk_farenheitus_į_celsijus(temperatūra_farenheitais):
     temperatūra_celsijais = (temperatūra_farenheitais - 32) * 5 / 9
     temperatūra_celsijais = (temperatūra_farenheitais - 32) * 5 / 9
     return temperatūra_celsijais
     return temperatūra_celsijais
31 eilutė: 31 eilutė:
         for eilutė in failas:
         for eilutė in failas:
             farenheitai = int(eilutė)
             farenheitai = int(eilutė)
             celsijai = paversk_fahrenheitus_į_celsijų(farenheitai)
             celsijai = paversk_farenheitus_į_celsijus(farenheitai)
             viso_celsijų += celsijai
             viso_celsijų += celsijai
             dienų_skaičius += 1
             dienų_skaičius += 1
39 eilutė: 39 eilutė:


if __name__ == "__main__":
if __name__ == "__main__":
     duomenų_failas = "temperatures.txt"
     duomenų_failas = "temperatūros.txt"
     vidutinė_temperatūra = skaičiuok_vidutinę_temperatūrą(duomenų_failas)
     vidutinė_temperatūra = skaičiuok_vidutinę_temperatūrą(duomenų_failas)
     print(f"Vidutinė temperatūra yra: {vidutinė_temperatūra}")
     print(f"Vidutinė temperatūra yra: {vidutinė_temperatūra}")
45 eilutė: 45 eilutė:


==== Itin sudėtingų problemų automatizavimas ====
==== Itin sudėtingų problemų automatizavimas ====
Egsituoja daug itin sudėtingų problemų, kurių automatizavimui reikalingi superkompiuteriai. Štai keletas pavyzdžių:
Egzistuoja daug itin sudėtingų problemų, kurių automatizavimui reikalingi superkompiuteriai. Štai keletas pavyzdžių:
1. Orų prognozė ir klimato modeliavimas. Orų prognozės modeliai naudoja didelius duomenų kiekius, gaunamus iš satelitų, radarų ir kitų šaltinių. Superkomiuteris gali apdorti šiuos duomenis realiu laiku. Tam naudojami sudėtingi algoritmai simuliuojantys atmosferos dinamiką, vandenynų sroves ir kitus atmosferos reiškinius. Tai leidžia gauti greitesnses ir tikslesnes orų prognezes, bei pasiruošti galimoms stichinėms nelaimėms.


2. Vaistų kūrimas. Superkompiuteris atlieka automatizuotas simuliacijas ir virtualius eksperimentus su modeliuojamais vaistais. Analizuojamos molekulinės struktūros ir jų tarpusavio sąveika. Taip nustatomos potencialios naujos vaistinės medžiagos ir įvertinamas jų naudingumas.
# ''Orų prognozė ir klimato modeliavimas''. Orų prognozės modeliai naudoja didelius duomenų kiekius, gaunamus iš satelitų, radarų ir kitų šaltinių. Superkompiuteris gali apdoroti šiuos duomenis realiu laiku. Tam naudojami sudėtingi algoritmai simuliuojantys atmosferos dinamiką, vandenynų sroves ir kitus atmosferos reiškinius. Tai leidžia gauti greitesnes ir tikslesnes orų prognozes, bei pasiruošti galimoms stichinėms nelaimėms.
 
# ''Vaistų kūrimas''. Superkompiuteris atlieka automatizuotas simuliacijas ir virtualius eksperimentus su modeliuojamais vaistais. Analizuojamos molekulinės struktūros ir jų tarpusavio sąveika. Taip nustatomos potencialios naujos vaistinės medžiagos ir įvertinamas jų naudingumas.
3. Transporto ir logistikos optimizacija. Superkompiuteriu optimizuojami transporto ir logistikos tinklai. Aviacijoje tai leidžia optimizuoti skrydžių maršrutus ir taip taupyti degalus, bei trumpinti kleionės laiką. Krovinių pervežime tai padeda optimizuoti maršrutus, efektyviai paskirstyti krovinius ir minimizuoti kaštus.
# ''Transporto ir logistikos optimizacija''. Superkompiuteriu optimizuojami transporto ir logistikos tinklai. Aviacijoje tai leidžia optimizuoti skrydžių maršrutus ir taip taupyti degalus, bei trumpinti kelionės laiką. Krovinių pervežime tai padeda optimizuoti maršrutus, efektyviai paskirstyti krovinius ir minimizuoti kaštus.


==== Uždaviniai, kuriuos sudėtinga automatizuoti ====
==== Uždaviniai, kuriuos sudėtinga automatizuoti ====
Ne visų problemų sprendimus įmanoma lengvai automatizuoti. Štai du pavyzdžiai:
Ne visų problemų sprendimus įmanoma lengvai automatizuoti. Štai du pavyzdžiai:
1. Matematinių teoremų įrodymas. Įrodyti teoremai gali reikėti patikrinti be galo daug įvairių galimybių ir atvejų. Dažnai to neįmanoma padryti automatizacijos būdu.


2. Sveikųjų skaičių faktorizavimas. Faktorizvaimo tikslas rasti duoto skaičiaus pirminius daugiklius. Pirminių skaičių faktorizavimu yra paremtos šiuolaikinės kriptografinės sistemos, pavyzdžiui RSA. Nors egzistuoja skaičių faktorizavimo algoritmai, tačiau faktorizuoti itin didelius skaičius, naudojamus kriptografijoje net ir galingais kompiuteriais užtrunka labai ilgai. Pavyzdžiui RSA dažnai galima sutikti raktus, kurie yra 2048 bitų ilgio sveikasis skaičius. Faktorizuoti tokį skaičių net ir galingu superkompiuteriu, gali užtrukti tūkstančius metų.
#''Matematinių teoremų įrodymas.'' Įrodyti teoremai gali reikėti patikrinti be galo daug įvairių galimybių ir atvejų. Dažnai to neįmanoma padaryti automatizacijos būdu.
# ''Sveikųjų skaičių faktorizavimas.'' Faktorizavimo tikslas rasti duoto skaičiaus pirminius daugiklius. Pirminių skaičių faktorizavimu yra paremtos šiuolaikinės kriptografinės sistemos, pavyzdžiui viešojo rakto kriptosistema [https://lt.wikipedia.org/wiki/RSA RSA]. Nors egzistuoja skaičių faktorizavimo algoritmai, tačiau faktorizuoti itin didelius skaičius, naudojamus kriptografijoje net ir galingais kompiuteriais užtrunka labai ilgai. Pavyzdžiui RSA dažnai galima sutikti raktus, kurie yra 2048 bitų ilgio sveikasis skaičius. Faktorizuoti tokį skaičių net ir galingu superkompiuteriu, gali užtrukti tūkstančius metų.


=== Skaičiavimo laikas ===
=== Skaičiavimo laikas ===
Vienas svarbiausių metrikų programavime yra programos vykdymo laikas. Vykdymo laiką tiesiogiai įtakoja naudojamų skaičiavimų sudėtingumas, duomenų kiekiai, bei naudojamo kompiuterio galia.
Viena svarbiausių metrikų programavime yra programos vykdymo laikas. Vykdymo laiką tiesiogiai įtakoja naudojamų skaičiavimų sudėtingumas, duomenų kiekis, bei naudojamo kompiuterio galia.


==== Skaičiavimo laiko nustatymas ====
==== Skaičiavimo laiko nustatymas ====
Nori sužinoti kiek ilgai užtruko įvykdyti tavo programą? Pitone tą galime padaryti keletu būdų. Pateiksime vieną iš jų
Nori sužinoti kiek ilgai užtruko įvykdyti tavo programą? Pitone tą gali padaryti keletu būdų. Pateikiu vieną iš jų:


<syntaxhighlight lang="python">
<syntaxhighlight lang="python">
#Importuojame paketą, leidžiantį dirbti su laiku
# Importuojame paketą, leidžiantį dirbti su laiku
import time
import time


#Pasiimimame algortimo vykdymo pradžios laiką
# Pasiimimame algortimo vykdymo pradžios laiką
pradžios_laikas = time.time()
pradžios_laikas = time.time()


# Čia įrašome kodą, kurio vykdymo trukmę norime matuoti
# Čia įrašome kodą, kurio vykdymo trukmę norime matuoti


#Pasiimame vykdymo pabaigos laiką
# Pasiimame vykdymo pabaigos laiką
pabaigos_laikas = time.time()
pabaigos_laikas = time.time()


#Paskaičiuojame vykdymo pradžios ir pabaigos laikus. Tai ir bus mūsų vykdymo laikas  
# Paskaičiuojame vykdymo pradžios ir pabaigos laikus. Tai ir bus mūsų vykdymo laikas  
vykdymo_laikas = pabaigos_laikas - pradžios_laikas
vykdymo_laikas = pabaigos_laikas - pradžios_laikas
print(f"Vykdymo laikas sekundėmis: {vykdymo_laikas:.6f}")
print(f"Vykdymo laikas sekundėmis: {vykdymo_laikas:.6f}")
82 eilutė: 81 eilutė:


==== Pasirinkto algoritmo įtaka skaičiavimo laikui ====
==== Pasirinkto algoritmo įtaka skaičiavimo laikui ====
Dažnai tą patį uždavinį galima spręsti ne vienu skirtingu būdu. Skaykime mums reikia suskaičiuoti visų skaičių nuo 1 iki N sumą. Tą padarysime dviem skirtingais būdais ir pamatuosime vykdymo laikus:
Dažnai tą patį uždavinį galima spręsti ne vienu skirtingu būdu. Sakykime mums reikia suskaičiuoti visų skaičių nuo 1 iki N sumą. Tą padarysime dviem skirtingais būdais ir pamatuosime vykdymo laikus:


<syntaxhighlight lang="python">
<syntaxhighlight lang="python">
#Importuojame paketą, leidžiantį dirbti su laiku
# Importuojame paketą, leidžiantį dirbti su laiku
import time
import time


def suma1(N):
def suma(N):
     result = 0
     rezultatas = 0
     #Skai2iuojame rezultatą tiesiogiai sudedami visus skaičius
     # Skaičiuojame rezultatą tiesiogiai sudėdami visus skaičius
     for i in range(1, N + 1):
     for i in range(1, N + 1):
         result += i
         rezultatas += i
     return result
     return rezultatas




99 eilutė: 98 eilutė:
N = int(input())
N = int(input())


 
# Pasiimame algoritmo vykdymo pradžios laiką
 
#Pasiimimame algortimo vykdymo pradžios laiką
pradžios_laikas = time.time()
pradžios_laikas = time.time()


print("Suma:", suma1(N))
print("Suma:", suma(N))


#Pasiimame vykdymo pabaigos laiką
# Pasiimame vykdymo pabaigos laiką
pabaigos_laikas = time.time()
pabaigos_laikas = time.time()


#Paskaičiuojame vykdymo pradžios ir pabaigos laikus. Tai ir bus mūsų vykdymo laikas  
# Paskaičiuojame vykdymo pradžios ir pabaigos laikus. Tai ir bus mūsų vykdymo laikas  
vykdymo_laikas = pabaigos_laikas - pradžios_laikas
vykdymo_laikas = pabaigos_laikas - pradžios_laikas
print(f"Vykdymo laikas sekundėmis: {vykdymo_laikas:.6f}")
print(f"Vykdymo laikas sekundėmis: {vykdymo_laikas:.6f}")
</syntaxhighlight>
</syntaxhighlight>
Įvykdžiau šį kodą savo kompiuteryje, su N=100000000 (100 milijonų), vykdymo trukmė buvo 11,6 sekundės
Įvykdžiau šį kodą savo kompiuteryje, su N=100000000 (100 milijonų), vykdymo trukmė buvo 11,6 sekundės


<syntaxhighlight lang="python">
<syntaxhighlight lang="python">
#Importuojame paketą, leidžiantį dirbti su laiku
# Importuojame paketą, leidžiantį dirbti su laiku
import time
import time


def suma2(N):
def suma(N):
     #Pasinaudojame matematine formule
     # Pasinaudojame matematine formule
     return N * (N + 1) // 2
     return N * (N + 1) // 2


127 eilutė: 125 eilutė:
N = int(input())
N = int(input())


 
# Pasiimame algoritmo vykdymo pradžios laiką
 
#Pasiimimame algortimo vykdymo pradžios laiką
pradžios_laikas = time.time()
pradžios_laikas = time.time()


print("Suma:", suma2(N))
print("Suma:", suma(N))


#Pasiimame vykdymo pabaigos laiką
# Pasiimame vykdymo pabaigos laiką
pabaigos_laikas = time.time()
pabaigos_laikas = time.time()


#Paskaičiuojame vykdymo pradžios ir pabaigos laikus. Tai ir bus mūsų vykdymo laikas  
# Paskaičiuojame vykdymo pradžios ir pabaigos laikus. Tai ir bus mūsų vykdymo laikas  
vykdymo_laikas = pabaigos_laikas - pradžios_laikas
vykdymo_laikas = pabaigos_laikas - pradžios_laikas
print(f"Vykdymo laikas sekundėmis: {vykdymo_laikas:.6f}")
print(f"Vykdymo laikas sekundėmis: {vykdymo_laikas:.6f}")
</syntaxhighlight>
</syntaxhighlight>


Dabar su N=100000000, vykdymo laikas tebuvo 0,001132 sekundės. Gali išbandyti abu kodo atvejus. Pamatysi, jog jų atsakymai bus vienodi, o vykdymo laikas skirsis daug kartų. Nors vykdymo greitis priklausys nuo konkretaus kompiuterio ir gali gauti kitokią trukmę, nei gavau aš, bet antrasis algoritmas visada bus greitesnis, nepriklausomai nuo nauodjamao kompiuterio.
Dabar su N=100000000, vykdymo laikas tebuvo 0,001132 sekundės. Gali išbandyti abu kodo atvejus. Pamatysi, jog jų atsakymai bus vienodi, o vykdymo laikas skirsis daug kartų. Nors vykdymo greitis priklausys nuo konkretaus kompiuterio ir gali gauti kitokią trukmę, nei gavau aš, bet antrasis algoritmas visada bus greitesnis, nepriklausomai nuo naudojamo kompiuterio.
 
=== Atmintis ===
Tam, kad galėtų įvykdyti tavo parašytą programą, kompiuteris turės naudoti operacinę atmintį laikyti vykdomam kodui, bei kintamiesiems.
Pitonas turi automatinę atminties valdymo sistemą. Ši sistema pasirūpina, kad būtų išskirta reikiama atmintis, programai vykdyti ir kad ta atmintis būtų atlaisvinta, kai programai jos nebereikia. Kai sukuriame naują kintamąjį ar objektą, pitonas išskirs reikiamą atminties kiekį reikšmei saugoti. Kai kintamasis taps nebereikalingas ta atmintis bus atlaisvinta. Tai padeda programuotojui susikoncentruoti į efektyvaus algoritmo kūrimą ir nesukti galvos dėl atminties atlaisvinimo.
 
==== Kiek atminties užima vienas kintamasis? ====
Patikrinti kiek atminties užima kintamasis, galima naudojant <code>sys.getsizeof</code>. Štai kodo pavyzdys:
<syntaxhighlight lang="python">
#Importuojame paketą, leidžiantį dirbti su atmintimi
import sys
skaičius = 10
print("Kintamojo užimama atmintis baitais:", sys.getsizeof(skaičius))
</syntaxhighlight>
 
Paleidęs kodą pamatysi, jog šis kintamasis užima 28 baitus. Taigi, sveikiesiems skaičiams saugoti pitonui reikės 28 baitų, bet labai dideliems skaičiams saugoti 28 baitų gali ir nepakakti. Pavyzdžiui: skaičiui 1234567890 saugoti pitonas jau naudos 32 baitus. Kitokio tipo kintamiesiems naudojamos atminties kiekis bus kitoks. Štai realiajam skaičiui saugoti reikės 24 baitų, o tekstui reikiamos atminties kiekis priklausys nuo teksto ilgio.
 
Dabartinių kompiuterių atmintis pakankamai didelė, tad sprendžiant šioje knygoje esančius uždavinius, greičiausiai neteks susidurti su atminties trūkumu. Tačiau sprendžiant dideles ir sudėtingas problemas atminties gali imti ir pristigti. Tad rašant programas svarbu pagalvoti kaip efektyviai naudoti atmintį.
 
==== Pasirinkto algoritmo įtaka naudojamos atminties kiekiui ====
Ankstesniame pavyzdyje jau matei, kad du skirtingi algoritmai gali išspręsti tą patį uždavinį, o jų veikimo greitis gali smarkiai skirtis. Taip pat ir su atmintimi. Gali būti skirtingų algoritmų, kuriems vykdyti reikės skirtingų atminties kiekių. Paimkime paprastą programą, kuri sudeda keturis skaičius 2 skirtingais būdais.
 
<syntaxhighlight lang="python">
def suma1 (sk1, sk2, sk3, sk4):
    suma = sk1 + sk2 + sk3 + sk4
    return suma
 
def suma2 (sk1, sk2, sk3, sk4):
    tarpinė_suma1 = sk1
    tarpinė_suma2 = tarpinė_suma1 + sk2
    tarpinė_suma3 = tarpinė_suma2 + sk3
    suma = tarpinė_suma3 + sk4
    return suma
</syntaxhighlight>
 
Abi funkcijos gražins tą patį rezultatą. Tačiau <code>suma1</code> naudoja tik 1 kintamąjį, o <code>suma2</code> naudoja net 4, tad ir atminties reikės net 4 kartus daugiau.
 
==== Atmintis, greitis ir kodo aiškumas ====
Kiekvienas programuotojas turbūt norėtų, kad jo parašytas kodas veiktų kiek galima greičiau, naudotų kiek galima mažiau atminties, būtų lengvai skaitomas ir keičiamas. Bet sprendžiant realius uždavinius to pasiekti gali būti neįmanoma. Gali tekti rinktis ar pasirinkti greitesnį algoritmą, kuris naudoja daugiau atminties ar tą kuris lėtesnis, bet taupoma atmintis. Bet gali būti ir taip kad itin greitas ar mažai atminties naudojantis algoritmas bus labai sudėtingas ir sunkiai suprantamas, o norint padaryti pakeitimus reikės labai daug programuotojo pastangų. Tuomet prioritetas gali būti teikimas ir kodo aiškumui. Tai kam skirti prioritetą: greičiui, atminčiai ar aiškumui? Tai priklauso nuo konkretaus uždavinio ir reikalavimų. Mokantis pitono rekomenduoju pirmiausia rašyti tvarkingą ir aiškų kodą. Bet visada pagalvok, ar gali kodą parašyt optimalesnį atminties ar greičio atžvilgiu.
 
=== Problemų formuluotės ===
==== Neaiškios formuluotės ====
Ar galėtum teisingai išspręsti tokius uždavinius su pitonu?:
 
1. Įvedus skaičių, programa turi atspausdinti "Didelis", jei skaičius didelis, arba "Mažas" - jei skaičius mažas.
2. Programa turi mokėti suskaičiuoti užrašytą sąlygą. Pavyzdžiui užrašius "2+3" turi būti atspausdinta 5.
 
Pirmu atveju gali parašyti tokį kodą:
<syntaxhighlight lang="python">
print ("Įvesk skaičių?")
skaičius = int(input())
if skaičius > 10:
  print("Didelis")
else:
  print ("Mažas")
</syntaxhighlight>
 
Ar žmogus pateikęs užduotį būtų patenkintas tokiu sprendimu? Sunku pasakyti, nes mes nežinome kas jo manymu yra didelis skaičius. Kaip turėtų būti suformuluota tokio uždavinio sąlygą, kad tokių neaiškumų nebeliktų? Ar tiktų toks formulavimas?:
 
Įvedus skaičių programa turi atspausdinti "Didelis", jei skaičius didesnis už 100, arba "Mažas" jei skaičius mažesnis už 100.
 
Nors uždavinio formuluotė jau aiškesnė, tačiau vis dar nėra pilnai aiški. Kas atsitiktų jei būtų įvestas skaičius 100? Užduoties formuluotė to nepaaiškina. Tad reikėtų ją dar patobulinti. Aiški sąlyga galėtų skambėti šitaip:
 
Įvedus skaičių programa turi atspausdinti "Didelis", jei skaičius didesnis arba lygus 100, arba "Mažas" jei skaičius mažesnis už 100.
 
Kaip spręstum antrąjį uždavinį? Galbūt parašytum programą kuri mokėtų sudėti daug skaičių? Bet ar to užtenka? Pavyzdyje nurodyta sudėtis, bet sąlygoje nėra parašyta, jog turi būti skaičiuojam '''tik''' sudėtis. Galbūt sąlygos autorius tikisi ir kitų veiksmų. Mes to nežinome, nes sąlyga nėra pakankamai aiški. Gerai suformuluota sąlyga galėtų skambėti šitaip:
 
Programa turi mokėti suskaičiuoti užrašytą sąlygą. Sąlygoje gali būti naudojamos sudėties, atimties, daugybos ir dalybos veiksmai, gali būti naudojami skliaustai. Pavyzdžiui užrašius "2+3" turi būti atspausdinta 5.
 
==== Perteklinės formuluotės ====
Gera problemos formuluotė neturėtų būti perteklinė. Perteklinė formuluotė nepadeda rasti geresnio sprendimo, bet gali netgi trukdyti įsigilinti į problemą. Pavyzdžiui:
 
Parašyk programą, kuri skaičiuotų stačiakampio plotą. Programa turi leisti įvesti stačiakampio ilgį ir plotį, o išvesti jo plotą. Formulės darbui su stačiakampiais:
Perimetras: 2 * (ilgis + plotis)
Plotas: ilgis * plotis
 
Nors šiame pavyzdyje prašoma suskaičiuoti plotą, bet taip pat duota ir perimetro formulė, kuri mums nereikalinga, šiai problemai išspręsti. Tai bereikalingai pailgina sąlygą ir gali sukelti papildomų klausimų kodėl ši formulė čia pateikta.

Dabartinė 11:59, 30 sausio 2025 versija

Problemų sprendimo automatizavimas

Žmonės nuo senų laikų susiduria su įvairiais sudėtingais uždaviniais. Iki atsirandant kompiuteriams, uždaviniams, kurių sprendimui gauti reikėdavo atlikti daug skaičiavimo operacijų XIX ir XX a. pirmoje pusėje būdavo surenkamos visos komandos skaičiuotojų, kurie turėdavo atlikti reikiamus skaičiavimus. Tokiu būdu būdavo atliekami sudėtingi skaičiavimai astronomijoje, apskaičiuojamos balistinių raketų trajektorijos, atliekami skaičiavimai orų prognozėms. Šis darbas būdavo ilgas ir gan nuobodus. Šiais laikais šias problemas galima automatizuoti panaudojant kompiuterius.

Automatizavimo pavyzdžiai

Aritmetinės operacijos

Turbūt vienas iš paprasčiausių automatizavimo pavyzdžių būtų - aritmetinių skaičiavimų automatizavimas. Parašykime paprastą programą:

print("Įvesk pirmą skaičių")
skaičius1 = int(input())
print("Įvesk antrą skaičių")
skaičius2 = int(input())
sandauga = skaičius1 * skaičius2
print("Skaičių sandauga yra:", sandauga)

Tokia paprasta automatizacija leidžia greitai sudauginti du didelius skaičius, išvengiant žmogaus klaidų.

Didelio kiekio duomenų apdorojimas

Vienas iš dažniausių automatizavimo pritaikymų - didelio kiekio duomenų apdorojimas. Kompiuteriu galima greitai apdoroti didelį kiekį duomenų, kuriuos apdoroti žmogui užtruktų daug laiko. Įsivaizduokime kad turime failą, kur surašytos kiekvienos metų dienos vidutinė temperatūra Farenheito skalėje. Mūsų tikslas suskaičiuoti vidutinę metų temperatūrą Celsijaus skalėje. Be kompiuterio tektų ilgai ir nuobodžiai atlikti daug veiksmų. Nesunku kažkur ir klaidą padaryti. Tuo tarpu galime viską automatizuoti parašius tokį kodą:

def paversk_farenheitus_į_celsijus(temperatūra_farenheitais):
    temperatūra_celsijais = (temperatūra_farenheitais - 32) * 5 / 9
    return temperatūra_celsijais

def skaičiuok_vidutinę_temperatūrą(duomenų_failo_pavadinimas):
    viso_celsijų = 0
    dienų_skaičius = 0

    with open(duomenų_failo_pavadinimas, 'r') as failas:
        for eilutė in failas:
            farenheitai = int(eilutė)
            celsijai = paversk_farenheitus_į_celsijus(farenheitai)
            viso_celsijų += celsijai
            dienų_skaičius += 1

    vidutinė_temperatūra = viso_celsijų / dienų_skaičius
    return vidutinė_temperatūra

if __name__ == "__main__":
    duomenų_failas = "temperatūros.txt"
    vidutinė_temperatūra = skaičiuok_vidutinę_temperatūrą(duomenų_failas)
    print(f"Vidutinė temperatūra yra: {vidutinė_temperatūra}")

Itin sudėtingų problemų automatizavimas

Egzistuoja daug itin sudėtingų problemų, kurių automatizavimui reikalingi superkompiuteriai. Štai keletas pavyzdžių:

  1. Orų prognozė ir klimato modeliavimas. Orų prognozės modeliai naudoja didelius duomenų kiekius, gaunamus iš satelitų, radarų ir kitų šaltinių. Superkompiuteris gali apdoroti šiuos duomenis realiu laiku. Tam naudojami sudėtingi algoritmai simuliuojantys atmosferos dinamiką, vandenynų sroves ir kitus atmosferos reiškinius. Tai leidžia gauti greitesnes ir tikslesnes orų prognozes, bei pasiruošti galimoms stichinėms nelaimėms.
  2. Vaistų kūrimas. Superkompiuteris atlieka automatizuotas simuliacijas ir virtualius eksperimentus su modeliuojamais vaistais. Analizuojamos molekulinės struktūros ir jų tarpusavio sąveika. Taip nustatomos potencialios naujos vaistinės medžiagos ir įvertinamas jų naudingumas.
  3. Transporto ir logistikos optimizacija. Superkompiuteriu optimizuojami transporto ir logistikos tinklai. Aviacijoje tai leidžia optimizuoti skrydžių maršrutus ir taip taupyti degalus, bei trumpinti kelionės laiką. Krovinių pervežime tai padeda optimizuoti maršrutus, efektyviai paskirstyti krovinius ir minimizuoti kaštus.

Uždaviniai, kuriuos sudėtinga automatizuoti

Ne visų problemų sprendimus įmanoma lengvai automatizuoti. Štai du pavyzdžiai:

  1. Matematinių teoremų įrodymas. Įrodyti teoremai gali reikėti patikrinti be galo daug įvairių galimybių ir atvejų. Dažnai to neįmanoma padaryti automatizacijos būdu.
  2. Sveikųjų skaičių faktorizavimas. Faktorizavimo tikslas rasti duoto skaičiaus pirminius daugiklius. Pirminių skaičių faktorizavimu yra paremtos šiuolaikinės kriptografinės sistemos, pavyzdžiui viešojo rakto kriptosistema RSA. Nors egzistuoja skaičių faktorizavimo algoritmai, tačiau faktorizuoti itin didelius skaičius, naudojamus kriptografijoje net ir galingais kompiuteriais užtrunka labai ilgai. Pavyzdžiui RSA dažnai galima sutikti raktus, kurie yra 2048 bitų ilgio sveikasis skaičius. Faktorizuoti tokį skaičių net ir galingu superkompiuteriu, gali užtrukti tūkstančius metų.

Skaičiavimo laikas

Viena svarbiausių metrikų programavime yra programos vykdymo laikas. Vykdymo laiką tiesiogiai įtakoja naudojamų skaičiavimų sudėtingumas, duomenų kiekis, bei naudojamo kompiuterio galia.

Skaičiavimo laiko nustatymas

Nori sužinoti kiek ilgai užtruko įvykdyti tavo programą? Pitone tą gali padaryti keletu būdų. Pateikiu vieną iš jų:

# Importuojame paketą, leidžiantį dirbti su laiku
import time

# Pasiimimame algortimo vykdymo pradžios laiką
pradžios_laikas = time.time()

# Čia įrašome kodą, kurio vykdymo trukmę norime matuoti

# Pasiimame vykdymo pabaigos laiką
pabaigos_laikas = time.time()

# Paskaičiuojame vykdymo pradžios ir pabaigos laikus. Tai ir bus mūsų vykdymo laikas 
vykdymo_laikas = pabaigos_laikas - pradžios_laikas
print(f"Vykdymo laikas sekundėmis: {vykdymo_laikas:.6f}")

Pasirinkto algoritmo įtaka skaičiavimo laikui

Dažnai tą patį uždavinį galima spręsti ne vienu skirtingu būdu. Sakykime mums reikia suskaičiuoti visų skaičių nuo 1 iki N sumą. Tą padarysime dviem skirtingais būdais ir pamatuosime vykdymo laikus:

# Importuojame paketą, leidžiantį dirbti su laiku
import time

def suma(N):
    rezultatas = 0
    # Skaičiuojame rezultatą tiesiogiai sudėdami visus skaičius
    for i in range(1, N + 1):
        rezultatas += i
    return rezultatas


print ("Įvesk N")
N = int(input())

# Pasiimame algoritmo vykdymo pradžios laiką
pradžios_laikas = time.time()

print("Suma:", suma(N))

# Pasiimame vykdymo pabaigos laiką
pabaigos_laikas = time.time()

# Paskaičiuojame vykdymo pradžios ir pabaigos laikus. Tai ir bus mūsų vykdymo laikas 
vykdymo_laikas = pabaigos_laikas - pradžios_laikas
print(f"Vykdymo laikas sekundėmis: {vykdymo_laikas:.6f}")

Įvykdžiau šį kodą savo kompiuteryje, su N=100000000 (100 milijonų), vykdymo trukmė buvo 11,6 sekundės

# Importuojame paketą, leidžiantį dirbti su laiku
import time

def suma(N):
    # Pasinaudojame matematine formule
    return N * (N + 1) // 2


print ("Įvesk N")
N = int(input())

# Pasiimame algoritmo vykdymo pradžios laiką
pradžios_laikas = time.time()

print("Suma:", suma(N))

# Pasiimame vykdymo pabaigos laiką
pabaigos_laikas = time.time()

# Paskaičiuojame vykdymo pradžios ir pabaigos laikus. Tai ir bus mūsų vykdymo laikas 
vykdymo_laikas = pabaigos_laikas - pradžios_laikas
print(f"Vykdymo laikas sekundėmis: {vykdymo_laikas:.6f}")

Dabar su N=100000000, vykdymo laikas tebuvo 0,001132 sekundės. Gali išbandyti abu kodo atvejus. Pamatysi, jog jų atsakymai bus vienodi, o vykdymo laikas skirsis daug kartų. Nors vykdymo greitis priklausys nuo konkretaus kompiuterio ir gali gauti kitokią trukmę, nei gavau aš, bet antrasis algoritmas visada bus greitesnis, nepriklausomai nuo naudojamo kompiuterio.

Atmintis

Tam, kad galėtų įvykdyti tavo parašytą programą, kompiuteris turės naudoti operacinę atmintį laikyti vykdomam kodui, bei kintamiesiems. Pitonas turi automatinę atminties valdymo sistemą. Ši sistema pasirūpina, kad būtų išskirta reikiama atmintis, programai vykdyti ir kad ta atmintis būtų atlaisvinta, kai programai jos nebereikia. Kai sukuriame naują kintamąjį ar objektą, pitonas išskirs reikiamą atminties kiekį reikšmei saugoti. Kai kintamasis taps nebereikalingas ta atmintis bus atlaisvinta. Tai padeda programuotojui susikoncentruoti į efektyvaus algoritmo kūrimą ir nesukti galvos dėl atminties atlaisvinimo.

Kiek atminties užima vienas kintamasis?

Patikrinti kiek atminties užima kintamasis, galima naudojant sys.getsizeof. Štai kodo pavyzdys:

#Importuojame paketą, leidžiantį dirbti su atmintimi
import sys
skaičius = 10
print("Kintamojo užimama atmintis baitais:", sys.getsizeof(skaičius))

Paleidęs kodą pamatysi, jog šis kintamasis užima 28 baitus. Taigi, sveikiesiems skaičiams saugoti pitonui reikės 28 baitų, bet labai dideliems skaičiams saugoti 28 baitų gali ir nepakakti. Pavyzdžiui: skaičiui 1234567890 saugoti pitonas jau naudos 32 baitus. Kitokio tipo kintamiesiems naudojamos atminties kiekis bus kitoks. Štai realiajam skaičiui saugoti reikės 24 baitų, o tekstui reikiamos atminties kiekis priklausys nuo teksto ilgio.

Dabartinių kompiuterių atmintis pakankamai didelė, tad sprendžiant šioje knygoje esančius uždavinius, greičiausiai neteks susidurti su atminties trūkumu. Tačiau sprendžiant dideles ir sudėtingas problemas atminties gali imti ir pristigti. Tad rašant programas svarbu pagalvoti kaip efektyviai naudoti atmintį.

Pasirinkto algoritmo įtaka naudojamos atminties kiekiui

Ankstesniame pavyzdyje jau matei, kad du skirtingi algoritmai gali išspręsti tą patį uždavinį, o jų veikimo greitis gali smarkiai skirtis. Taip pat ir su atmintimi. Gali būti skirtingų algoritmų, kuriems vykdyti reikės skirtingų atminties kiekių. Paimkime paprastą programą, kuri sudeda keturis skaičius 2 skirtingais būdais.

def suma1 (sk1, sk2, sk3, sk4):
    suma = sk1 + sk2 + sk3 + sk4
    return suma

def suma2 (sk1, sk2, sk3, sk4):
    tarpinė_suma1 = sk1
    tarpinė_suma2 = tarpinė_suma1 + sk2
    tarpinė_suma3 = tarpinė_suma2 + sk3
    suma = tarpinė_suma3 + sk4
    return suma

Abi funkcijos gražins tą patį rezultatą. Tačiau suma1 naudoja tik 1 kintamąjį, o suma2 naudoja net 4, tad ir atminties reikės net 4 kartus daugiau.

Atmintis, greitis ir kodo aiškumas

Kiekvienas programuotojas turbūt norėtų, kad jo parašytas kodas veiktų kiek galima greičiau, naudotų kiek galima mažiau atminties, būtų lengvai skaitomas ir keičiamas. Bet sprendžiant realius uždavinius to pasiekti gali būti neįmanoma. Gali tekti rinktis ar pasirinkti greitesnį algoritmą, kuris naudoja daugiau atminties ar tą kuris lėtesnis, bet taupoma atmintis. Bet gali būti ir taip kad itin greitas ar mažai atminties naudojantis algoritmas bus labai sudėtingas ir sunkiai suprantamas, o norint padaryti pakeitimus reikės labai daug programuotojo pastangų. Tuomet prioritetas gali būti teikimas ir kodo aiškumui. Tai kam skirti prioritetą: greičiui, atminčiai ar aiškumui? Tai priklauso nuo konkretaus uždavinio ir reikalavimų. Mokantis pitono rekomenduoju pirmiausia rašyti tvarkingą ir aiškų kodą. Bet visada pagalvok, ar gali kodą parašyt optimalesnį atminties ar greičio atžvilgiu.

Problemų formuluotės

Neaiškios formuluotės

Ar galėtum teisingai išspręsti tokius uždavinius su pitonu?:

1. Įvedus skaičių, programa turi atspausdinti "Didelis", jei skaičius didelis, arba "Mažas" - jei skaičius mažas.
2. Programa turi mokėti suskaičiuoti užrašytą sąlygą. Pavyzdžiui užrašius "2+3" turi būti atspausdinta 5.

Pirmu atveju gali parašyti tokį kodą:

print ("Įvesk skaičių?")
skaičius = int(input())
if skaičius > 10:
   print("Didelis")
else:
   print ("Mažas")

Ar žmogus pateikęs užduotį būtų patenkintas tokiu sprendimu? Sunku pasakyti, nes mes nežinome kas jo manymu yra didelis skaičius. Kaip turėtų būti suformuluota tokio uždavinio sąlygą, kad tokių neaiškumų nebeliktų? Ar tiktų toks formulavimas?:

Įvedus skaičių programa turi atspausdinti "Didelis", jei skaičius didesnis už 100, arba "Mažas" jei skaičius mažesnis už 100.

Nors uždavinio formuluotė jau aiškesnė, tačiau vis dar nėra pilnai aiški. Kas atsitiktų jei būtų įvestas skaičius 100? Užduoties formuluotė to nepaaiškina. Tad reikėtų ją dar patobulinti. Aiški sąlyga galėtų skambėti šitaip:

Įvedus skaičių programa turi atspausdinti "Didelis", jei skaičius didesnis arba lygus 100, arba "Mažas" jei skaičius mažesnis už 100.

Kaip spręstum antrąjį uždavinį? Galbūt parašytum programą kuri mokėtų sudėti daug skaičių? Bet ar to užtenka? Pavyzdyje nurodyta sudėtis, bet sąlygoje nėra parašyta, jog turi būti skaičiuojam tik sudėtis. Galbūt sąlygos autorius tikisi ir kitų veiksmų. Mes to nežinome, nes sąlyga nėra pakankamai aiški. Gerai suformuluota sąlyga galėtų skambėti šitaip:

Programa turi mokėti suskaičiuoti užrašytą sąlygą. Sąlygoje gali būti naudojamos sudėties, atimties, daugybos ir dalybos veiksmai, gali būti naudojami skliaustai. Pavyzdžiui užrašius "2+3" turi būti atspausdinta 5.

Perteklinės formuluotės

Gera problemos formuluotė neturėtų būti perteklinė. Perteklinė formuluotė nepadeda rasti geresnio sprendimo, bet gali netgi trukdyti įsigilinti į problemą. Pavyzdžiui:

Parašyk programą, kuri skaičiuotų stačiakampio plotą. Programa turi leisti įvesti stačiakampio ilgį ir plotį, o išvesti jo plotą. Formulės darbui su stačiakampiais:
Perimetras: 2 * (ilgis + plotis)
Plotas: ilgis * plotis

Nors šiame pavyzdyje prašoma suskaičiuoti plotą, bet taip pat duota ir perimetro formulė, kuri mums nereikalinga, šiai problemai išspręsti. Tai bereikalingai pailgina sąlygą ir gali sukelti papildomų klausimų kodėl ši formulė čia pateikta.