470 likes | 762 Views
AAO. Csink László 2008. november. 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();
E N D
AAO Csink László 2008. november
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 < n - 1; i++) // b1 végére rakjuk az 1-ket if (b[i] ==1) b1[--n] =b[i]; // fontos, hogy --n és nem n-- if (b.Length > 0) b1[--n] = b[i]; // az utolsót már így is lehet! else Console.WriteLine("a tömb üres!"); for(i=0; i < b.Length; i++) Console.Write(b1[i]+" ");
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;
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, de több ciklusra volt szükség
Geometriai feladat 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!
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)); };
Közepe (a Main() belseje) 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; 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]);
Háromszög területe Heron képlettel A 3szög pontjai (x1,y1), (x2,y2), (x3,y3) public static doubleheron(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)); }
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);
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!
Legnagyobb közös osztó 1. • public static int euklidesz(int a, int b) • { • int t,a1,b1; • a1 = Math.Max(a, b); • b1 = Math.Min(a, b); • while (a1 != b1) • { • if (a1 > b1) a1 = a1 - b1; • else { t = a1; a1 = b1; b1 = t; } • } • return a1; • }
Legnagyobb közös osztó 2. public static int gcd(int a, int b) { int t; while (b != 0) { t = b; b = a % b; a = t; } return a; } Hívás gcd(6,10)
Legnagyobb közös osztó 3. functiongcd(a, b) if b = 0 return a elsereturngcd(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
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 - 8 = (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]= - 5 N=3; POL= a[N]; for(i=n-1; i>=0; i--) POL = POL*x + a[i];
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 0,618:1)
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
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];
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]);
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…
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.
Fibonacci zárt alakban – a képlet Ekkor c0=0, c1=1, és n >=1 esetén (teljes indukcióval belátható)
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);
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.
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 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 itt: http://en.wikipedia.org/wiki/Big_O_notation
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.
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 korrdiná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)
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.
Buborék 1. (csere figyelésével) 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 kezeltük, ha a tömb 0 vagy 1 hosszú
Buborék 1. főprogram 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(); }
Buborék 2. 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; } } }
Kétirányú buborék – cocktail sort (C#) 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; cs = a[i]; a[i]=a[i+1]; a[i+1]=cs; } top = top - 1; for (i = top; i > bottom; i--) if (a[i] < a[i - 1]){ csere_volt = true; cs = a[i]; a[i] = a[i-1]; a[i-1] = cs; } bottom = bottom + 1; }
Kerti törpe rendezés (C#)Wikipedia: The name comes from the supposed behavior of the Dutch garden gnome in sorting a line of flowerpots. It is conceptually simple, requiring no nested loops. i = 1; while (i < n) { if (a[i - 1] <= a[i]) i++;// ha jó a sorrend, előre! else { int cs = a[i - 1]; a[i - 1] = a[i]; a[i] = cs; i--; if (i == 0) i = 1; }// else vége } // while vége
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.
A buborék lényeges javítása: fésűs rendezés • A fésűs rendezést (comb sort) 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).
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 == 11)) gap = 9; i = 0; cserevolt = 0; while (i + gap < A.Length) { if (A[i] > A[i+gap]){ kesztyu = A[i]; A[i]=A[i+1]; A[i+1] = kesztyu; cserevolt = 1; } i++; } } } }
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ő.
Combsort versus quicksort 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.]
Quicksort -rekurzívan static void csere( ref int x, ref int y) { int t= x; x = y; y = 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], refa[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); } }
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]); } }
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,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 = 1; i < n; i++) { j = i - 1; while ((j> -1) && (a[j] > a[j+1])) { csere(ref a[j], ref a[j+1]); j--; } } }
Sorting demo • http://www.cs.ubc.ca/~harrison/Java/sorting-demo.html
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õ. cmaximális elemszáma n és m közül a kisebbik. Feltételeztük, hogy egyik halmaz sem üres.
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.
Metszet C# kód 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
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 legfeljebbn+m. Feltételeztük, hogy egyik halmaz sem üres.
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ükazt 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ű!
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ő)!