Az előadás letöltése folymat van. Kérjük, várjon

Az előadás letöltése folymat van. Kérjük, várjon

AAO Csink László 2010. ősz.

Hasonló előadás


Az előadások a következő témára: "AAO Csink László 2010. ősz."— Előadás másolata:

1 AAO Csink László 2010. ősz

2 Adminisztráció Előadók:
Csink László A tárgy weblapja: Login: aao Jelszó: algorithm Az objektumorientált programozás 5 előadásból áll, melyet Miklós Árpád tart

3 A tárgy célja Ismerkedés az algoritmus fogalmával, egyszerű „klasszikus” algoritmusokkal Ismerkedés az algoritmusok implementálásához (megvalósításához) szükséges legalapvetőbb adatstruktúrákkal Bevezetés az objektumorientált programozási paradigma alapjaiba Algoritmuskészítési készség kialakítása AAO= Algoritmusok, Adatstruktúrák, Objektumok Mindez az OOP tárggyal való szoros együttműködésben valósul meg

4 Irodalom Könyvek, amibikből lehet meríteni:
Cormen, Leiserson, Rivest: Algoritmusok (újabb kiadás is, valamint Informatikai algoritmusok) Trahtenbrot: Algoritmusok és absztrakt automaták Kotsis et al: Többnyelvű programozástechnika Lovász L., Gács P.: Algoritmusok Referencia linkek: Matematikai háttér Lexikon: Ajánlott feladatgyűjtemény (PPKE) (A Kotsis et al könyv mellett):

5 Hogyan készüljenek? Sajnos, igen nagy szokott lenni a bukási arány!
A diák csak vázlatot jelentenek, a vetített anyag nem tankönyv! Sokakat megtéveszt, hogy az előadásokon nincs névsor ellenőrzés. Ennek ellenére folyamatosan kell tanulni már a félév elejétől, sok gyakorlatra van szükség, a tudásnak „érlelődnie” is kell Az előadásokon tárgyalt algoritmusokat célszerű C#-ban otthon megírni A Kotsis könyvben található algoritmusokat elolvasni, megérteni, megpróbálni segítség nélkül megírni A feladatgyűjtemény feladataira a programokat megírni Az OOP gyakorlatokon tanultakat hasznosítani

6 Algoritmus fogalma Az algoritmus egy eljárás (jóldefiniált utasítások véges halmaza), amelyet valamely feladat megoldására készítünk. A feladat egy adott kezdeti állapotból egy meghatározott végállapotba jut (szakszóval terminál). Az algoritmus kiszámítási bonyolultsága és hatékony implementációja az alkalmazott adatstruktúrától függ.

7 Informális és formális definíció
Az algoritmus tipikusan kisebb-nagyobb alkotórészekből épül fel. Az egyszerű algoritmus egy ételrecepthez hasonlítható. A formális definíció Alan Turing (Turing gép, 1936) és Alonzo Church (lambda kalkulus) matematikusokhoz köthető. (lásd Haladó algoritmusok tárgy a későbbiekben)

8 Informális megközelítés: ételrecept
Informálisan – hétköznapi szavakkal – egy egyszerű algoritmus egy receptnek tekinthető. A recept tartalmazza az adott étel nevét; elkészítésének idejét; a szükséges alkotórészek nevét és mennyiségét; az elkészítéshez szükséges eszközöket illetve környezetet (például mikrosütő); az elkészítéshez szükséges eljárásokat meghatározott sorrendben; az elkészült adagok számát; a kalóriatartalmat egy adagra vetítve; az étel eltarthatóságának idejét.

9 A másodfokú egyenlet „receptje” 1.
Készítsünk algoritmust az ax2+bx+c=0 egyenlet megoldására. 1. lépés: az input adatok meghatározása Jelen esetben: a, b, c valós számok 2. lépés: output adat(ok) meghatározása: egy x valós szám (egy megoldás, illetve ha a két gyök egybeesik), x1 és x2 valós számok (két különböző megoldás), illetve a „nincs valós megoldás” szöveg.

10 A megoldás menete Úgy fogjuk a megoldás menetét prezentálni, ahogy tervezés közben „eszünkbe jut” Óhatatlan, hogy menet közben lesznek még hibák, mert nem pattan ki a tökéletes megoldás az agyunkból egyszerre Minden lépés után elemezzük, hogy jól működik-e a programunk, és ha nem tökéletes, akkor javítunk rajta

11 A feladat „receptje”: a hozzávalók
A „recept” adatai: Név: MASODFOKU elkészítésének ideje (és szerző): CsL a feltétlenül szükséges adatok neve és típusa: a,b,c, 3 valós szám (INPUT) x valós szám, VAGY x1 és x2 valós számok, VAGY ” nincs valós megoldás” szöveg (OUTPUT) az elkészítéshez szükséges eszközök illetve környezet: Turbo C (természetesen más is lehetne, például C#) Windows XP operációs rendszer alatt (ez is lehetne más) az elkészítéshez szükséges eljárások: gyökvonás, abszolút érték (ezek elkészítettnek tekintett programok) Megjegyzés: Az OOP gyakorlatokon Visual Studio C# környezetben fogunk dolgozni. (A Turbo C++ előnye, hogy ingyenes)

12 A feladat pontosítása Hasznos dolog egy konkrét példát megnézni: 4x2-12x+8=0 A megoldás x1=2; x2=1 Erre a feladatra ez a két értékadás tulajdonképpen egy program. Ez a program azonban nem ad megoldást a 3x2+4x-2.11=0 feladatra. Azt szeretnénk, hogy ugyanaz a a program erre a feladatra is megoldást adjon. Mi a helyzet az 12x+8=0 egyenlettel? Ez már elsőfokú. El kell dönteni, hogy eredetileg a másodfokú, vagy a legfeljebb másodfokú egyenlet megoldását tűztük-e ki célul? Megállapítjuk, ezen példa kapcsán, hogy a feladat pontos mibenléte gyakran a megoldás során tisztázódik. A feladat specifikációja tehát finomodhat a megoldás közben.

13 A specifikáció Oldjuk meg az ax2+bx+c=0 egyenletet minden a, b, c valós szám esetén. Nevezzük a {ax2+bx+c=0 | a, b, c valós számok} problémaosztálynak, melynek a megoldását keressük. Lehetett volna a problémaosztályt {ax2+bx+c=0 | a, b, c valós számok és a≠0} halmaznak választani, ekkor a megoldási algoritmus kicsit egyszerűbb, de kevésbé általános.

14 Létezik-e megoldás? Tudjuk (például mert érettségiztünk ), hogy van megoldóképlet: Ha komplex megoldást nem engedünk meg – azaz a specifikációt úgy finomítjuk tovább, hogy valós x megoldásokat keresünk – akkor nincs megoldás.

15 Tanulságok Hajlamosak vagyunk azt gondolni, hogy mindig létezik valamilyen megoldás, talán azért, mert hozzászoktunk a megoldható feladatokhoz a középiskolában. Vannak azonban algoritmussal nem megoldható problémák. Nem oldható meg például a szögharmadolási probléma: tetszőleges szög harmadának megszerkesztése. Egy probléma algoritmussal való megoldhatóságának kérdése igen mély dolog, lásd incompleteness theorem a wikipedia-ban. (E tételt felállító Kurt Gödelről az az anekdota szól, hogy Einstein csak azért járt be Princetonban az egyetemre, hogy Gödellel sétálhasson haza és beszélgethessenek )

16 Első nekifutás VÁLTOZÓK gy, a,b,c VALÓS SZÁM BEOLVAS(a,b,c)
gy mint egész nem jó, pl. b=1.1., a=1, c=0.2 VÁLTOZÓK gy, a,b,c VALÓS SZÁM BEOLVAS(a,b,c) gy ← (b*b-4*a*c) HA gy ≥0 AKKOR gy ← GYOKVONAS(b*b-4*a*c) EGYÉBKÉNT KIIR(nincs valós megoldás) Itt folytatódna a megoldás, de csak akkor, ha a gyökvonás lehetséges volt. Mivel a gy<0 esetben a KIIR végrehajtása után nem akarjuk, hogy a program folytatódjon, ezért trükkösen megcseréljük a feltételt a HA mögött. értékadás

17 Folytatjuk VÁLTOZÓK gy,a,b,c, x1 VALÓS SZÁM BEOLVAS(a,b,c)
6., 7., 8. sorok egybetartozásának jelölése (a zárójelek párban vannak!) VÁLTOZÓK gy,a,b,c, x1 VALÓS SZÁM BEOLVAS(a,b,c) gy ← (b*b-4*a*c) HA gy <0 AKKOR KIIR(nincs valós megoldás) EGYÉBKÉNT{ gy ← GYOKVONAS(b*b-4*a*c) HA gy=0 AKKOR x1= -b/(2*a) EGYÉBKÉNT folytatni kell a dolgot } Piros színnel jelezzük a változásokat az előzőekhez képest.

18 Tovább folytatjuk (EZ MÉG NEM VÉGLEGES!
VÁLTOZÓK gy,a,b,c, x1,x2 VALÓS SZÁM ag EGÉSZ SZÁM BEOLVAS(a,b,c) gy ← (b*b-4*a*c) HA gy <0 AKKOR ag=1 EGYÉBKÉNT{ ag=2 gy ← GYOKVONAS(b*b-4*a*c) HA gy=0 AKKOR x1= -b/(2*a) EGYÉBKÉNT{ ag=3 x1=-b+gy/(2a) x2=-b-gy/(2a) } ESETEK ag 1 : KIIR(nincs valós megoldás) 2 : KIIR(egy megoldás van: , x1) 3 : KIIR(két megoldás van:, x1, x2) ESETEK VEGE

19 Javítások, megjegyzések
Kellenek további zárójelek: x1=(-b+gy)/(2a) x2=(-b-gy)/(2a) Feltételezzük, hogy a KIIR argumentumában ha magyar mondat van, akkor a szöveg íródik ki, ha változónév van, akkor a változó értéke. Azt is feltesszük, hogy a BEOLVAS a megfelelő változókba a billentyűzetről megadott adatokat elhelyezi. A BEOLVAS és a KIIR megvalósításával nem „bíbelődünk”. Mi történik, ha a 7., vagy a 9. vagy a 10. sorban a=0 eset áll elő? Ekkor nullával való osztásra utaló hibaüzenet generálódik. Az ilyen esetek kezelésére a legtöbb nyelvben vannak eszközök (ezzel most nem foglalkozunk), hanem megpróbáljuk „előre látni”, hogy ilyen eset előfordulhat.

20 a=0 eset HA a=0 AKKOR Megoldjuk a bx+c=0 egyenletet EGYÉBKÉNT
Amit eddig csináltunk (a≠0, b,c tetszőleges) Kész programblokk Megírandó programblokk A lépésenkénti finomítás módszerét alkalmazuk, pszeudokód segítségével.

21 A nyilak jelzik a megfelelő színű zárójelpárokat, azaz hogy melyik AKKOR és EGYÉBKÉNT melyik HA-hoz tartozik. Az alapértelmezés pont olyan, hogy ezek a zárójelek elhagyhatók. VÁLTOZÓK gy,a,b,c, x1,x2 VALÓS SZÁM BEOLVAS(a,b,c) HA a=0 AKKOR { HA b=0 AKKOR HA c=0 AKKOR KIIR(minden valós szám megoldás) EGYÉBKÉNT KIIR(nincs megoldás)} } EGYÉBKÉNT x1=-c/b EGYÉBKÉNT Amit eddig csináltunk (a≠0, b,c tetszőleges)

22 A program (algoritmus) összerakása
PROGRAM MASODFOKU VÁLTOZÓK gy,a,b,c, x1,x2 VALÓS SZÁM ag EGÉSZ SZÁM BEOLVAS(a,b,c) HA a=0 AKKOR HA b=0 AKKOR HA c=0 AKKOR ag=4 EGYÉBKÉNT ag=5 EGYÉBKÉNT{ ag=6; x1=-c/b} EGYÉBKÉNT{ gy ← (b*b-4*a*c) HA gy <0 AKKOR ag=1 EGYÉBKÉNT{ ag=2 gy ← GYOKVONAS(b*b-4*a*c) HA gy=0 AKKOR x1= -b/(2*a) EGYÉBKÉNT{ ag=3 x1=(-b+gy)/(2a) x2=(-b-gy)/(2a) } ESETEK ag 1 : KIIR(nincs valós megoldás) 2 : KIIR(egy megoldás van: , x1) 3 : KIIR(két megoldás van:, x1, x2)) 4 : KIIR(minden valós szám megoldás) 5 : KIIR(nincs megoldás) 6 : KIIR(elfajulo egyenlet, egy megoldás:, x1) ESETEK VEGE PROGRAM VÉGE

23 Közelítő gyökvonás (Newton módszer)
Legyen A>0, emelyből gyököt szeretnénk vonni, és gy1 tetszőleges, például 1. Ekkor elég nagy n-re gyn közel van egy számhoz, amely A négyzetgyöke. Más szóval a gyn sorozat konvergens és határértéke A négyzetgyöke. Ilyenkor elég nagy n-re gyn és gyn+1 egy tetszőlegesen előre megadott számnál is közelebb kerülnek egymáshoz. A következőkben gyn helyett gy-t, gyn+1 helyett ujgy-t írunk és iteratíven számolunk.

24 A gyökvonás programja // inp adott szám, melyből gyököt akarunk vonni
VÁLTOZÓK inp, ujgy, gy, d, eps DUPLAPONTOS BEOLVAS(inp) gy = 1.0 d=1.0 eps= ISMETELNI HA( d > eps){ ujgy = (1.0/2)*(gy + inp/gy) d = | ujgy – gy | gy = ujgy } KIIR(gy) Abszolút érték A piros rész ismétlődik mindaddig, amíg a sárga rész teljesül. Figyeljük meg, hogy d menetközben változik!

25 6 négyzetgyökének közelítő számítása
ujgy d (=|ujgy-gy|) 1 3.5 2.5 2,454256 2,449494 e-06

26 Vigyázat! A fentiek csak azért működtek jól, mert tudtuk (matematikából), hogy gyn konvergens. Ha most akkor ugyan sn+1-sn bármilyen kicsi lehet, sn mégsem konvergens! Tanulság: egy program használhatóságához szükséges, hogy az elméleti megalapozás korrekt legyen! Anélkül, hogy a gyn konvergenciáját bebizonyítjuk, vagy legalább hivatkozunk a megfelelő szakirodalomra, a fenti program nem ad megoldást, mert nem tudhatjuk, hogy véget ér-e a program, és ha igen, korrekt eredményt ad-e?

27 Példa: Hogyan keresünk algoritmust egy feladatra igazi alkalmazásoknál?
A napi gyakorlatban –ahelyett, hogy magunk készítünk algoritmust – sokszor inkább körülnézünk. Pontosabban: mindig körülnézünk, hogy van-e megoldás a feladatunkra, vagy nekünk kell nekifognunk megcsinálni (kivétel persze a zh vagy vizsga ) Nézzünk egy példát: tegyük fel, hogy egy szám prím vagy összetett voltát akarjuk megállapítani. Keresünk egy algoritmus gyűjteményt, például a MathWorld weblapját (ld. referencia linkek): Mivel jól tudunk angolul , könnyedén elnavigálunk az adott területre: Number Theory, Prime Numbers, Primality Testing.

28 ERATHOSTENESI SZITA „An algorithm for making tables of primes. Sequentially write down the integers from 2 to the highest number you wish to include in the table. Cross out all numbers which are divisible by 2 (every second number). Find the smallest remaining number . It is 3. So cross out all numbers which are divisible by 3 (every third number). Find the smallest remaining number . It is 5. So cross out all numbers which are divisible by 5 (every fifth number). Continue until you have crossed out all numbers divisible by The numbers remaining are prime. „ Forrás: MathWorld

29 Akkor miért nem csináljuk mindig így?
A feladat pontos megértéséhez gyakran el kell kezdeni megoldani a feladatot. Nem mindig van tehát elegendő információnk a feladatmegoldás kezdetén ahhoz, hogy algoritmus adatbázisokat vagy szakirodalmat használjunk. Ahhoz, hogy a megfelelő megoldási változatot (paraméterek, sebesség, környezet stb.) kiválasszuk, algoritmuselmélet ismeretekre – és implementációs gyakorlatra – van szükségünk, melyet például ez a tárgy is adhat. Lehet, hogy az adott feladatra nem találunk az igényeinket kielégítő megoldást. Lehet, hogy a talált megoldásnál jobb jut eszünkbe (hiszen az irodalomban és a weben publikált megoldásokat is kitalálta valaki valamikor).

30 Egyszerű programozási tételek
Sorozatszámítás Eldöntés Kiválasztás Keresés Megszámolás Maximumkeresés

31 Sorozatszámítás A múlt évben minden hónapban eltettem a gázszámlát. Szeretném kiszámolni, hogy mennyi pénzbe került az éves gázfogyasztás. A megoldás lépései: Lenullázok egy gyűjtőváltozót. A következő két lépést 12-szer ismételem: Megfogom a soron következő számlát. Hozzáadom az előző összeghez. Megvan az összeg.

32 Pszeudokód sum ← sum + szamla[i]; i ← i+1; } KIIR(sum)
VÁLTOZÓK i, sum EGÉSZ, szamla[i] EGÉSZ) i ← 0; sum ← 0; ISMÉTELNI HA( i kisebb mint 12){ sum ← sum + szamla[i]; i ← i+1; } KIIR(sum) Január a 0.-dik, február az 1., …, december a 11. hónap a jelölés szerint

33 Eldöntés Egy tanuló érdemjegyei alapján szeretném eldönteni, hogy kitűnő-e, vagy sem. Kétféle ötlet is lehet: Ha a jegyei közt van olyan, ami nem ötös, akkor nem kitűnő. Ha minden jegye ötös, akkor kitűnő. Vizsgáljuk meg először az első ötletet közelebbről! Nézzük végig a jegyeket, először az elsőt, majd sorra a többit, és ellenőrizzűk, hogy ötös-e. Ha találtunk olyat, ami nem ötös, akkor nem kell megnézni a további jegyeket, mert van nem ötös osztályzat, azaz nem kitűnő.

34 Az 1. számú pszeudokód VÁLTOZÓK tantárgy_szám, i, jegyek[i] EGÉSZ,
van_nemotos LOGIKAI i ← 1     ISMÉTELD HA(i ≤ tantárgy_szám) és (jegyek[i] egyenlő 5-tel){ i ← i+1 } van_nemotos ←(i ≤ tantárgy_szám) HA (van_nemotos) AKKOR KIIR(nem kitűnő) EGYÉBKÉNT KIIR(kitűnő) Végignézem a jegyeket. Az elsővel kezdem. ellenőrizzük a többit is, hogy ötös-e.

35 Második ötlet Nézzük végig a jegyeket, először az elsőt,majd sorra a többit, és ellenőrizzük, hogy ötös-e. Ha a tömb minden elemét megvizsgáltuk, akkor minden érdemjegy ötös, azaz kitűnő. VÁLTOZÓK tantárgy_szám, i, jegyek[i] EGÉSZ, mind_otos LOGIKAI i ← 1     ISMÉTELD HA(i ≤ tantárgy_szám) és (jegyek[i] egyenlő 5) { i ← i } mind_otos ←(i > tantárgy_szám)   HA(mind_otos) AKKOR KIIR(kitűnő) EGYÉBKÉNT KIIR(nem kitűnő)

36 Kiválasztás Egy tankör zárthelyi dolgozatai közül válasszuk ki az egyik elégséges dolgozatot. Megoldás: nézzük végig a dolgozatokat, először az elsőt, majd sorra a többit, amíg nem találunk elégséges dolgozatot. Amikor megtaláltunk egy elégségest, akkor ő lesz a kiválasztott.

37 Pszeudokód VÁLTOZÓK i, sorsz, dolg_szama, dolgozatok[i] EGÉSZ i ← 1
ISMÉTELD HA ( (i ≤ dolg_szama) és (dolgozatok[i] ≠ 2) ) { i ← i + 1 } sorsz ← i    // Ha találtunk elégségest, az lesz a megoldás. HA(i <= dolg_szama) AKKOR KIIR(sorsz); EGYÉBKÉNT KIIR ("nincs elégséges") Hasonlít az eldöntéshez, de más a konklúzió!

38 Keresés Ismerjük egy üzlet januári napi bevételeit. Adjunk meg egy olyan napot -ha van-, amikor a bevétel több volt, mint 20,000 Ft. Megoldás: Nézzük végig a bevételeket, először az elsőt, majd sorra a többit, amíg nem találunk 20,000 Ft-nál nagyobbat. Ha találtunk ilyet, akkor van megoldás, és a megoldás a megtalált bevétel sorszáma különben nincs megoldás

39 Input adatok generálása
Töltsük fel véletlen egészekkel a január tömb elemeit. (Ezt most még nem kell megérteni, a félév végére világos lesz.) int[] januar = new int [32]; // 0-tól indul, a 32. lesz jan. 31 ! Random RandomClass = new Random(); for(int i = 1;i <=31; i++) januar[i] = RandomClass.Next(10,30); …. Január 1, 2, 3, 4, 5, …. Bevételei rendre ezer forintban, akkor január 3.-át kell megtalálnunk. Persze az is lehet, hogy a bevétel egyetlen napon sem haladja meg a 20 ezer forintot. A for ciklust fogjuk részletesen taglalni

40 A kód (a while ciklust fogjuk részletesen taglalni)
int i = 1; while (januar[i] < 20) i++; Console.Write(januar[i] + " "); Ez akkor nem jó, ha nincs megoldás, azaz jan. 31.-én (i=32) Is a bevétel kisebb, mint 20. Mitől állna meg a ciklus? Ha a napokszama == 31, akkor: i = 1; while ( (i<=napokszama) && (januar[i] <= 20)) i++; if (i > napokszama) Console.WriteLine("NINCS megoldas"); else Console.Write(i + "dik nap! ");

41 Megszámolás Az előző feladatot kicsit módosítjuk. Számoljuk meg, hány napon (ha egyáltalán) volt az üzlet bevétele 20 ezer forintnál nagyobb?

42 Megszámolás kódja int darab = 0; for (i = 1; i <= napokszama; i++)
if (januar[i] > 20) darab++; Console.WriteLine("a nagyobb bevételek száma: " +darab);

43 Maximum-kiválasztás Tegnap este a térképet nézegettem. Kiírtam magamnak 20 magas hegy tengerszint feletti magasságát. Adjuk meg az egyik legmagasabb csúcsot! Megoldás: Megjegyzem az első hegy magasságát. Ezt tekintem a legmagasabbnak. A többi hegyet sorra végignézem: Ha valamelyik magasabb, mint az eddigi legmagasabb, akkor az eddigi legmagasabbat elfelejtem, és az újat jegyzem meg. A végén pont az első legmagasabb hegyet jegyeztük meg.

44 Maximum-kiválasztás pszeudokódja 1
int hegyekszama = 10; int[] magas = new int [hegyekszama+1]; // mert 1-től indexelünk int i; Random RandomClass = new Random(); for (i = 1; i <= hegyekszama; i++){ magas[i] = RandomClass.Next(10, 26); Console.Write(magas[i]+" "); }

45 Maximum-kiválasztás pszeudokódja 2
int max = magas[1]; int eddigi = 1; for (i = 1; i <= hegyekszama; i++) if (magas[i] > max) { max = magas[i]; eddigi = i; } Console.WriteLine("az (egyik) legmagasabb sorszáma: "+eddigi+" magassag "+max); // magas[] tartalmát 1-től kezeljük // Ha mondjuk a 2. hegy és a 7. hegy ugyanolyan magas, // és a többi mind kisebb, az eredmény 2 lesz vagy 7? // 2 lesz. Azonban ha > helyett >= kerül be, akkor 7.

46 Egyszerű változók (OOP gyakorlatokon részletezzük)
C# típus .Net Framework (System) típus Elő-jeles? Elfoglalt bájtok Lehetséges értékek sbyte System.Sbyte Igen 1 -128 to 127 short System.Int16 2 to 32767 int System.Int32 4 to long System.Int64 8 to byte System.Byte Nem 0 to 255 ushort System.Uint16 0 to 65535 uint System.UInt32 0 to ulong System.Uint64 0 to float System.Single Approximately ±1.5 x to ±3.4 x 1038 with 7 significant figures double System.Double Approximately ±5.0 x to ±1.7 x with 15 or 16 significant figures decimal System.Decimal 12 Approximately ±1.0 x to ±7.9 x 1028 with 28 or 29 significant figures char System.Char --- Any Unicode character (16 bit) bool System.Boolean 1 / 2 true or false

47 Példák int x = 10; // deklarálás kezdeti értékadással int y = x; y = 20; // x értéke 10 és y értéke 20 float v=2.34; double w;

48 Adatkonverziók A konverzió C# -ban lehet implicit vagy explicit. Ha az átalakítás automatikus, akkor implicit konverzióról beszélünk, ilyenkor nincs adatvesztés. Az explicit konverzió kikényszerített, ilyenkor előfordulhat adatvesztés. Konverzió leggyakrabban függvényhívás paraméter-átadásakor történik (később látunk majd példákat), vagy kevert típusú adatokkal való numerikus számítások esetén.

49 A nyilak mutatják az implicit konverziós lehetőségeket (referencia táblázat)

50 Implicit numerikus konverzió
long x1; int y1 = 25; x1 = y1; // implicit numerikus konverzió int ->long Console.WriteLine(x1); int x1; long y1 = 25; x1 = y1; // long -> int nyíl nincs, mi lesz ? // HIBAÜZENET: NEM LÉTEZŐ implicit konverzió! // OK: mert esetleges adatvesztés lehetséges!

51 Megoldás kasztolással (explicit)
int x1, x2; long y1 = , y2 = ; //y1 „belefér” int-be, y2 „nem fér bele” x1 = (int)y1; x2 = (int)y2; // (int) kasztolás Console.WriteLine(x1+" "+x2); // x1 = , x2 = -10 // x2 esetében adatvesztés történt

52 Egész-osztás vagy valós osztás?
int i = 13, j = 7; // mindkét argumentum eredetileg egész int k = i / j; Console.WriteLine(k); // KIIR: 1 float k1 = i / j; Console.WriteLine(k1); // KIIR: 1 float k2 = (float) i / j; Console.WriteLine(k2); // // (float) i / (float) j vagy i / (float) j is jó! Console.WriteLine( 55 / 7); // 7 Console.WriteLine(55.0 / 7); //

53 Osztás folytatása int j = 7; float i2 = 13,0f; f = i2 / j;
Console.WriteLine(f); // f == double i1 = 13.15; int j = 7; // i1 duplapontos, nem egészértékű Console.WriteLine(i1/j); // float i2 = 13.15; // double, nem tud floattá alakulni, HIBA! int j = 7; Console.WriteLine((int) i2 / j); float i2 = 13.15F; // 13.15F valós konstans int j = 7; Console.WriteLine((int) i2 / j); // 1-t fog kiírni

54 Duplapontos változó konvertálása egészre adatvesztéssel
double i2 = 13.83; Console.WriteLine( (int) i2 ); // 13 Példa logikai kifejezésre int a = 7; bool log; log = a % 2 == 0; // log = (a % 2 == 0); if (log) Console.WriteLine(" paros"); else Console.WriteLine("paratlan");

55 String -> numerikus (egész) érték
string sz = "123"; int sz1 = Int32.Parse(sz); // így is lehet int sz2 = Convert.ToInt32(sz); // vagy akár így is lehet

56 String -> numerikus (valós) érték
string myDoubleStr = "-12,2"; double x = Double.Parse(myDoubleStr) + 5.3; double y = Convert.ToDouble(myDoubleStr) +1.1; Console.WriteLine(x); // -6,9 Console.WriteLine(y); // -11,1 Érdemes a tizedespont illetve a vessző használatára figyelni. A stringek részletes tárgyalása az IMOP gyakorlatokon történik!!

57 Elöltesztelő és hátultesztelő ciklus 1.
Vissza int a = 0; while (a < 3) { Console.WriteLine(a); a++; } // a=0 indulással kiírja a 0, 1, 2 számokat int a = 0; do{ Console.WriteLine(a); a++; } while(a < 3); // a=0 indulással ugyanúgy, mint a zöld kód, kiírja a 0,1,2-t Nem olyan, mint a repeat Delphiben!

58 Elöltesztelő és hátultesztelő ciklus 2.
int a = 5; while (a < 3) { Console.WriteLine(a); a++; } // ha a=5 induláskor, akkor egyszer sem hajtódik végre int a = 5; do{ Console.WriteLine(a); a++; } while(a < 3); // ha induláskor a=5; akkor kiírja az 5-t, egyszer végrehajtódik, többször nem Nem olyan, mint a repeat Delphiben!

59 For ciklus for (int a = 0; a < 5; a++) // 0, 1, 2, 3, 4 kiírása
Console.WriteLine(a); // Nem hajtódik végre a ciklusmag a következő esetben: for (int a = 6; a < 5; a++) // 0, 2, 4 íródik ki a következő esetben (ámbár nem ajánlott // ciklusváltozót egyszerre két helyen változtatni!): for (int a = 0; a < 5; a++) Console.WriteLine(a++);

60 Függvények használata érték visszaadása nélkül
public static void change(int b) // void szerepe !! { b = 5; } static void Main() int a = 0; change(a); Console.WriteLine(a); // hívás után is a==0 marad

61 Egy érték visszaadása a metódus nevében
public static int change(int b) // int szerepe !! { b = 5; return b; } static void Main() int a = 0; int c; c = change(a); Console.WriteLine(c); // c==5 lesz

62 Paraméterátadás ref segítségével
public static void change(ref int b) { b = 5; } static void Main() int a = 0; // ha hívás előtt ‘a’ nem kapna értéket, az hiba! change(ref a); Console.WriteLine(a); // a==5

63 Paraméterátadás out segítségével
public static void change(out int b) { b = 5; } static void Main() int a; // = 0; nem muszáj, hogy ‘a’ értéket kapjon! change(out a); // a==5, még akkor is, ha az előző sorban a értéket kapna Console.WriteLine(a);

64 Egydimenziós tömb int[] itomb; // deklarálás
itomb = new int[3] { 0, 1, 2 }; // inicializálás for (int i = 0; i < itomb.Length; i++) Console.WriteLine(itomb[i]); int[] itomb = new int[3] { 0, 1, 2 }; // a deklarálás és az inicializálás össze is vonható

65 Egydimenziós tömb átlaga
public static double atlag(double[] vektor) { double s = 0.0; for (int i = 0; i < vektor.Length; i++) s += vektor[i]; return s/vektor.Length; } public static void Main(){ double[] tomb = new double[] { -4.11, 1.23, 2.14 }; Console.WriteLine(atlag(tomb)); // - 0,246.. Console.ReadKey(); // várakozás

66 Írjunk egy függvényt, amely nullára cseréli egy tömb ama elemeit, melyek abszolút értéke egy paraméternél nagyobb public static double atl_csere (ref double[] vektor, double eps) { double s = 0.0; for (int i = 0; i < vektor.Length; i++) s += vektor[i]; s = s/vektor.Length; // átlagszámítás for (int i = 0; i < vektor.Length; i++) // csere, ha kell if ((vektor[i] >eps) || (vektor[i] < - eps)) vektor[i] = 0; return s; // a „régi átlag” visszaadása }

67 …és a főprogram public static void Main() {
double[] tomb; double eps = 5.1; tomb = new double[] { -4.11, 1.23, }; Console.WriteLine(atl_csere(ref tomb,5)); for (int i = 0; i < tomb.Length; i++) Console.WriteLine( tomb[i]); // „új” tömb Console.ReadKey(); // várakozás } // a ref ebben az esetben mellőzhető! A tömbök tárgyalására visszatérünk!

68 Nem-egydimenziós tömbök
A nem-egydimenziós tömbökkel a későbbiekben részletesen fogunk foglalkozni!

69 Egy n elemű sorozat csupa 0-ból és 1-ből áll
Egy n elemű sorozat csupa 0-ból és 1-ből áll. Rendezzük n-1 összehasonlítással! int i; int[] b = { 0, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 1, 1, 0 }; for (i = 0; i < b.Length; i++) Console.Write(b[i] + " "); Console.WriteLine(); int n = b.Length; int[] b1 = new int[n]; // b1 segédtömb for (i = 0; i < b.Length; i++) b1[i] = 0; // kinullázzuk a segédtömböt for (i = 0; i <= b.Length - 1; i++) if (b[i] == 1) b1[--n] = 1; // fontos, hogy --n és nem n-- for (i = 0; i < b.Length; i++) Console.Write(b1[i] + " "); Console.ReadLine();

70 Egy n elemű sorozat csupa 0-ból és 1-ből áll
Egy n elemű sorozat csupa 0-ból és 1-ből áll. Rendezzük összehasonlítás nélkül! int szum = 0, i = 0; int[] b = { 0,0,1,0,1,1,0,0,1,0,1,0,0,1,1,0 }; // megszámoljuk, hány 1-es van for (i = 0; i < b.Length; i++) szum += b[i]; // b „elejét” kellő számú nullával feltöltjük for (i = 0; i < b.Length - szum; i++) b[i]=0; // b „végét” feltöltjük annyi 1-essel, amennyivel kell for (int j= i; j < b.Length; j++) b[j] = 1;

71 A két megoldás összehasonlítása
Az első változatban kellett egy n hosszú ciklus és n-1 összehasonlítás, valamint még egy tömb A második változatban nem kellett összehasonlítás és extra tömb

72 Geometriai feladat Számítsuk ki a sokszög kerületét és területét!
Legyen adott egy konvex sokszög a síkban, csúcsainak koordinátáival, melyek óramutató járása szerinti bejárási tömbben adottak egy tömbben: x0 y0 x1 y1 x2 y2 ….. Számítsuk ki a sokszög kerületét és területét!

73 A kerületszámítás eleje
Az egyik pont (x,y), a másik (xx,yy) koordinátájú A távolságot számító függvény: public static double tav(double x, double y, double xx, double yy) { return Math.Sqrt((x - xx) * (x - xx) + (y - yy) * (y - yy)); };

74 Közepe (a Main() belseje)
// példaadatok double[] tomb = new double[] { 1.0, 0.0, 0.0, 2.0, -1.0, 0.0, 0.0, -3.0 }; double x, y, xx, yy, d = 0.0; int i, n=tomb.Length; // a kód for (i = 0; i+3 < n; i=i+2) { // végig a pontokon x = tomb[i]; y = tomb[i+1]; xx = tomb[i+2]; yy = tomb[i+3]; d += tav(x, y, xx, yy); } d += tav(tomb[0], tomb[1], tomb[n - 2], tomb[n - 1]);

75 Háromszög területe Heron képlettel
A 3szög pontjai (x1,y1), (x2,y2), (x3,y3) public static double heron(double x1, double y1, double x2, double y2, double x3, double y3){ double s, d1, d2, d3 ; d1 = Math.Sqrt((x1 - x2)*(x1 - x2) + (y1 - y2)*(y1 - y2)); d2 = Math.Sqrt((x2 - x3) * (x2 - x3) + (y2 - y3) * (y2 - y3)); d3 = Math.Sqrt((x1 - x3) * (x1 - x3) + (y1 - y3) * (y1 - y3)); s = (d1 + d2 + d3) / 2.0; return Math.Sqrt(s * (s - d1) * (s - d2) * (s - d3)); }

76 Konvex sokszög területszámítása
double[] tomb = new double[] { 1.0, 0.0, 0.0, 2.0, -1.0, 0.0, 0.0, -3.0 }; double x, y, x1, y1, x2,y2, h = 0.0; int n = tomb.Length; x = tomb[0]; y = tomb[1]; for (int i = 2; i +3 < n; i = i+2) { x1 = tomb[i]; y1 = tomb[i+1]; x2 = tomb[i+2]; y2 = tomb[i+3]; h += heron(x, y, x1, y1, x2,y2); } Console.WriteLine("terulet: " + h); Koordinátatömbbeli indexek

77 Miért kell a konvexitás?
Amit számítunk: T=(sárga+lila+kék)+(lila+kék) +(lila+barna) Helyesen: T=(sárga+lila+kék)-(lila+kék) +(lila+barna)=sárga+lila+barna A konvexitás biztosítja, hogy a területek összeadhatók!

78 Legnagyobb közös osztó 1.
public static int euklidesz(int a, int b) { int t,a1,b1; a1 = Math.Max(a, b); // kell-e ez a sor ?! b1 = Math.Min(a, b); // kell-e ez a sor ?! while (a1 != b1) if (a1 > b1) a1 = a1 - b1; else { t = a1; a1 = b1; b1 = t; } } return a1;

79 Legnagyobb közös osztó 2.
public static int gcd(int a, int b) { int t=0; while (b != 0) { Console.WriteLine("t= " + t " a= " + a + " b= " + b); t = b; b = a % b; a = t; } Console.WriteLine("t= " + t " a= " + a + " b= " + b); return a; Hívás gcd(6,10) t a b 6 10 4 2

80 Legnagyobb közös osztó 3.
function gcd(a, b) ha b = 0 return a egyébként return gcd(b, a mod b) Pszeudokódban public static int gcd(int a, int b) { return ( b == 0 ? a : gcd(b, a % b) ); } Rekurzív C# algoritmus

81 Horner séma egy polinom adott x helyen vett értékének gyors kiszámítására
Példa egy harmadfokú polinomra: P(x)=7x3 – 2x2 + 5 x = (7x2 – 2x + 5) x - 8= ((7x-2) x +5) x - 8 Az együtthatókat jelöljük: a[3] = 7 a[2]= - 2 a[1]= 5 a[0]= - 8 n=3; POL= a[n]; for(i=n-1; i>=0; i--) POL = POL*x + a[i]; A szorzások száma 3+2+1, ált esetben O(n*n) A szorzások száma 3, ált esetben O(n)

82 A Fibonacci sorozat A Fibonacci számsorozatban minden szám az első kettő után - az azt megelőző kettő összege. Így tehát a számsorozat: 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233 stb. Minél későbbi tagjait vesszük a sorozatnak, két egymást követő szám aránya annál inkább az aranymetszéshez fog közelíteni (ami megközelítőleg 1:1,618 vagy – ami ezzel közelítőleg egyenlő -0,618:1)

83 Kiszámítás rekurzívan
public static int fibo(int i){ if (i == 0) return 0; else if (i == 1) return 1; else return fibo(--i) + fibo(--i); } Beugrató kérdés: Ez ugyanaz-e, mint return 2*fibo(--i) ? return fibo(i-1) + fibo(i-2); A Fibonacci számok gyorsan nőnek, a long indokolt lehet

84 Fibonacci tömbbel int i, n; Console.Write("n= ");
n = int.Parse(Console.ReadLine()); long[] f = new long[n]; // Fibonacci számok gyorsan nőnek f[0]=0; f[1]=1; for( i=2;i<n; i++) f[i]= f[i-1]+f[i-2];

85 Fibonacci ArrayList-tel
int i=0, value=0; Console.Write(„Keressük az első Fibonacci számot, amely nagyobb mint: "); int n = int.Parse(Console.ReadLine()); ArrayList fiblist = new ArrayList(); fiblist.Add(0); fiblist.Add(1); for (i = 2; value <= n; i++) { int[] tomb = new int[fiblist.Count]; fiblist.CopyTo(tomb); value = tomb[i - 1] + tomb[i - 2]; fiblist.Add(value); } Console.WriteLine(n+" . fibo= " + fiblist[--i]);

86 Volt-e szükség menetközben mindegyik Fibonacci számra?
Ha a feladat úgy szól, hogy olvassunk be egy n számot, és ha az első n-nél nagyobb Fibonacci szám 5-tel osztható, akkor írjuk ki az eddigi Fibonacci számok számtani közepét, egyéb esetben pedig a mértani közepét, akkor szükség van az összes korábbi Fibonacci számra (avagy mégsem??) De ha csak az aktuális Fibonacci szám kell, akkor van a tömbhasználatnál egyszerűbb megoldás. A rekurzió, vagy…

87 Fibonacci segédváltozókkal
int i = 0, seged0 = 0, seged1 = 1, f=0; Console.Write("Keressük az n-dikFibonacci számot, ahol n= "); int n = int.Parse(Console.ReadLine()); if (n == 0) Console.WriteLine("A válasz 0"); else if (n==1) Console.WriteLine("A válasz 1"); else{ for (i = 2; i <= n; i++) { f =seged0 + seged1; seged0 = seged1; seged1 = f; } Console.WriteLine("f= "+f); Gyors és memóriatakarékos! Csak a legutolsó Fibonacci számot adja meg.

88 Fibonacci zárt alakban – a képlet
Ekkor c0=0, c1=1, és n >=1 esetén (teljes indukcióval belátható) Fentiekből következik, hogy cn a Fibonacci sorozat!!!

89 Fibonacci zárt alakban – a program
int fib; Console.Write("Hanyadik Fibonacci számot számítsuk: "); double n = double.Parse(Console.ReadLine()); double gyot =Math.Sqrt(5.0); fib = (int) Math.Truncate( (1.0/gyot)*( Math.Pow(((1.0+gyot)/2.0),n) - Math.Pow(((1.0-gyot)/2.0),n) ) ); Console.WriteLine("Az eredmény:"+fib); Duplapontos szám egész részét számolja (kihagyható)

90 Rendezések A rendezési algoritmus olyan algoritmus, amely egy lista vagy egy tömb elemeit egy meghatározott (növekvő vagy csökkenő) sorrendbe helyezi el. A rendezés lehet numerikus és lehet lexikografikus. A rendezés legtöbbször azért szükséges, hogy utána könnyebben lehessen keresni. Ezentúl, szűkebb értelemben vett rendezés alatt egy olyan algoritmust fogunk érteni, melynek inputja egy egészeket tartalmazó számsorozat, outputja a számsorozat növekvő sorrendben. Az output mindig egy permutációja az inputnak.

91 A rendezési algoritmusok osztályozása
Egy n elemű tömb esetében a szükséges összehasonlítások száma szerint Általános esetben (mi is az általános eset ?!) O(n*log n) összehasonlításnál kevesebb nem elég Számos ismert esetben (példák jönni fognak) O(n*n) összehasonlítás szükséges Néhány igen speciális esetben (legutóbb tárgyalt eset, ahol csak 0 és 1 szerepelt) O(n) is elég Az O (ordó) jelölés magyarázatát ld. az Analízis tárgyban, vagy a következő dián:

92 Ordó, ordó Legyen f és g két, természetes számokon értelmezett, nem-negatív, valós értékű függvény. Vezessük be a következő jelöléseket: f=O(g), (f=nagy Ordó g) ha van olyan c>0 konstans, hogy minden elég nagy n-re f(n) ≤ c*g(n) f=o(g), (f=kis ordó g) ha g(n) csak véges sok ehlyen nulla, és f(n)/g(n)→0, ha n →∞ f=Ω(g), ha g=O(f). (ritkán használatos) f=Θ (g), ha f=O(g) és g=O(f), azaz vannak olyan c1,c2 konstansok, hogy minden elég nagy n-re c1g(n) ≤ f(n) ≤c2g(n) Az egyenlőség nem szimmetrikus, pl. O(n)=O(n2), ez ugyanis azt jelenti, hogy f=O(n) esetén f=O(n2) is fennáll, de fordítva ez nem igaz: O(n2) ≠O(n)

93 További szempontok Szokás vizsgálni a cserék (swap) számát is.
Legyen a és b egészek. Cseréljük ki az értéküket! { int kesztyu = a; a = b; b = kesztyu; } Vizsgálható a memória takarékosság, vannak „helyben” rendező algoritmusok, és vannak olyanok, amelyek segéd tárterületeket igényelnek. Vannak rekurzív és nem rekurzív rendezések.

94 Stabilitás Stabil rendezésnél az azonos kulcsú rekordok relatív sorrendje megmarad, nem stabil rendezésnél változhat. Tekintsünk egész koordinátájú pontokat a síkban, és bocsássunk merőlegeseket az x-tengelyre. Legyen az a feladat, hogy az így keletkezett szakaszokat rendezni kell az x-tengelyen vett koordinátájuk szerint. Nevezzük az x-koordinátákat kulcsnak. Eszerint történnek az összehasonlítások, de csere esetén a teljes szakaszt (azaz pontpárt) kell cserélni. Legyen a pontsorozat (4, 2) (3, 7) (3, 1) (5, 6) Két sorrend is létrejöhet, ha vannak azonos x-koordinátájú (x=3) pontok: (3, 7) (3, 1) (4, 2) (5, 6) (eredeti sorrend marad, ez a stabil) (3, 1) (3, 7) (4, 2) (5, 6) (eredeti sorrend változik)

95 Buborékrendezés A buborék rendezés a legrosszabb esetben (amikor az elemek csökkenő sorrendben vannak) valamint az átlagos esetben (amikor az elemek véletlenszerűen helyezkednek el) is O(n*n) komplexitású. Mivel sok O(n*log n) komplexitású rendezés van, nagy n esetén a buborék nem előnyös, kivéve, ha az elemek nagyjából eleve növekvően rendezettek.

96 Buborék 1. (a legnagyobb elem „felszáll”)
public static void bubbleSort(int[] A) { bool csereltunk; int i, kesztyu; do { csereltunk = false; for (i = 0; i < A.Length - 1; i++) { if (A[i] > A[i + 1]) { kesztyu = A[i]; A[i] = A[i + 1]; A[i + 1] = kesztyu; csereltunk = true; } } while (csereltunk); } // nem ad visszajelzést, ha a tömb 0 vagy 1 hosszú

97 Buborék 1. főprogram static void Main() {
// példának okáért rendezzünk 10 véletlen generált egész számot static void Main() { int[] A = new int[10]; int i; Random RandomClass = new Random(); for (i = 0; i < A.Length; i++) { A[i] = RandomClass.Next(10, 30); Console.Write(A[i] + " "); } Console.WriteLine(); bubbleSort( A ); for (i = 0; i < A.Length; i++) Console.Write(A[i] + " "); Console.ReadLine();

98 Buborék 2. (a lgkisebb elem „leszáll”)
public static void bubbleSort(int[] A) { if (A.Length == 0) Console.WriteLine("A tömb üres!"); else if (A.Length == 1) Console.WriteLine("A tömb 1 elemű!"); else { int i, j, kesztyu; for (i = 0; i < A.Length - 1; i++) for (j = A.Length - 1; j > i; j--) if (A[ j – 1 ] > A[ j ]) { kesztyu = A[j]; A[j] = A[j - 1]; A[j - 1] = kesztyu; } }

99 Kétirányú buborék – cocktail sort
bottom = 0; top = n-1; bool csere_volt = true; while (csere_volt == true){ csere_volt = false; for (i = bottom; i < top; i++) if (a[i] > a[i + 1]) { csere_volt= true; kesztyu = a[i]; a[i]=a[i+1]; a[i+1]=kesztyu; } top = top - 1; for (i = top; i > bottom; i--) if (a[i] < a[i - 1]) { csere_volt = true; kesztyu = a[i]; a[i] = a[i-1]; a[i-1] = kesztyu; } bottom = bottom + 1; }

100 2, 3, 4, 5, 1 sorozatnál „felszálló buborék” 4 menetet csinál, a „leszálló buborék” egyet
5, 1, 2, 3, 4 sorozatnál fordítva A kétirányú buborék mindkét esetben egy menetet csinál, igaz; egy menet két ciklust igényel Nagyságrendileg a cocktail is O(n*n)

101 Kerti törpe rendezés (C#)
while (i < n) { if (a[i - 1] <= a[i]) i++; // ha jó a sorrend, előre! else { int kesztyu = a[i - 1]; a[i - 1] = a[i]; a[i] = kesztyu; i--; if (i == 0) i = 1; } // else vége } // while vége

102 Hogyan működik? Az algoritmus megkeresi az első olyan helyet, ahol két egymást követő elem rossz sorrendben van, és megcseréli őket. Ha egy ilyen csere után rossz sorrend keletkezik, az csak közvetlenül a legutolsó csere előtt lehet, így ezt is ellenőrizzük. Talán ez az elképzelhető legegyszerűbb rendezés.

103 A buborék lényeges javítása: fésűs rendezés
A fésűs rendezést (combsort) eredetileg 1980-ban tervezték, majd újra felfedezték és 1991-ben publikálták a Byte magazinban. A buborék rendezés módosítása, sebességében közelít a híres quicksorthoz. Az alapötlet az, hogy a sorozat végén levő kicsi értékekre találjunk rá minél hamarabb, mert ezek lassítják jelentősen a buborékot. (A sorozat elején levő nagy értékek nem annyira lassítóak, mert ezekre hamar rátalálunk).

104 A fésű kódja public static void combSort(int[] A) { int i; if (A.Length == 0) Console.WriteLine("A tömb üres!"); else if (A.Length == 1) Console.WriteLine("A tömb egyelemű!"); else { int gap = A.Length, cserevolt = 0, kesztyu; while ((gap >1) || (cserevolt !=0)) { if (gap > 1) // gap /1.3 lefelé kerekítve gap = (int)Math.Truncate((double)gap / 1.3); if ((gap == 10) || (gap == 9)) gap = 11; i = 0; cserevolt = 0; while (i + gap < A.Length) { if (A[i] > A[i+gap]){ kesztyu = A[i]; A[i]=A[i+gap]; A[i+gap] = kesztyu; cserevolt = 1; } i++;

105 Megjegyzések 10 ezer elemű tömbön végzett kísérletek szerint a fésűs rendezés alig rosszabb a quicksortnál (10 %-kal); a változtatás a buborékhoz képest nem nagy. Ugyanakkor nem kell gondoskodni az eleve rendezett esetről, ami a quicksortot nagyon lelassítja (látni fogjuk). A gap beállításával először a távollevő elemeket rendezzük. Ezután a gap csökken, míg végül egy lesz. Ez esetben azonos a program a buborékkal; következésképpen korrekt. Lacey és Richard Box megmutatták, hogy a gap minden lépésben 1.3-mal osztandó. Továbbá felfedezték, hogy 9 és 10 nem alkalmas gap-nek, és 11-gyel helyettesítendő.

106 Combsort versus quicksort
sec Combsort sec Bubblesort 1.36 sec 10 ezer egész szám rendezési kísérletének időeredményei Forrás:http://www.yagni.com/combsort/index.php [2008. nov. 16.]

107 Quicksort -rekurzívan
static void csere( ref int x, ref int y) { int t= x; x = y; y = t; } // a baloldali elemet a helyére teszi és visszaadja a hely indexét static int partition (int[] a, int first, int last) { int pivot = a[first], lastS1 = first, firstUnknown = first + 1; while (firstUnknown <= last) { if (a[firstUnknown] < pivot) { lastS1++; csere( ref a[firstUnknown], ref a[lastS1]); } firstUnknown++; } csere( ref a[first], ref a[lastS1]); return lastS1; static void quicksort (int[] a) { quicksort (a, 0, a.Length - 1); } static void quicksort (int[] a, int first, int last) { if (first < last) { int pivotIndex = partition (a, first, last); quicksort (a, first, pivotIndex - 1); quicksort (a, pivotIndex + 1, last);

108 Minimum kiválasztásos rendezés
static void csere (ref int x, ref int y) { int cs =x; x=y; y=cs; } static void Main() { int i,min, n = 10; int[] a = new int[n]; Random RandomClass = new Random(); for (i = 0; i < n; i++) a[i] = RandomClass.Next(10, 26); for (i = 0; i < n; i++) { min = i; for (int j = i + 1; j < n; j++) if (a[j] < a[min]) min = j; csere(ref a[i], ref a[min]); }

109 Beszúrásos rendezés static void csere (ref int x, ref int y) { int cs =x; x=y; y=cs; } static void Main() { int i,j, n = 10; int[] a = new int[n]; Random RandomClass = new Random(); for (i = 0; i < n; i++) a[i] = RandomClass.Next(10, 26); for (i = 1; i < n; i++) { j = i - 1; while ((j> -1) && (a[j] > a[j+1])) { csere(ref a[j], ref a[j+1]); j--; }

110 Sorting demo

111 Vödörrendezés // a 0 és M intervallumba eső elemek rendezése
const int darab = 60; // rendezendő elemek száma const int M = 100; // maximum érték const int vödörszám = 10; int vödör_range = M / vödörszám; // a rendezendő sorozat véletlen generálása és kiíratása int i,j; int[] a = new int[darab]; Random r = new Random(); for (i = 0; i < a.Length; i++) a[i] = r.Next(M); for (i = 0; i < a.Length; i++) Console.Write(" " + a[i]); Console.WriteLine();

112 A tömböt szétszedjük vödrökre
// listatömb deklarációja ArrayList[] array = new ArrayList[vödörszám]; for (i = 0; i < array.Length; i++) array[i] = new ArrayList(); // a tömböt szétszedjük vödrökre (=listákra) for (i = 0; i < a.Length; i++) { j = a[i] / vödör_range; // a[i] a j-dik vödörbe esik array[j].Add(a[i]); // a[i]-t betesszük a j-dik vödörbe }

113 A listák rendezése egyenként
for (i = 0; i < array.Length; i++) { Console.WriteLine(i + " . lista= "); // a listákat egyenként rendezzük for (j = 0; j < array[i].Count; j++) array[i].Sort(); // és kiírjuk a demo kedvéért for (j = 0; j < array[i].Count; j++) Console.Write(" " + array[i][j]); Console.WriteLine(); }

114 Befejezés // átmásoljuk a vödröket egyetlen listába
ArrayList egylista = new ArrayList(); for (i = 0; i < array.Length; i++) for (j = 0; j < array[i].Count; j++) egylista.Add(array[i][j]); // az egyesített listát visszaírjuk a tömbbe egylista.CopyTo(a); // és kiírjuk a rendezett tömböt for (i = 0; i < a.Length; i++) Console.Write(" " + a[i]);

115 Halmazműveletek: metszet
A feladat most két tömb a[0..n-1] és b [0..m-1] azonos elemeinek kiválogatása c tömbbe. A feladat csak úgy értelmezhető pontosan, ha az egyes tömbökben egy elem nem szerepel kétszer. (Mivel most a matematikai halmazokat tömbként ábrázoljuk.) Az algoritmus lényege: menjünk végig az a tömb elemein, és válogassuk ki azokat (kiválogatás), melyek szerepelnek b-ben (eldöntés). Így a feladat a korábbi tételekre visszavezethetõ. c maximális elemszáma n és m közül a kisebbik. Feltételeztük, hogy egyik halmaz sem üres.

116 Deklarációk a metszethez C#
int n = 10, m=6, db =Math.Min(n,m); // tömbméretek int i,j,k; // ciklusváltozók int[] a = new int [n]; // a halmaz elemei 0..n-1 int[] b = new int [m]; // b halmaz elemei 0..m-1 int[] c = new int [db]; // a metszet elemei // db csak a maximális lehetséges tömbméret. Ha a metszet // üres, akkor nyilván nincs elem a metszetben.

117 Metszet C# kód k = 0; for (i=0; i<n;i++) {
Az if NINCS a ciklusban, mert a for törzse üres! k = 0; for (i=0; i<n;i++) { for(j=0; (j<m) && (b[j] != a[i]); j++); // amíg j<m és b[j]<>a[i] if (j<m) c[k++]=a[i]; // ha j=m, akkor a[i] nem szerepelt b-ben // ha j<m, akkor a[i] előfordult b-ben } for (i = 0; i < k; i++) Console.WriteLine(c[i]); // kiíratás

118 Halmazműveletek: unió
A feladat most két tömb a[0..n-1] és b [0..m-1] elemeinek egyesítése c tömbbe. Az egyes tömbökben egy elem nem szerepel kétszer. (mint az előbb) A legkézenfekvőbb megoldás: tegyük be c-be a összes elemét, majd b-ből azokat, melyek nem szerepelnek a-ban. c elemszáma legfeljebb n+m. Feltételeztük, hogy egyik halmaz sem üres.

119 Unió C# kód for (i = 0; i < n; i++) c[i] = a[i]; //a-t áttöltjük c-be k = n; for (j = 0; j < m; j++){ // keressük azt a b-belit, ami nincs a-ban for (i = 0; (i< n) && (b[j] != a[i]); i++) ; if (i >= n) c[k++] = b[j]; // ha volt ilyen, c-be teszzük } for (i = 0; i < k; i++) Console.WriteLine(c[i]); // Futásidő n*m nagyságrendű!

120 Unió speciális esetben (C#): az a és b (halmazokat reprezentáló) tömbök rendezettek (összefuttatás)
Csak az egyik fut! i = 0; j = 0; k = 0; while(( i < n) && ( j < m)) if (a[i]<b[j]) c[k++] = a[i++]; else if (a[i]==b[j]) {c[k++]= a[i++]; j++;} else c[k++] = b[j++]; for (x=i; x<n; x++) c[k++] =a[x]; for (x=j; x<m; x++) c[k++] =b[x]; for (i = 0; i < k; i++) Console.WriteLine(c[i]); // Futásidő n+m nagyságrendű, n*m helyett (előző)!

121 Rekurzív bináris keresés (rendezett tömbben)
public static int binker(int[] tomb, int value, int low, int high) { if (high < low) return -1; int mid = (low + high) / 2; if (tomb[mid] > value) { mid--; return binker(tomb, value, low, mid); } else if (tomb[mid] < value) { mid++; return binker(tomb, value, mid, high); else return mid;

122 Iteratív bináris keresés (rendezett tömbben)
public static int binker(int[] tomb, int value) { int mid, low = 0, high = tomb.Length-1; while (low <= high) { mid = (low + high) / 2; if (tomb[mid] > value) high = --mid; else if (tomb[mid] < value) low = ++mid; else return mid; // megtalálva } return -1; // nincs meg

123 Dinamikus Programozás

124 Mátrixok láncszorzása
A dinamikus programozás egy általános eljárás bizonyos problémák megoldására Példa: Mátrixok láncszorzása Hogyan szorzunk mátrixokat. C = AB A is d × e, B is e × f O(d*e*f ) időben A C B d f e i j i,j

125 Mátrixok láncszorzása
Kiszámítandó A = A0A1…An-1 Ai mérete di × di+1 Probléma: Hogyan zárójelezzünk? Az eredménynek mindegy, mert a szorzás asszociatív, de a műveleti sebesség nagyon eltérhet! Example B mérete 3 × 100 C mérete 100 × 7 D mérete 7 × 5 (BC)D 3×100×7 + 3×7×5 = 2305 szorzás B(CD) 3×100× ×7×5 = 5000 szorzás

126 A „brutális” módszer Mátrix láncszorzás: Futásidő:
Próbáljuk megkeresni az összes zárójelezését A=A0A1…An-1-nak Mindegyikre számítsuk ki a műveletek számát Válasszuk ki a legjobbat Futásidő: Zárójeléezések száma = n-csúcsú bináris fák száma Exponenciális! Ez az n-dik Catalan szám – nagyságrendileg 4n. Kiszámíthatatlan !

127 A mohó megközelítés Ötlet #1: mindig azt a szorzatot válasszuk, amelyik a legkevesebb műveletet igényli Ellenpélda arra, hogy a fenti ötlet optimális lehetne: A mérete 101 × 11 B mérete 11 × 9 C mérete 9 × 100 D mérete 100 × 99 Az ötlet szerint legjobb A((BC)D) = szorzás Ennél jobb: (AB)(CD) = szorzás AB 101*11*9=9999 BC 11*9*100=9900 CD 9*100*99=89100 A(BC) 101*11*100=111100 (BC)D 11*100*99=108900

128 A “rekurzív” megközelítés
Definiálunk alproblémákat: Keressük meg a legjobb zárójelezését ennek: AiAi+1…Aj. Legyen Ni,j = ezen részprobléma műveleti igénye. A teljes probléma optimális megoldása N0,n-1. Részprobléma optimalitás: Az optimális megoldás az optimális részprblémák függvényében lesz definiálva Kell, hogy legyen egy utoljára végrehajtott szorzás az optimális megoldásban (ez a bináris fa gyökere) Mondjuk, hogy ez az i indexnél volt: (A0…Ai)(Ai+1…An-1). Ekkor az optimális megoldás N0,n-1 ia két optimális részmegoldás, N0,i és Ni+1,n-1 összege, valamint az utolsó szorzás

129 A karakterisztikus egyenlet
A globális optimum az optimális részproblémáktól, valamint az utolsó szorzás helyétől függ Vizsgáljunk meg minden lehetséges helyet az utolsó szorzásra: Tudjuk, hogy Ai mérete di × di+1 Így a karakterisztikus egyenlet Ni,j –re a következő alakú: Az alproblémák nem függetlenek, az alproblémák átfedik egymást.

130 A dinamikus programozási algoritmus
Nem használunk rekurziót, mivel az alproblémák átfedik egymást helyette “bottom-up” módon alproblémákkal foglalkozunk Ni,i számítása könnyű, ezért ezzel kezdünk Ezután 2,3,… hosszú alproblémákkal foglalkozunk A futási idő: O(n3) Algorithm matrixChain(S): Input: n db összeszorzandó mátrix Output: a szorzások optimális száma for i  1 to n-1 do Ni,i  0 for b  1 to n-1 do for i  0 to n-b-1 do j  i+b Ni,j  + for k  i to j-1 do Ni,j  min{Ni,j , Ni,k +Nk+1,j +di dk+1 dj+1}

131 Az algoritmus magyarázata
A bottom-up konstrukció feltölti az N tömböt az átlók mentén Ni,j az i-dik sor és a j-dik oszlop korábban kiszámított értékeiből számítódik A táblázat egy eleme O(n) idő alatt tölthető ki A teljes idő O(n3) A zárójelezéshez az kell, hogy „k” értékére „emlékezzünk” mindegyik esetben A válasz N n-1 1 2 j 1 i n-1

132 A láncszorzási algoritmus
Példa: ABCD A mérete 10 × 5 B mérete 5 × 10 C mérete 10 × 5 D mérete 5 × 10 Algorithm matrixChain(S): Input: n db összeszorzandó mátrix Output: # szorzások optimális száma for i  0 to n-1 do Ni,i  0 for b  1 to n-1 do // b is # of ops in S for i  0 to n-b-1 do j  i+b Ni,j  +infinity for k  i to j-1 do sum = Ni,k +Nk+1,j +di dk+1 dj+1 if (sum < Ni,j) then Ni,j  sum Oi,j  k return N0,n-1 N 500 500 1000 2 A AB A(BC) (A(BC))D 1 250 500 1 B BC (BC)D 2 500 C CD 3 D

133 static void Main(string[] args) {
int[] d = { 10, 5, 10, 5, 10 }; int i, j, k, b, n = d.Length-1; double sum = 0.0; const double v = 1.0 / 0; // végtelen double[,] N = new double[n, n]; for(i=0;i<n;i++) N[i,i] = 0; for(b=1;b<n;b++) for (i = 0; i < n - b ; i++) { j = i + b; N[i,j] = v; // végtelen for (k = i; k < j ; k++) { sum = N[i, k] + N[k + 1, j] + d[i] * d[k + 1] * d[j + 1]; if (sum < N[i, j]) N[i, j] = sum; } for (i = 0; i < n; i++) { for (j = 0; j < n; j++) Console.Write(N[i, j].ToString().PadLeft(6) ); Console.WriteLine(); Console.WriteLine("Az eredmény= " + N[0, n - 1]); Console.ReadLine(); } // Main vége

134 Recovering operations
Example: ABCD A is 10 × 5 B is 5 × 10 C is 10 × 5 D is 5 × 10 // a zárójelezéshez // mátrixlánc Ai -től Aj -ig exp(i,j) if (i=j) then // alapeset, 1 mátrix return ‘Ai’ else k = O[i,j] // piros értékek S1 = exp(i,k) // 2 rekurzív hívás S2 = exp(k+1,j) return ‘(‘ S1 S2 ‘)’ N 500 500 1000 2 A AB A(BC) (A(BC))D 1 250 500 1 B BC (BC)D 2 500 C CD 3 D

135 static string EXP(int i, int j, int[,] O) {
int k; string s1, s2; if (i == j) return "A" + i.ToString(); else { k = O[i, j]; s1 = EXP(i, k, O); s2 = EXP(k+1,j,O); return "("+s1 + s2+")"; static void Main(string[] args) { int[,] O = new int[n, n]; for(i=0;i<n;i++) N[i,i] = 0; for(b=1;b<n;b++) for (i = 0; i < n - b ; i++) { j = i + b; N[i,j] = v; for (k = i; k < j ; k++) { sum = N[i, k] + N[k + 1, j] + d[i] * d[k + 1] * d[j + 1]; if (sum < N[i, j]) { N[i, j] = sum; O[i, j] = k; } } Console.WriteLine("Az eredmény= " + N[0, n - 1]); Console.WriteLine("A zárójelezés = " + EXP(0,n-1,O)); Console.ReadLine(); } // Main vége

136 Optimalizálási problémák
Egy optimalizálási problémában nemcsak egy megoldást, hanem a legjobb megoldást keressük A mohó algoritmus fázisokban dolgozik. Minden egyes fázisban: Tegyük azt, ami most a legjobb, nem törődve a jövővel. Abban bízik a mohó, hogy a lokális optimumok sorozata globális optimumhoz vezet. (Néha ez a helyzet, lásd Kruskal algoritmus, PPT, jövő félév).

137 Mohó algoritmusok Maximalizáljuk életünk örömeit:
Első algoritmus: élvezzük az életet naponta Második algoritmus: dolgozz keményen, tanulj, szerezz diplomát, legyen jó fizetésű állásod, keress pénzt, és legyen jó nyugdíjad Melyik algoritmus maximalizál a teljes életre?

138 Ritka mátrixok

139 4 11 16 2 -1 3 -7 Jelölje a fenti 10x8 –as mátrixot A. Összesen 7 nem nulla eleme van.

140 10 8 7 1 2 4 3 11 5 16 -1 9 -7 Sorok száma az eredeti A mátrixban
Oszlopok száma az eredeti A mátrixban 10 8 7 1 2 4 3 11 5 16 -1 9 -7 Jelölje RA a baloldali mátrixot. Tárigénye 8*3*4=96 byte. Az előző dia A mátrixának tárigénye 10*8*4= 320 byte. A két mátrix ugyanazt az információt hordozza! Figyeljük meg, hogy RA sorai (a fejlécet nem beszámítva) rendezettek a sorok első mezői szerint, és azonos első mező esetén (5) a második mező szerint rendezettek.

141 Mi ennek a jelentősége? Ha az A mátrix nagyon nagy (pl. 1000*1000 méretű), akkor tárolása gondot okozhat. Ha azonban a nem nulla elemek száma relatíve kicsi (mondjuk 100), és A-t átírjuk RA ritka mátrixos formába, akkor RA tárigénye 101*3*4 byte, szemben A tárigényével, ami 1000*1000*4 byte. A „spórolást” úgy kaptuk, hogy a nullákat nem tároltuk, azaz megállapodtunk abban, hogy amit nem tárolunk, az nulla.

142 normál mátrix (A) átalakítása ritka formátumúvá
static int[,] normal2ritka(int[,] A) { int i, j, t=0; int k=1; // a nulladikba a fejléc kerül for (i = 0; i < A.GetLength(0); i++) for (j = 0; j < A.GetLength(1); j++) if (A[i, j] != 0) t++; int[,] R = new int[t + 1, 3]; R[0, 0] = A.GetLength(0); R[0, 1] = A.GetLength(1); R[0, 2] = t; for (i = 0; i < A.GetLength(0); i++) { for (j = 0; j < A.GetLength(1); j++) if (A[i, j] != 0) { R[k, 0] = i; R[k, 1] = j; R[k, 2] = A[i,j]; k++; } return R; Figyeljük meg, hogy ebben az algoritmusban A elemein sorfolytonosan kell végigmenni, tehát ha A túl nagy és nem fér el a tárban, lehet szekvenciálisan eljárni!

143 Transzponálás Egy mátrix transzponálása a főátlóra való tükrözést jelenti (komplex esetben konjugálni is szoktunk, de most vegyük csak a valós esetet). Jelölje B az A transzponáltját. int[,] B = new int [A.GetLength(1),A.GetLength(0)]; for (i = 0; i < A.GetLength(0); i++) { for (j = 0; j < A.GetLength(1); j++) B[j,i] = A[i, j]; } Ha A nagyon nagy méretű, akkor ez az egyszerű algoritmus nem használható, mert például A nem fog elférni a tárban. Itt i és j „messze” lehet egymástól, a szekvenciális feldolgozás nem megy.

144 RA (A ritka mátrixos alakja)
8 10 7 2 1 4 3 11 5 16 -1 9 -7 10 8 7 1 2 4 3 11 5 16 -1 9 -7 Ez leírja A transzponáltját, de sajnos nem ritka mátrixos forma, mert hiányzik a rendezettség!

145 R ritka mátrix transzponáltjának (TR) előállítása: első módszer
int n = normal2ritka(A).GetLength(0); //A nem nulla elemeinek száma +1 int m = normal2ritka(A).GetLength(1); // m== 3 int[,] R = new int[n, m]; R = normal2ritka(A); int[,] TR = new int[n, m]; TR[0, 0] = R[0,1]; TR[0, 1] = R[0,0]; TR[0, 2] = R[0, 2]; // TR[0,2]==n – 1== A nem nulla elemeinek száma int q = 1; for(j=1; j< R[0,1]; j++) // j A oszlopain megy végig for (i = 1; i <= R[0, 2]; i++) // R sorain megy végig if (R[i,1] == j) { TR[q,0]=R[i,1]; TR[q,1]=R[i,0]; TR[q,2]=R[i,2]; q++; }

146 Egy gyorsabb módszer int[,] TR = new int[n, m];
TR[0, 0] = R[0,1]; TR[0, 1] = R[0,0]; TR[0, 2] = R[0, 2]; int darab = R[0,2]+1; int[] s = new int[darab]; int[] t = new int[darab]; for (i = 0; i < darab; i++) s[i] = 0; // összeszámoljuk, hogy melyik oszlopból mennyi van for (j = 1; j <darab; j++) s[R[j, 1]]++;

147 int sum = 0; t[0] = 1; // pozíciók előkészítése for (j = 1; j < darab; j++) { sum = sum + s[j - 1]; t[j] = sum + 1; } for (j=1;j<= R[0,2]; j++) { TR[t[R[j, 1]], 0] = R[j, 1]; TR[t[R[j, 1]], 1] = R[j, 0]; TR[t[R[j, 1]], 2] = R[j, 2]; t[R[j, 1]]++;


Letölteni ppt "AAO Csink László 2010. ősz."

Hasonló előadás


Google Hirdetések