Kvadrat

Kvadrat

Avtor: Eva janša

Kvadrat-navodila in ideja

Navodila za nalogo


Vsi razredi naj obvezno vsebujejo:

  • Minimalni (tj. prazni) kontruktor public Razred(), ki tvori objekt s privzetimi (smiselnimi!) vrednostmi.
  • Metodo public override string ToString(), ki vse podatke v objektu zapiše v obliki niza. Podatki naj bodo med seboj ločeni z znakom ';'.
  • Polni konstruktor public Razred(string s), ki s klicem Razred(niz) generira tak objekt Razred r, za katerega velja niz = r.ToString().
  • Metodo DodajNaDatoteko(string imeDatoteke), ki objekt this zapiše (oz. doda; glej metodo File.AppendText()) kot vrstico this.ToString() v tekstovni datoteki.
  • Metodo PreberiZDatoteke(string imeDatoteke), ki vrne tabelo objektov, kjer posamezni objekt vsebuje podatke, ki so zapisani v posamezni vrstici tekstovne datoteke.
  • Metodo IzpisiDatoteko(string imeDatoteke), ki izpiše vsebino datoteke.
  • Metodo TvoriDatoteke(string imeDatoteke, int stElementov), ki tvori naključno datoteko.
  • Če je potrebno, napišite še metodo, ki izpiše tabelo elementov tega razreda.

Vsi razredi morajo biti robustni – vsi konstruktorji in metode morajo biti taki, da objekta ne spravijo v nesmiselno stanje. Če je potrebno, pri klicu metode s nedopustnimi parametri sproži izjemo. Razred je potrebno napisati kot 'class library' v ločenem projektu v isti rešitvi. Pri sestavljanju razredov si prej oglejte probleme, ki jih rešujete, da boste vedeli, kako mora biti razred zgrajen (katere podatke in metode naj vsebuje). V testnem programu, s katerim prikažete ključne funkcionalnosti razreda, naredite naslednje:

  • Napišite ustrezne metode za reševanje zastavljenih problemov.
  • V programu samem pa po vrsti
  • Pokličite metodo, ki na datoteko zapiše ducat objektov.
  • Preberite vse podatke z datoteke v tabelo.
  • Tabelo pregledno izpišite.
  • Nato kličite metode, ki so potrebne, da rešite zastavljene probleme in pregledno izpišite rezultate.
  • Na koncu na isto datoteko ponovno zapišite vse objektov (ne glede na to, ali so jih zgornje metode spremenile ali ne).

Napišite razred Kvadrat, s katerim predstavite kvadrat v ravnini, katerega stranice so vzporedne koordinatnim osem. Kvadrat je predstavljen s koordinatama stredišča in dolžino stranice.

  • Sestavite metodi za izračun ploščine in obsega kvadrata.
  • Izračunajte, kakšno površino prekrijejo kvadrati v dani tabeli. Upoštevajte, da se lahko kvadrata tudi prekrivata.
  • Iz dane datoteke kvadratov izbrišite tiste, ki v svoji notranjosti vsebujejo koordinatno izhodišče.
  • Vsem tistim kvadratom v tabeli, katerih obseg je več kot 10% manjši od povprečnega obsega vseh kvadratov, spremenite stranico tako, da bodo imeli obseg (do zaokrožitvene napake) enak povprečnemu.
  • Za vse kvadrate v tabeli preverimo, če se prekrivata, in če se izbrišemo tistega z večjo ploščino.

Ideja rešitve


Napišemo razred za objekt Kvadrat, ki je definiran s koordinatama središča in dolžino stranice. V razredu zapišemo vse zgoraj naštete metode, ki jih potrebujemo za nadaljnje delo z razredom.

  • Metodi za ploščino in obseg napišemo enostavno tako, da na koncu vrnemo izračun.
  • Za skupno ploščino kvadratov si napišemo pomožno metodo, ki nam vrne ploščino prekritega dela kvadratov. Te prekrite dele nato odštejemo od skupne ploščine vseh kvadratov.
  • Za izbris kvadratov potrebujemo le premislek, kdaj koordinatno izhodišče leži v objektu.
  • Za spremembo stranice pa potrebujemo izračun povprečnega obsega, potem pa še izračun velikosti posameznega obsega glede na povprečnega.
  • Za prekrivanje, pa se spet sklicujemo na pomožno metodo PrekritaPl. Če se kvadrata prekrivata, potem mora metod vrniti rezultat različen od 0. V tem primeru pa potem pogledamo, kateri kvadrat je večji in ga izbrišemo.

Koda-razred

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace kvadrat
{
    public class Kvadrat
    {
        //globalne spremenljivke
        private int _a;
        public int a
        {
            get { return _a; }
            set
            {
                if (value < 0)
                    throw new Exception("Dolžina stranice ne mire biti negativna!");
                else
                    this._a = value;
            }
        }
        public int x;
        public int y;


        /// <summary>
        /// minimalni konstruktor
        /// </summary>
        public Kvadrat()
        {
            //središče nastavimo na koordinatno izhodišče, stranico pa na dolžino 1
            x = 0;
            y = 0;
            a = 1;
        }

        /// <summary>
        /// konstruktor, ki mu podamo objekt zapisan v nizu
        /// </summary>
        /// <param name="n">niz oblike "x;y;a"</param>
        public Kvadrat(string n)
        {
            //iz niza s split dobimo podatke, ki jih spremenimo v števila
            string[] k = n.Split(';');
            x = int.Parse(k[0]);
            y = int.Parse(k[1]);
            a = int.Parse(k[2]);
        }

        /// <summary>
        /// metoda vrne v nizu zapisan objekt oblike "x;y;a"
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            string niz = x + ";" + y + ";" + _a;
            return niz;
        }

        /// <summary>
        /// na dano datoteko zapišemo objekt v obliki niza "x;y;a"
        /// </summary>
        /// <param name="imeDatoteke">v nizu podana pot do datoteke</param>
        public void DodajNaDatoteko(string imeDatoteke)
        {
            //datoteki dodamo vrstico z obstoječim objektom
            StreamWriter pisi = File.AppendText(imeDatoteke);
            pisi.WriteLine(this.ToString());
            pisi.Close();
        }

        /// <summary>
        /// iz datoteke preberemo vse objekte in jih shranimo v tabelo
        /// </summary>
        /// <param name="imeDatoteke">v nizu podana pot do datoteke</param>
        /// <returns></returns>
        public static Kvadrat[] PreberiZDatoteke(string imeDatoteke)
        {
            //beremo iz datoteke
            StreamReader beri = File.OpenText(imeDatoteke);
            //najprej v seznam shranimo vse vrstice
            List<string> vr = new List<string>();
            string v;
            while ((v = beri.ReadLine()) != null)
            {
                vr.Add(v);
            }
            //potem naredimo tabelo objektov dolžine enake številu vrstic
            Kvadrat[] tab = new Kvadrat[vr.Count];
            //po seznamu pa nato v tabelo dodajamo objekte
            for (int i = 0; i < tab.Length; i++)
            {
                tab[i] = new Kvadrat(vr[i]);
            }
            beri.Close();
            //na koncu vrnemo tabelo
            return tab;
        }

        /// <summary>
        /// na konzolo izpišemo vse vrstice v datoteki
        /// </summary>
        /// <param name="imeDatoteke">v nizu podana pot do datoteke</param>
        public static void IzpisiDatoteko(string imeDatoteke)
        {
            StreamReader beri = File.OpenText(imeDatoteke);
            string v;
            //dokler ne pridemo do konca datoteke vrstice izpisujemo na konzolo
            while ((v = beri.ReadLine()) != null)
            {
                Console.WriteLine(v);
            }
            beri.Close();
        }

        /// <summary>
        /// tvorimo datoteko z naključnimi objekti, podano dobimo število objektov
        /// </summary>
        /// <param name="imeDatoteke">v nizu podana pot do datoteke</param>
        /// <param name="stElementov">število elementov, ki jih želimo napisati na datoteko</param>
        public static void TvoriDatoteke(string imeDatoteke, int stElementov)
        {
            Random g = new Random();
            StreamWriter pisi = File.CreateText(imeDatoteke);
            //po zanki pišemo naključne objekte na datoteko
            for (int i = 0; i < stElementov; i++)
            {
                //koordinati x in y sta lahko med -100 in 100
                int x1 = g.Next(-100, 100);
                int y1 = g.Next(-100, 100);
                //stranica pa je lahko dolga do 50
                int a1 = g.Next(1,50);
                pisi.WriteLine(x1 + ";" + y1 + ";" + a1);
            }
            pisi.Close();
        }

        /// <summary>
        /// na konzolo izpišemo tabelo objektov
        /// </summary>
        /// <param name="t"></param>
        public static void IzpisiTabeloEl(Kvadrat[] t)
        {
            for (int i = 0; i < t.Length; i++)
            {
                Console.WriteLine(t[i].ToString());
            }
        }


    }
}

Koda-problemi

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using kvadrat;

namespace testKvadrat
{
    class Program
    {
        /// <summary>
        /// metoda vrne ploščino kvadrata
        /// </summary>
        /// <returns></returns>
        public static int Ploscina(Kvadrat k)
        {
            return k.a * k.a;
        }

        /// <summary>
        /// metoda vrne obseg kvadrata
        /// </summary>
        /// <returns></returns>
        public static int Obseg(Kvadrat k)
        {
            return 4 * k.a;
        }

        /// <summary>
        /// metoda vrne ploščino kvadratov podanih v tabeli
        /// </summary>
        /// <param name="k"></param>
        /// <returns></returns>
        public static int PloscinaKvadratov(Kvadrat[] k)
        {
            //ploščino nastavimo na 0
            int p = 0;
            //najprej seštejemo vse ploščine vseh kvadratov v tabeli
            for (int i = 0; i < k.Length; i++)
            {
                p += Ploscina(k[i]);
            }
            //če je v tabeli samo en kvadrat, potem vrnemo njegovo ploščino
            if (k.Length == 1) return p;
            //drugače pa za vsak par kvadratov
            for (int j = 0; j < k.Length; j++)
            {
                for (int l = j + 1; l < k.Length; l++)
                {
                    //z metodo PrekritaPl izračunamo ploščino prekrivajočega se območja in jo odštejemo od skupne
                    p -= PrekritaPl(k[j], k[l]);
                }
            }
            //na koncu to ploščino vrnemo
            return p;
        }

        /// <summary>
        /// vrne ploščino območja, kjer se kvadrata prekrivata
        /// </summary>
        /// <param name="k1"></param>
        /// <param name="k2"></param>
        /// <returns></returns>
        public static int PrekritaPl(Kvadrat k1, Kvadrat k2)
        {
            //ploščino nastavimo na 0
            int pl = 0;
            //pogledamo v kakšnem razmerju sta x koordinati središč
            if (k1.x <= k2.x)
            {
                //nato pa še v kakšnem razmerju sta koordianti y središč
                if (k1.y <= k2.y)
                {
                    //če se kvadrata prekrivata vrnemo ploščino območja, kjer se prekrivata
                    if ((k1.x + (k1.a / 2)) > (k2.x - (k2.a / 2)) && ((k1.y + (k1.a / 2)) > (k2.y - (k2.a / 2))))
                    {
                        pl = ((k1.x + (k1.a / 2)) - (k2.x - (k2.a / 2))) * ((k1.y + (k1.a / 2)) - (k2.y - (k2.a / 2)));
                    }
                }
                //enako naredimo tudi za ostale primere
                else
                {
                    if ((k1.x + (k1.a / 2)) > (k2.x - (k2.a / 2)) && ((k1.y - (k1.a / 2)) < (k2.y + (k2.a / 2))))
                    {
                        pl = ((k1.x + (k1.a / 2)) - (k2.x - (k2.a / 2))) * ((k2.y + (k2.a / 2)) - (k1.y - (k1.a / 2)));
                    }
                }
            }
            else
            {
                if (k1.y <= k2.y)
                {
                    if ((k1.x - (k1.a / 2)) < (k2.x + (k2.a / 2)) && ((k1.y + (k1.a / 2)) > (k2.y - (k2.a / 2))))
                    {
                        pl = ((k2.x + (k2.a / 2)) - (k1.x - (k1.a / 2))) * ((k1.y + (k1.a / 2)) - (k2.y - (k2.a / 2)));
                    }
                }
                else
                {
                    if ((k1.x - (k1.a / 2)) < (k2.x + (k2.a / 2)) && ((k1.y - (k1.a / 2)) < (k2.y + (k2.a / 2))))
                    {
                        pl = ((k2.x + (k2.a / 2)) - (k1.x - (k1.a / 2))) * ((k2.y + (k2.a / 2)) - (k1.y - (k1.a / 2)));
                    }
                }
            }
            //na koncu vrnemo naračunano ploščino
            return pl;
        }

        /// <summary>
        /// Z datoteke zbrišemo vse kvadrate, ki vsebujejo koordinatno izhodišče
        /// </summary>
        /// <param name="imeDatoteke"></param>
        public static void IzbrisiKvadrate(string imeDatoteke)
        {
            //odpremo datoteko za branje
            StreamReader beri = File.OpenText(imeDatoteke);
            List<string> vo = new List<string>();
            string v;
            //z zanko preberemo vse vrstice v datoteki
            while ((v = beri.ReadLine()) != null)
            {
                //za vsak kvadrat preverimo
                string[] o = v.Split(';');
                int x1 = int.Parse(o[0]);
                int y1 = int.Parse(o[1]);
                int a1 = int.Parse(o[2]) / 2;
                //če ne vsebuje koordintnega izhodišča
                if (!(((x1 - a1) <= 0) && ((y1 - a1) <= 0) && ((x1 + a1) >= 0) && ((y1 + a1) >= 0)))
                {
                    //ga dodamo na seznam
                    vo.Add(v);
                }
            }
            //zapremo datoteko za branje in jo odpremo za pisanje
            beri.Close();
            StreamWriter pisi = File.CreateText(imeDatoteke);
            //vsak kvadrat v novem seznamu nato zapišemo na datoteko
            foreach (string el in vo)
            {
                pisi.WriteLine(el);
            }
            pisi.Close();
        }

        /// <summary>
        /// vsakemu kvadratu v tabeli, katerega obseg je več kot 10% manjši od povprečnega obsega
        /// vseh kvadratov, spremenimo stranico tako, da bo obseg enak povprečnemu
        /// </summary>
        /// <param name="k"></param>
        public static void SpremeniStranico(Kvadrat[] k)
        {
            //za vsak kvadrat izračunamo obseg
            int[] obsegi = new int[k.Length];
            for (int i = 0; i < obsegi.Length; i++)
            {
                obsegi[i] = Obseg(k[i]);
            }
            //potem izračunamo povprečen obseg vseh kvadratov
            int povp = obsegi.Sum() / obsegi.Length;
            //po zanki preverjamo
            for (int j = 1; j < obsegi.Length; j++)
            {
                //če je obseg za več kot 10% manjši od povprečnega obsega
                if (obsegi[j] * 0.9 < povp)
                {
                    //kvadratu nastavimo stranico tako, da obseg povprečen
                    k[j].a = povp / 4;
                }
            }
        }

        /// <summary>
        /// metoda iz seznama kvadratov izbriše kvadrat, če se ta prekriva z enim od ostalih in je od le tega večji
        /// </summary>
        /// <param name="k"></param>
        public static void Prekrivanje(List<Kvadrat> k)
        {
            //za vsak par kvadratov preverimo
            for(int i = 0; i < k.Count; i++)
            {
                for(int j = i + 1 ; j < k.Count; j++)
                {
                    //če se prekrivata
                    if(PrekritaPl(k[i], k[j]) != 0)
                    {
                        //potem iz seznama izbrišemo tistega z večjo ploščino
                        if (Ploscina(k[i]) >= Ploscina(k[j])) k.Remove(k[i]);
                        else k.Remove(k[j]);
                    }
                }
            }
        }

        static void Main(string[] args)
        {
            //tvorimo datoteko z 12 naključnimi objekti
            Kvadrat.TvoriDatoteke("dat.txt", 7);
            //te objekte prepišemo v tabelo
            Kvadrat[] k = Kvadrat.PreberiZDatoteke("dat.txt");
            //tabelo izpišemo na konzolo
            Console.WriteLine("Objekti na datoteki:");
            Kvadrat.IzpisiTabeloEl(k);
            //za prvi kvadrat izračunamo ploščino in obseg
            Console.Write("Prvi kvadrat: ");
            Console.WriteLine(k[0].ToString());
            Console.Write("Ploščina: ");
            Console.WriteLine(Ploscina(k[0]));
            Console.Write("Obseg: ");
            Console.WriteLine(Obseg(k[0]));
            //iz datoteke zbrišemo kvadrate, ki vsebujejo koordinatno izhodišče
            Console.WriteLine("Zbriši kvadrate:");
            IzbrisiKvadrate("dat.txt");
            //potem ponovno preberemo vsebino datoteke in jo izpišemo
            k = Kvadrat.PreberiZDatoteke("dat.txt");
            Kvadrat.IzpisiTabeloEl(k);
            //izpišemo še njihovo skupno ploščino
            Console.Write("Skupna ploščina kvadratov: ");
            Console.WriteLine(PloscinaKvadratov(k));
            //spremenimo stranico kvadratom, ki imajo obseg za več kot 10% manjši od povprečnega
            Console.WriteLine("Kvadratu spremenimo stranico, če je njegov obseg več kot za 10% manjši od povprečnega: ");
            SpremeniStranico(k);
            Kvadrat.IzpisiTabeloEl(k);
            //pogledamo še, če se kvadrati prekrivajo in izbrišemo večje
            Console.WriteLine("Izbrišemo večje kvadrate, če se prekrivajo: ");
            List<Kvadrat> k1 = k.ToList();
            Prekrivanje(k1);
            k = k1.ToArray();
            Kvadrat.IzpisiTabeloEl(k);
            Console.ReadKey();
        }
    }
}

Razlaga rešitve-razred

  • V razredu najprej definiramo globalne spremenljivke, ki so: dolžina stranice in koordinati središča. Za dolžino stranice preverimo, če je podana pravilno, mora biti pozitivna.
  • Najprej napišemo minimalni konstruktor. Ta naredi objekt, ki ima središče v izhodišču, dolžino stranice pa ima 1.
  • Napišemo še konstruktor, ki dobi podan niz oblike"x;y;a", iz katerega potem naredi objekt. Tukaj niz najprej razdelimo glede na ';', potem pa posamezne lastnosti shranimo v globalne spremenljivke.
    Potem napišemo metodo, ki vrne objekt v obliki niza. Ta niz je oblike "x;y;z".
  • Naslednja metoda na datoteko doda nov objekt. Datoteko najprej odpremo za dodajanje teksta, potem nanjo zapišemo objekt v obliki niza, nato pa datoteko še zapremo.
  • Z naslednjo metodo iz datoteke preberemo objekte in jih dodamo v tabelo. Najprej tabelo odpremo za branje, in naredimo nov seznam nizov. Dokler beremo iz datoteke, nize dodajamo v seznam. Nato naredimo še novo tabelo, v tabelo pa shranjujemo objekte, ki jih naredimo iz nizov v seznamu. Datoteko zapremo, vrnemo pa tabelo objektov.
  • V naslednji metodi izpišemo vse vrstice v datoteki na konzolo. Datoteko najprej odpremo za branje. Dokler beremo iz datoteke, vsak niz izpišemo na konzolo. Na koncu datoteko zapremo.
  • Ta metoda na datoteko zapiše podano število naključnih objektov. Najprej definiramo generator naključnih števil. Nato ustvarimo datoteko na katero bomo pisali. Z zanko nato na datoteko napišemo podano število naključnih elementov. Koordinati x in y sta ptevili med -100 in 100, dolžina sranice pa je lahko med 1 in 50. Ko zapišemo vse objekte, datoteko zapremo.
  • Zadnja metoda v razredu pa je izpis tabele objektov na konzolo. Po zanki torej vsak objekt spremenimo v niz in ga izpišemo na konzolo.

Razlaga rešitve-problemi

  • Najprej napišemo metodo, ki vrne ploščino podanega kvadrata. Vrnemo torej kvadrat dolžine stranice.
  • Druga metoda pa vrne obseg podanega kvadrata. Obseg je štirikratnik dolžine stranice.
  • Naslednja metoda nam vrne skupno ploščino kvadratov v tabeli. Ploščino najprej nastavimo na 0. Potem po zanki seštejemo vse ploščine kvadratov v tabeli. Če je ve tabeli samo en kvadrat, vrnemo kar njegovo ploščino. Drugače pa se z dvema zanka sprehodimo najprej po celi tabeli, nato pa še od tenutnega indeksa do konca tabele. Potem pa za vsak par kvadratov z metodo PrekritaPl, ki jo bomo predstavili kasneje, izračunamo ploščino območja, kjer se kvadrata prekrivata. To ploščino potem odštejemo od skupne. Na koncu pa ploščino pe vrnemo.
  • Pomožna metoda PrekritaPl pa za dva podava kvadrata pogleda, če se prekrivata, nato pa izračuna ploščino prekritega območja. Ploščino območja pa računamo tako, da pogledamo pozicijo središč. Če je x koordinata prvega izhodišča manjša od x koordinate drugega izhodišča in velja enak tudi za y koordinato, je območje prekrivanja kar produkt razlik med koordinatami najbližjih oglišč. Enako stvar računamo tudi za vsa ostala možna prekrivanja kvadratov. Na koncu vrnemo naračunano ploščino.
  • Metoda IzbrisiKvadrate in datoteke pobriše objekte, ki vsebujejo koordinatno izhodišče. Najprej odpremo datoteko za branje in naredimo nov seznam nizov. Po zanki beremo iz datoteke. Za vsak objekt si zapolnimo njegove lastnosti, nato pa preverimo, če vsebuje koordinatno izhodišče. Če ga ne vsebuje, potem ga dodamo v seznam. Ko se zanka izteče, datoteko zapremo. Potem pa to datoteko ponovno odpremo za pisanje in nanjo napišemo na novo sestavljen seznam objektov. Na koncu datoteko ponovno zapremo.
  • SpremeniStranico v tabeli objektov vsem objektom, ki imajo obseg za več kot 10% manjši od povprečnega obsega kvadratov. Najprej naredimo novo tabelo. Po zanki izračunamo obseg vsakega posameznega objekta in gadodamo v novo tabelo. Nato izračunamo povprečni obseg kvadratov. Po novi zanki pa pregledujemo obsege posameznih kvadratov in jim popravimo stranice, če je njihov obseg za več kot 10% manjši od povprečnega.
  • Zadnja metoda pa iz seznama kvadratov izbriše kvadrat, če se ta prekriva s katerimo od drugih kvadratov in je od njega večji. Zopet imamo dve zanki, s prvo se sprehodimo po celi tabeli, z drugo pa od trenutnega indeksa do konca tabele. Za vsak par kvadratov nato pogledamo, če se prekrivata. Če se, potem pogledamo, kateri od kvadratov ima večjo ploščino in ga potem izbrišemo iz seznama.

Testni primeri

Testne primere izvajam tako, kot je napisano v navodilih. Najprej naredimo datoteko s 7 naključnimi objekti. Te objekte nato prepišemo v tabelo. Tabelo izpišemo na konzolo. Nato pa preverimo delovanje vseh metod, ki rešijo predpisane probleme:

  • Za prvi kvadrat izračunamo ploščino in obseg.
  • Nato nad tabelo izvršimo metodo IzbrisiKvadrate. Ponovno izpišemo tabelo objektov.
  • Izračunamo še skupno ploščino kvadratov in jo izpišemo.
  • Pogledamo še, če z metodo SpremeniStranico kateremu od kvadratov spremenimo stranico. Zato po izvedeni metodi spet izpišemo tabelo objektov.
  • Enako kot zgoraj naredimo še za metodo Prekrivanje. Le da moramo tukaj za izpis tabelo najprej pretvoriti v seznam, in nato spremenjen seznam nazaj v tabelo za izpis.

Te testne primere izvajamo na naključnih objektih. Tako namreč najlažje detektiramo možne napake v metodah.

Filmček

Viri

0%
0%