Προτάσεις Ολοκληρωμένων Ασκήσεων στη Java για Ηλεκτρολόγους Μηχανικούς
Εισαγωγή Προγραμματισμού (Java): 4 συνθετικές ασκήσεις και 1 προαιρετική τελική εργασία εξαμήνου, με θέματα από την ηλεκτρολογία, που καλύπτουν ολόκληρο το εξάμηνο
Αξιότιμε κύριε καθηγητά,
Σας παραθέτω πέντε προτάσεις για ολοκληρωμένες (συνθετικές) ασκήσεις, τις οποίες σκοπεύω να υλοποιήσω ο ίδιος ως εξάσκηση πάνω στην ύλη του μαθήματος: τέσσερις βασικές ασκήσεις και μία πέμπτη, προαιρετική και μεγαλύτερη, τελική εργασία εξαμήνου. Όλες είναι χτισμένες γύρω από απλά προβλήματα του Ηλεκτρολόγου Μηχανικού (αντιστάσεις και νόμος του Ohm, μετρήσεις τάσης, φορτία εγκατάστασης, μετρητής κατανάλωσης, φωτοβολταϊκό πάρκο), με φυσική που δεν ξεπερνά το επίπεδο του πρώτου έτους. Κάθε άσκηση συνδυάζει την ύλη πολλών εργαστηρίων (erg_1 έως erg_10) σε ένα ενιαίο πρόγραμμα κονσόλας και είναι αισθητά μεγαλύτερη από μία μεμονωμένη άσκηση των φυλλαδίων, ενώ παραμένει εφικτή μέσα σε λίγες ώρες. Οι ασκήσεις κλιμακώνονται σε δυσκολία με τη σειρά της ύλης: η 1η καλύπτει είσοδο/έξοδο, συνθήκες, επαναλήψεις και συναρτήσεις, η 2η πίνακες με συναρτήσεις, η 3η κλάσεις με ιδιωτικά πεδία και κατασκευαστές, και η 4η είναι μία τελική εργασία με μενού επιλογών που τα συνδυάζει όλα. Η 5η (Διαχείριση Φωτοβολταϊκού Πάρκου) καλύπτει ολόκληρη την ύλη σε ένα πρόγραμμα που θα μπορούσε να παραδοθεί ως εργασία εξαμήνου. Διαθέτει επίσης μία ενότητα προαιρετικών επεκτάσεων που ξεπερνούν την ύλη (π.χ. κληρονομικότητα, αρχεία, αναδρομή). Κάθε επέκταση είναι ξεχωριστό, σαφώς σημειωμένο ερώτημα, ώστε να μπορείτε να αφαιρέσετε όποια κρίνετε χωρίς να επηρεαστεί το υπόλοιπο. Οι ασκήσεις προορίζονται να συμπληρώσουν τα υπάρχοντα φυλλάδια erg_* και όχι να τα αντικαταστήσουν, και θα χαιρόμουν πολύ να ακούσω τις παρατηρήσεις σας πριν ξεκινήσω.
- Προγράμματα κονσόλας μόνο: είσοδος με
Scanner, έξοδος μεSystem.out. Δεν χρησιμοποιούνται γραφικό περιβάλλον (GUI), Greenfoot, αρχεία, δίκτυο ή νήματα. - Δεν χρησιμοποιούνται κληρονομικότητα, διεπαφές (interfaces), αφηρημένες κλάσεις ή πολυμορφισμός, γιατί δεν περιλαμβάνονται στην ύλη.
- Μοναδική εξαίρεση στους δύο παραπάνω κανόνες: η ενότητα «Επεκτάσεις εκτός ύλης» της Άσκησης 5 (ερωτήματα ε–ι), όπου εμφανίζονται
staticπεδία, ταξινόμηση/δυαδική αναζήτηση, επαυξημένηfor, κληρονομικότητα, αρχεία και αναδρομή. Κάθε τέτοιο ερώτημα σημειώνεται ρητά ως εκτός ύλης, είναι ανεξάρτητο από τα υπόλοιπα και μπορεί να αφαιρεθεί χωρίς να επηρεαστεί ο κορμός της άσκησης (ερωτήματα α–δ). - Τηρούνται οι συμβάσεις των φυλλαδίων: κύρια κλάση
Testμεpublic static void main(String[] args), άγκιστρο σε δική του γραμμή,Scanner input = new Scanner(System.in);, σύντομα ονόματα μεταβλητών (i,num,arr,cnt,sum,avg,max), συναρτήσεις πρόσβασης με ονόματα τύπουget_name(). - Κάθε κλάση γράφεται σε δικό της αρχείο (π.χ.
Load.java,Test.java), όπως αναφέρεται στοerg_8. - Μετά από
nextInt()/nextFloat()και πριν απόnextLine()καλείται ένα επιπλέονinput.nextLine();για να απομακρυνθεί ο χαρακτήρας νέας γραμμής, όπως στοerg_10. - Δεκαδικοί αριθμοί στην είσοδο: σε υπολογιστή με ελληνικές τοπικές ρυθμίσεις η
nextFloat()περιμένει κόμμα ως υποδιαστολή (π.χ.0,25). Στα παραδείγματα εκτέλεσης γράφεται τελεία, όπως στις αγγλικές ρυθμίσεις. - Μονάδες: όλα τα μηνύματα της κονσόλας αναγράφουν τη μονάδα (
V,A,W,Wp,kWp,VA,Ω,kWh,m2,€). Αν η κονσόλα δεν εμφανίζει σωστά τον χαρακτήραΩ, μπορεί να γραφτείOhm. - Εμφάνιση πραγματικών αριθμών: οι υπολογισμοί γίνονται με
floatχωρίς στρογγυλοποίηση. Μόνο κατά την εμφάνιση στρογγυλοποιούνται σε 2 ή 3 δεκαδικά με μία βοηθητική συνάρτηση (π.χ.Math.round(x * 100) / 100.0f), ώστε να μην εμφανίζονται αποτελέσματα όπως12.599999. - Σχόλια στον κώδικα (στα ελληνικά), όπως στα φυλλάδια.
- Τα κομμάτια που σημειώνονται ως Bonus χρησιμοποιούν υλικό που εμφανίζεται μόνο στις αγγλικές εργαστηριακές ενότητες (
ArrayList,try/catch) ή αποτελούν μικρή επέκταση της ύλης (πίνακες δύο διαστάσεων). Είναι προαιρετικά.
Ένα πρόγραμμα που αποκωδικοποιεί τον χρωματικό κώδικα αντιστάσεων, υπολογίζει ρεύμα και ισχύ για μία εφαρμοζόμενη τάση, ελέγχει αν ξεπερνιέται η ονομαστική ισχύς της αντίστασης και στο τέλος εμφανίζει συγκεντρωτικά στοιχεία.
#Μαθησιακοί στόχοι
| Ενότητα | Τι εξασκείται |
|---|---|
erg_1 | Scanner (nextInt, nextFloat, nextLine), ακέραια διαίρεση / και υπόλοιπο % για τον διαχωρισμό των ψηφίων του κώδικα |
erg_2 | Λογικοί τελεστές (&&, ||) και if-else για έλεγχο εγκυρότητας και για τον χαρακτηρισμό OK / OVERLOAD |
erg_3 | Επανάληψη for για τη δύναμη του 10 και while για την επεξεργασία πολλών αντιστάσεων |
erg_4 | do-while για επανάληψη εισόδου μέχρι να δοθεί έγκυρη τιμή, break για τερματισμό με τιμή-φρουρό |
erg_6 | Ορισμός και κλήση συναρτήσεων με παραμέτρους και τιμή επιστροφής |
| Αγγλικές ενότητες | switch, τριαδικός τελεστής ? :, σύγκριση String με .equals, Math.round |
#Εκφώνηση
Ο χρήστης πληκτρολογεί τον χρωματικό κώδικα μιας αντίστασης 4 ζωνών ως τριψήφιο ακέραιο, ένα ψηφίο για καθεμία από τις τρεις πρώτες ζώνες (η ζώνη ανοχής αγνοείται). Π.χ. ο κώδικας 472 αντιστοιχεί στις ζώνες κίτρινο–βιολετί–κόκκινο. Αν τα ψηφία είναι d1, d2, d3, η τιμή της αντίστασης είναι
R = (d1·10 + d2) · 10^d3 Ω, άρα το 472 δίνει 47 · 100 = 4700 Ω.
Χρώματα ψηφίων: 0 black, 1 brown, 2 red, 3 orange, 4 yellow, 5 green, 6 blue, 7 violet, 8 grey, 9 white. Έγκυρος είναι ένας κώδικας στο [100, 999] με d3 ≤ 6, ώστε η τιμή να χωράει σε int (οι ζώνες χρυσό/ασημί ως πολλαπλασιαστής δεν υποστηρίζονται). Για εφαρμοζόμενη τάση V, το ρεύμα είναι I = V / R και η ισχύς που καταναλώνεται είναι P = V · I. Η αντίσταση υπερφορτώνεται αν η P ξεπερνά την ονομαστική της ισχύ, που είναι μία από τις τιμές 0.25, 0.5, 1 ή 2 W.
α. Να γραφεί ένα πρόγραμμα το οποίο να διαβάζει τον χρωματικό κώδικα μιας αντίστασης, να ελέγχει ότι είναι έγκυρος (αλλιώς να εμφανίζει Invalid code), να διαχωρίζει τα τρία ψηφία με τους τελεστές / και % και να εμφανίζει την τιμή της αντίστασης σε Ω. Η δύναμη του 10 να υπολογίζεται με επανάληψη for και όχι με Math.pow.
β. Τροποποιήστε το πρόγραμμα ώστε η εγκυρότητα, η τιμή της αντίστασης και το όνομα του χρώματος κάθε ψηφίου να υπολογίζονται σε ξεχωριστές συναρτήσεις (βλ. Προδιαγραφές), με switch για τα χρώματα. Το πρόγραμμα να εμφανίζει τις τρεις ζώνες ολογράφως, να διαβάζει στη συνέχεια την τάση (V) και την ονομαστική ισχύ (W) και να εμφανίζει το ρεύμα, την ισχύ και το μήνυμα OK ή OVERLOAD (με if-else).
γ. Τροποποιήστε το πρόγραμμα ώστε να επεξεργάζεται συνεχώς αντιστάσεις, μέχρι ο χρήστης να εισάγει κώδικα 0. Για κάθε μη έγκυρη τιμή το πρόγραμμα να ζητά ξανά την ίδια τιμή (επανάληψη do-while) αντί να τερματίζει: κώδικα εκτός κανόνων (Invalid code), τάση ≤ 0 (Invalid voltage), ονομαστική ισχύ που δεν είναι μία από τις τέσσερις επιτρεπτές (Invalid rating). Όταν τελειώσει η εισαγωγή, να εμφανίζεται το πλήθος των αντιστάσεων, πόσες υπερφορτώθηκαν, η συνολική ισχύς και το μεγαλύτερο ρεύμα.
δ. Για κάθε αντίσταση το πρόγραμμα να ρωτά επιπλέον Series connection (y/n): και να διαβάζει την απάντηση ως String. Οι αντιστάσεις με απάντηση y θεωρείται ότι συνδέονται σε σειρά σε ένα ξεχωριστό κύκλωμα, οπότε η ισοδύναμη αντίστασή τους είναι το άθροισμά τους, R_eq = R1 + R2 + …. Η σύγκριση να γίνεται με .equals και η πρόσθεση στο R_eq με τον τριαδικό τελεστή ? :. Η τελική αναφορά να εμφανίζεται από μία συνάρτηση print_report και να περιλαμβάνει και το R_eq.
#Προδιαγραφές
Όλες οι συναρτήσεις είναι static μέσα στην κλάση Test.
| Συνάρτηση | Περιγραφή |
|---|---|
public static boolean is_valid_code(int code) | Επιστρέφει true αν 100 <= code <= 999 και το τελευταίο ψηφίο είναι ≤ 6. |
public static int resistance(int code) | Επιστρέφει την τιμή σε Ω, π.χ. resistance(472) επιστρέφει 4700. Η δύναμη του 10 υπολογίζεται με for. |
public static String colour(int d) | Επιστρέφει το όνομα του χρώματος για ψηφίο 0–9 με switch (π.χ. colour(4) επιστρέφει "yellow"). |
public static boolean is_valid_rating(float w) | Επιστρέφει true αν το w είναι 0.25, 0.5, 1 ή 2. Η σύγκριση με == είναι ασφαλής εδώ, γιατί οι τιμές αυτές αναπαρίστανται ακριβώς στο δυαδικό σύστημα. |
public static float round3(float x) | Επιστρέφει το x στρογγυλοποιημένο σε 3 δεκαδικά: Math.round(x * 1000) / 1000.0f. Χρησιμοποιείται μόνο για την εμφάνιση. |
public static void print_report(int cnt, int over, float sum_p, float max_i, int req) | (δ) Εμφανίζει την τελική αναφορά. Αν cnt == 0, εμφανίζει No resistors. |
Η ισχύς P υπολογίζεται από το μη στρογγυλοποιημένο ρεύμα I. Η τιμή R αποθηκεύεται σε int, ενώ τα V, I, P σε float.
import java.util.Scanner;
public class Test
{
public static void main(String[] args)
{
int code, r, cnt, over, req;
float v, w, cur, pw, sum_p, max_i;
String ans;
Scanner input = new Scanner(System.in);
// ...
}
public static boolean is_valid_code(int code)
{
// ...
}
public static int resistance(int code)
{
// ...
}
public static String colour(int d)
{
// ...
}
public static float round3(float x)
{
// ...
}
}
#Παράδειγμα εκτέλεσης (πλήρες πρόγραμμα, ερώτημα δ)
Enter colour code (0 to stop): 471
Bands: yellow violet brown R = 470 Ω
Enter voltage (V): 12
Enter power rating (0.25/0.5/1/2 W): 0.25
Series connection (y/n): y
I = 0.026 A P = 0.306 W OVERLOAD
Enter colour code (0 to stop): 478
Invalid code
Enter colour code (0 to stop): 102
Bands: brown black red R = 1000 Ω
Enter voltage (V): 5
Enter power rating (0.25/0.5/1/2 W): 3
Invalid rating
Enter power rating (0.25/0.5/1/2 W): 0.25
Series connection (y/n): y
I = 0.005 A P = 0.025 W OK
Enter colour code (0 to stop): 101
Bands: brown black brown R = 100 Ω
Enter voltage (V): 9
Enter power rating (0.25/0.5/1/2 W): 1
Series connection (y/n): n
I = 0.09 A P = 0.81 W OK
Enter colour code (0 to stop): 0
Resistors: 3 Overloaded: 1
Total power: 1.141 W Max current: 0.09 A
Series Req: 1470 Ω
Έλεγχος των αριθμών:
471: R = 47 · 10 = 470 Ω. I = 12 / 470 = 0.02553 A → 0.026. P = 12 · 0.02553 = 0.3064 W → 0.306, που είναι > 0.25 W, άρα OVERLOAD.478: d3 = 8 > 6, άρα μη έγκυρος κώδικας.102: R = 10 · 100 = 1000 Ω. I = 5 / 1000 = 0.005 A, P = 0.025 W ≤ 0.25 W, άρα OK.101: R = 10 · 10 = 100 Ω. I = 9 / 100 = 0.09 A, P = 0.81 W ≤ 1 W, άρα OK.- Σύνολο ισχύος = 0.3064 + 0.025 + 0.81 = 1.1414 W → 1.141. R_eq = 470 + 1000 = 1470 Ω (μόνο οι δύο πρώτες αντιστάσεις έχουν απάντηση
y).
#Bonus (προαιρετικό)
Ανθεκτική είσοδος με try/catch. Αν ο χρήστης πληκτρολογήσει κάτι που δεν είναι αριθμός (π.χ. 4k7), η nextInt() προκαλεί εξαίρεση και το πρόγραμμα τερματίζει. Γράψτε μία συνάρτηση public static int read_int(Scanner input, String msg) που εμφανίζει το μήνυμα msg, διαβάζει μία γραμμή με nextLine(), τη μετατρέπει με Integer.parseInt μέσα σε try/catch και, αν αποτύχει, εμφανίζει Not a number και ξαναρωτά. Χρησιμοποιήστε τη για την ανάγνωση του χρωματικού κώδικα. (Το try/catch εμφανίζεται μόνο στις αγγλικές ενότητες.)
#Κριτήρια αξιολόγησης
| Κριτήριο | Μονάδες |
|---|---|
α. Ανάγνωση και έλεγχος κώδικα, διαχωρισμός ψηφίων με / και %, δύναμη του 10 με for, εμφάνιση R | 20 |
β. Συναρτήσεις is_valid_code, resistance, colour (switch), round3, σωστά I, P και OK/OVERLOAD | 25 |
| γ. Συνεχής επεξεργασία, επανάληψη μη έγκυρης εισόδου, πλήθος/υπερφορτωμένες/συνολική ισχύς/μέγιστο ρεύμα | 25 |
δ. Σύνδεση σε σειρά με .equals και ? :, R_eq, print_report | 20 |
| Ποιότητα κώδικα (συμβάσεις, στοίχιση, σχόλια, ονόματα) | 10 |
| Σύνολο | 100 |
#Εκτιμώμενος χρόνος
3–4 ώρες (α–β: περίπου 1.5 ώρα).
Ένα πρόγραμμα που αποθηκεύει δείγματα τάσης από ένα τροφοδοτικό σε πίνακα και, με μία συνάρτηση για κάθε μέγεθος, υπολογίζει τα βασικά στατιστικά (ελάχιστο, μέγιστο, μέση τιμή, RMS, peak-to-peak), ελέγχει την ανοχή ±5%, βρίσκει τη μεγαλύτερη συνεχόμενη υπέρβαση και εξομαλύνει το σήμα με κινητό μέσο όρο.
#Μαθησιακοί στόχοι
| Ενότητα | Τι εξασκείται |
|---|---|
erg_5 | Πίνακας float[] μίας διάστασης: ανάγνωση, διάσχιση, ελάχιστο/μέγιστο/άθροισμα, μέτρηση με συνθήκη, δεύτερος πίνακας αποτελεσμάτων |
erg_7 | Μεταβίβαση πινάκων σε συναρτήσεις, συναρτήσεις που γράφουν αποτελέσματα σε πίνακα που τους δόθηκε |
erg_3, erg_4 | Επαναλήψεις, μετρητής συνεχόμενων στοιχείων που μηδενίζεται, επανάληψη εισόδου με do-while |
erg_6 | Συναρτήσεις που επιστρέφουν τιμή (float, int) |
erg_2 | Έλεγχος ορίων ανοχής με λογικούς τελεστές |
| Αγγλικές ενότητες | Math.sqrt, Math.round, Math.random(), μετατροπή τύπου (float), .equals |
#Εκφώνηση
Ένα πολύμετρο καταγράφει N δείγματα της τάσης εξόδου ενός τροφοδοτικού με ονομαστική τάση V_nom. Τα μεγέθη που ζητούνται:
- Μέση τιμή: V_mean = (v₀ + v₁ + … + v_{N−1}) / N
- Ενεργός τιμή (RMS): V_rms = √( (v₀² + v₁² + … + v_{N−1}²) / N ), με
Math.sqrt - Peak-to-peak: V_pp = V_max − V_min
- Ζώνη ανοχής ±5%: κάτω όριο 0.95 · V_nom, άνω όριο 1.05 · V_nom
α. Να γραφεί ένα πρόγραμμα το οποίο να διαβάζει το πλήθος των δειγμάτων N (3 ≤ N ≤ 100, αλλιώς να ζητείται ξανά) και στη συνέχεια N τιμές τάσης, τις οποίες να αποθηκεύει σε έναν πίνακα float. Το πρόγραμμα να εμφανίζει την ελάχιστη, τη μέγιστη και τη μέση τιμή, η καθεμία υπολογισμένη από ξεχωριστή συνάρτηση που δέχεται τον πίνακα.
β. Προσθέστε συναρτήσεις για την ενεργό τιμή (RMS) και την τιμή peak-to-peak. Επιπλέον, το πρόγραμμα να διαβάζει την ονομαστική τάση V_nom, να εμφανίζει τα όρια της ζώνης ανοχής και το πλήθος των δειγμάτων που βρίσκονται εκτός ζώνης (συνάρτηση count_outside).
γ. Προσθέστε μία συνάρτηση longest_run που βρίσκει τη μεγαλύτερη ακολουθία διαδοχικών δειγμάτων πάνω από το άνω όριο, δηλαδή τη μεγαλύτερη συνεχή υπέρταση. Η συνάρτηση γράφει το μήκος της ακολουθίας και τη θέση από την οποία ξεκινά (αρίθμηση από το 0) σε έναν πίνακα δύο θέσεων int[] res που της μεταβιβάζεται, γιατί μία συνάρτηση μπορεί να επιστρέψει μόνο μία τιμή. Αν δεν υπάρχει κανένα δείγμα πάνω από το όριο, το μήκος είναι 0 και εμφανίζεται No overvoltage. Υπόδειξη: ένας μετρητής που αυξάνεται όσο τα δείγματα είναι πάνω από το όριο και μηδενίζεται μόλις κάποιο δεν είναι.
δ. Προσθέστε εξομάλυνση με κινητό μέσο όρο παραθύρου 3. Η συνάρτηση moving_avg συμπληρώνει έναν δεύτερο πίνακα N−2 θέσεων με out[i] = (arr[i] + arr[i+1] + arr[i+2]) / 3 και ο πίνακας εμφανίζεται με τη συνάρτηση show. Επιπλέον, το πρόγραμμα να ρωτά στην αρχή Generate random samples (y/n): (σύγκριση με .equals). Αν η απάντηση είναι y, τα δείγματα παράγονται με Math.random() ομοιόμορφα στο διάστημα [0.92 · V_nom, 1.08 · V_nom] αντί να πληκτρολογούνται, και εμφανίζονται με τη show.
#Προδιαγραφές
Όλες οι συναρτήσεις είναι static μέσα στην κλάση Test. Το n είναι το πλήθος των δειγμάτων στον πίνακα.
| Συνάρτηση | Περιγραφή |
|---|---|
public static float get_min(float[] arr, int n) | Ελάχιστη τιμή. |
public static float get_max(float[] arr, int n) | Μέγιστη τιμή. |
public static float get_mean(float[] arr, int n) | Μέση τιμή. |
public static float get_rms(float[] arr, int n) | (β) Ενεργός τιμή: (float) Math.sqrt(sum_sq / n). |
public static float get_pp(float[] arr, int n) | (β) Peak-to-peak, καλώντας τις get_max και get_min. |
public static int count_outside(float[] arr, int n, float nom) | (β) Πλήθος δειγμάτων με τιμή < 0.95f * nom ή > 1.05f * nom. |
public static void longest_run(float[] arr, int n, float lim, int[] res) | (γ) Γράφει στο res[0] το μήκος της μεγαλύτερης ακολουθίας διαδοχικών δειγμάτων > lim και στο res[1] τη θέση έναρξής της (ή −1 αν το μήκος είναι 0). Σε ισοπαλία κρατά την πρώτη ακολουθία. |
public static void moving_avg(float[] arr, int n, float[] out) | (δ) Συμπληρώνει τις n − 2 θέσεις του out. |
public static void generate(float[] arr, int n, float nom) | (δ) Συμπληρώνει τον πίνακα με τυχαίες τιμές: nom * (0.92f + (float) Math.random() * 0.16f). |
public static void show(float[] arr, int n) | Εμφανίζει τις n πρώτες τιμές σε μία γραμμή, στρογγυλοποιημένες σε 2 δεκαδικά. |
public static float round2(float x) | Math.round(x * 100) / 100.0f, μόνο για την εμφάνιση. |
Κανόνες εγκυρότητας: N στο [3, 100] (απαιτούνται τουλάχιστον 3 δείγματα για το παράθυρο του κινητού μέσου όρου), V_nom > 0, δείγματα ≥ 0.
#Παράδειγμα εκτέλεσης (πλήρες πρόγραμμα, ερώτημα δ, χειροκίνητη εισαγωγή)
Τροφοδοτικό 12 V με ασταθή έξοδο:
Enter number of samples: 10
Enter nominal voltage (V): 12
Generate random samples (y/n): n
Enter sample 1 (V): 12.1
Enter sample 2 (V): 12.7
Enter sample 3 (V): 12.4
Enter sample 4 (V): 12.8
Enter sample 5 (V): 12.9
Enter sample 6 (V): 13.1
Enter sample 7 (V): 12.3
Enter sample 8 (V): 11.2
Enter sample 9 (V): 12.0
Enter sample 10 (V): 12.7
Min: 11.2 V Max: 13.1 V Pk-Pk: 1.9 V
Mean: 12.42 V RMS: 12.43 V
Limits: 11.4 V - 12.6 V Outside: 6
Longest overvoltage: 3 samples, start index 3
Moving avg (V): 12.4 12.63 12.7 12.93 12.77 12.2 11.83 11.97
Έλεγχος των αριθμών:
- Άθροισμα = 124.2 V, άρα V_mean = 12.42 V.
- Άθροισμα τετραγώνων = 146.41 + 161.29 + 153.76 + 163.84 + 166.41 + 171.61 + 151.29 + 125.44 + 144.00 + 161.29 = 1545.34. Μέσος όρος 154.534, άρα V_rms = √154.534 = 12.4312 → 12.43 V.
- V_pp = 13.1 − 11.2 = 1.9 V. Όρια ανοχής: 0.95 · 12 = 11.4 V και 1.05 · 12 = 12.6 V.
- Εκτός ζώνης: πάνω από 12.6 είναι οι θέσεις 1, 3, 4, 5, 9 και κάτω από 11.4 η θέση 7, σύνολο 6.
- Ακολουθίες πάνω από 12.6: {1} μήκους 1, {3, 4, 5} μήκους 3 και {9} μήκους 1. Η μεγαλύτερη έχει μήκος 3 και ξεκινά από τη θέση 3.
- Κινητός μέσος όρος: 37.2/3 = 12.4, 37.9/3 = 12.633, 38.1/3 = 12.7, 38.8/3 = 12.933, 38.3/3 = 12.767, 36.6/3 = 12.2, 35.5/3 = 11.833, 35.9/3 = 11.967.
#Bonus (προαιρετικό)
Πολλά κανάλια μέτρησης (πίνακας δύο διαστάσεων). Ένα σύστημα καταγραφής μετρά ταυτόχρονα ch κανάλια (π.χ. τις εξόδους +5 V, +12 V και −12 V ενός τροφοδοτικού υπολογιστή). Τα δείγματα αποθηκεύονται σε έναν πίνακα float[][] v = new float[ch][n]; και οι ονομαστικές τάσεις σε έναν πίνακα float[] nom = new float[ch];. Για κάθε κανάλι εμφανίστε μέση τιμή, RMS και πλήθος δειγμάτων εκτός ανοχής, χρησιμοποιώντας ξανά τις συναρτήσεις της άσκησης με όρισμα τη γραμμή v[k], και στο τέλος το κανάλι με τις περισσότερες παραβιάσεις. Για κανάλι με αρνητική ονομαστική τάση επιτρέπονται αρνητικά δείγματα, και η ζώνη ανοχής ορίζεται με βάση την απόλυτη τιμή (Math.abs). (Οι πίνακες δύο διαστάσεων αποτελούν μικρή επέκταση της ύλης.)
#Κριτήρια αξιολόγησης
| Κριτήριο | Μονάδες |
|---|---|
α. Ανάγνωση N και δειγμάτων με έλεγχο, get_min, get_max, get_mean με πίνακα ως όρισμα | 20 |
β. get_rms με Math.sqrt, get_pp, ζώνη ανοχής και count_outside | 25 |
γ. longest_run: σωστό μήκος και θέση έναρξης, περίπτωση «καμία υπέρβαση», αποτελέσματα μέσω int[] res | 25 |
δ. moving_avg σε δεύτερο πίνακα, show, τυχαία δείγματα με Math.random() και .equals | 20 |
| Ποιότητα κώδικα (συμβάσεις, σχόλια, ονόματα) | 10 |
| Σύνολο | 100 |
#Εκτιμώμενος χρόνος
4–5 ώρες (α–β: περίπου 2 ώρες).
Μια μικρή εφαρμογή που καταχωρεί τα φορτία ενός κυκλώματος μιας κατοικίας ως αντικείμενα, υπολογίζει ρεύμα και φαινόμενη ισχύ για κάθε φορτίο, επιλέγει την κατάλληλη τυποποιημένη ασφάλεια και δείχνει την επίδραση της διόρθωσης του συντελεστή ισχύος.
#Μαθησιακοί στόχοι
| Ενότητα | Τι εξασκείται |
|---|---|
erg_1 | Ανάγνωση String με nextLine() μετά από nextFloat() (απομάκρυνση της νέας γραμμής) |
erg_7 | Συναρτήσεις που δέχονται πίνακα (εδώ πίνακα αντικειμένων και πίνακα τυποποιημένων τιμών) |
erg_8 | Ορισμός κλάσης, δημιουργία αντικειμένων, πίνακας αντικειμένων |
erg_9 | Ιδιωτικά πεδία (ενθυλάκωση), συνάρτηση που αλλάζει πεδίο μόνο αν η νέα τιμή είναι έγκυρη |
erg_10 | Κατασκευαστές (υπερφορτωμένοι), συναρτήσεις πρόσβασης get_...() |
erg_3–erg_5 | Επαναλήψεις, άθροισμα, μέγιστο, αναζήτηση στον πίνακα των ασφαλειών |
| Αγγλικές ενότητες | Math.round, μετατροπή (float) |
#Εκφώνηση
Για ένα μονοφασικό φορτίο με ενεργό ισχύ P (W), τάση V (V) και συντελεστή ισχύος pf (0 < pf ≤ 1):
- Ρεύμα: I = P / (V · pf) (A)
- Φαινόμενη ισχύς: S = V · I = P / pf (VA)
Οι τυποποιημένες ασφάλειες (μικροαυτόματοι) του κυκλώματος είναι 6, 10, 16, 20, 25 και 32 A. Επιλέγεται η μικρότερη ασφάλεια με ονομαστικό ρεύμα μεγαλύτερο από το συνολικό ρεύμα.
Απλοποίηση: το συνολικό ρεύμα υπολογίζεται ως αριθμητικό άθροισμα των ρευμάτων των φορτίων. Επειδή ρεύματα με διαφορετικό συντελεστή ισχύος δεν είναι συμφασικά, το άθροισμα αυτό είναι άνω φράγμα του πραγματικού ρεύματος, δηλαδή συντηρητική εκτίμηση για την επιλογή ασφάλειας.
α. Να ορίσετε μία κλάση Load με ιδιωτικά πεδία: όνομα φορτίου, ενεργός ισχύς (W), τάση (V) και συντελεστής ισχύος. Η κλάση να περιέχει δύο δημόσιους κατασκευαστές: έναν που δέχεται και τα τέσσερα, και έναν που δέχεται όνομα, ισχύ και τάση και θέτει τον συντελεστή ισχύος ίσο με 1.0 (ωμικό φορτίο). Να προσθέσετε τις συναρτήσεις πρόσβασης get_...() για όλα τα πεδία. Να γραφεί ένα πρόγραμμα που να δημιουργεί ένα φορτίο με κάθε κατασκευαστή (με στοιχεία που διαβάζει από το πληκτρολόγιο) και να εμφανίζει τα στοιχεία τους καλώντας τις get_...().
β. Προσθέστε στην κλάση τις δημόσιες συναρτήσεις get_current() και get_apparent_power(), που υπολογίζουν τα I και S από τα πεδία (δεν αποθηκεύονται σε πεδία), καθώς και μία συνάρτηση set_pf(float f) που αλλάζει τον συντελεστή ισχύος μόνο αν 0 < f <= 1 και επιστρέφει true ή false ανάλογα με το αν έγινε η αλλαγή. Δοκιμάστε τις στο πρόγραμμα, και με μία μη έγκυρη τιμή στη set_pf.
γ. Τροποποιήστε το πρόγραμμα ώστε να διαβάζει ένα πλήθος φορτίων n (1 ≤ n ≤ 20), να δημιουργεί έναν πίνακα Load[] και να διαβάζει τα στοιχεία των n φορτίων. Αν ο χρήστης δώσει συντελεστή ισχύος 0, να χρησιμοποιείται ο κατασκευαστής με τρεις παραμέτρους (pf = 1.0). Αν δώσει τιμή εκτός του (0, 1], να εμφανίζεται Invalid pf και να ζητείται ξανά. Στη συνέχεια το πρόγραμμα να εμφανίζει έναν πίνακα με όνομα, P, I και S κάθε φορτίου (συνάρτηση show_all), τη συνολική ενεργό ισχύ, το συνολικό ρεύμα και το φορτίο με το μεγαλύτερο ρεύμα (συνάρτηση max_current).
δ. Το πρόγραμμα να επιλέγει την κατάλληλη τυποποιημένη ασφάλεια (συνάρτηση select_fuse, με τις τιμές σε έναν πίνακα int[] fuses = {6, 10, 16, 20, 25, 32};) και να εμφανίζει το ποσοστό φόρτισής της, δηλαδή (συνολικό ρεύμα / ονομαστικό ρεύμα ασφάλειας) · 100. Αν καμία ασφάλεια δεν επαρκεί, να εμφανίζεται No standard fuse - split the circuit. Στη συνέχεια να εμφανίζει τα φορτία με pf < 0.9 (Low pf) και να προσομοιώνει διόρθωση συντελεστή ισχύος: για αυτά τα φορτία καλεί set_pf(0.95f) και εμφανίζει το νέο συνολικό ρεύμα.
#Προδιαγραφές
public class Load
{
private String name;
private float power; // ενεργός ισχύς σε W
private float voltage; // τάση σε V
private float pf; // συντελεστής ισχύος, 0 < pf <= 1
public Load(String n, float p, float v, float f)
{
// ...
}
public Load(String n, float p, float v) // pf = 1.0
{
// ...
}
public String get_name()
{
// ...
}
public float get_power()
{
// ...
}
public float get_voltage()
{
// ...
}
public float get_pf()
{
// ...
}
public float get_current() // P / (V * pf), σε A
{
// ...
}
public float get_apparent_power() // P / pf, σε VA
{
// ...
}
public boolean set_pf(float f) // false αν f <= 0 ή f > 1
{
// ...
}
}
Συναρτήσεις static στην κλάση Test:
| Συνάρτηση | Περιγραφή |
|---|---|
public static void show_all(Load[] arr, int n) | Εμφανίζει κάθε φορτίο σε μία γραμμή: όνομα, P (W), I (A), S (VA), με 2 δεκαδικά. |
public static float total_power(Load[] arr, int n) | Άθροισμα των ενεργών ισχύων. |
public static float total_current(Load[] arr, int n) | Αριθμητικό άθροισμα των ρευμάτων (βλ. απλοποίηση). |
public static int max_current(Load[] arr, int n) | Επιστρέφει τη θέση του φορτίου με το μεγαλύτερο get_current(). |
public static int select_fuse(int[] fuses, float cur) | (δ) Επιστρέφει τη μικρότερη τιμή του fuses (ταξινομημένου σε αύξουσα σειρά) που είναι > cur, ή -1 αν δεν υπάρχει. |
public static float round2(float x) | Math.round(x * 100) / 100.0f, μόνο για την εμφάνιση. |
Κανόνες εγκυρότητας: n στο [1, 20], P > 0, V > 0, pf στο (0, 1] ή 0 για προεπιλογή. Προαιρετικά, η κλάση μπορεί να έχει και συνάρτηση public String toString() για την εμφάνιση ενός φορτίου.
#Παράδειγμα εκτέλεσης (ερωτήματα γ–δ)
Enter number of loads: 4
Enter name: Oven
Enter power (W): 2000
Enter voltage (V): 230
Enter power factor (0 for 1.0): 0
Enter name: Fridge
Enter power (W): 150
Enter voltage (V): 230
Enter power factor (0 for 1.0): 0.8
Enter name: Washer
Enter power (W): 2200
Enter voltage (V): 230
Enter power factor (0 for 1.0): 1.2
Invalid pf
Enter power factor (0 for 1.0): 0.9
Enter name: LED
Enter power (W): 100
Enter voltage (V): 230
Enter power factor (0 for 1.0): 0.95
Oven P: 2000.0 W I: 8.7 A S: 2000.0 VA
Fridge P: 150.0 W I: 0.82 A S: 187.5 VA
Washer P: 2200.0 W I: 10.63 A S: 2444.44 VA
LED P: 100.0 W I: 0.46 A S: 105.26 VA
Total P: 4450.0 W Total I: 20.6 A
Max current: Washer (10.63 A)
Fuse: 25 A Loading: 82.39%
Low pf: Fridge (0.8)
After correction to 0.95: Total I: 20.47 A
Έλεγχος των αριθμών:
- Oven: I = 2000 / (230 · 1.0) = 8.696 A → 8.7. S = 2000 VA.
- Fridge: I = 150 / (230 · 0.8) = 150 / 184 = 0.815 A → 0.82. S = 150 / 0.8 = 187.5 VA.
- Washer: I = 2200 / (230 · 0.9) = 2200 / 207 = 10.628 A → 10.63. S = 2200 / 0.9 = 2444.44 VA.
- LED: I = 100 / (230 · 0.95) = 100 / 218.5 = 0.458 A → 0.46. S = 100 / 0.95 = 105.26 VA.
- ΣP = 4450 W. ΣI = 8.696 + 0.815 + 10.628 + 0.458 = 20.597 A → 20.6. Η μικρότερη ασφάλεια > 20.6 A είναι τα 25 A, με φόρτιση 20.597 / 25 · 100 = 82.39%.
- Μόνο το Fridge έχει pf < 0.9 (το Washer έχει ακριβώς 0.9). Με pf = 0.95: I = 150 / 218.5 = 0.686 A, άρα νέο ΣI = 20.597 − 0.815 + 0.686 = 20.468 A → 20.47.
#Bonus (προαιρετικό)
Δυναμική λίστα φορτίων με ArrayList<Load>. Αντικαταστήστε τον πίνακα σταθερού μεγέθους με ArrayList<Load> και προσθέστε έναν βρόχο όπου ο χρήστης μπορεί να προσθέτει (add) ή να αφαιρεί (remove, με βάση το όνομα και .equals) φορτία. Μετά από κάθε αλλαγή επανυπολογίζονται το συνολικό ρεύμα και η ασφάλεια, και εμφανίζεται προειδοποίηση WARNING: over 80% of fuse rating όταν η φόρτιση ξεπερνά το 80%. Οι συναρτήσεις show_all, total_current και max_current να δέχονται πλέον ArrayList<Load> (χρήση size(), get(i)). (Το ArrayList εμφανίζεται μόνο στις αγγλικές ενότητες.)
#Κριτήρια αξιολόγησης
| Κριτήριο | Μονάδες |
|---|---|
α. Κλάση Load με ιδιωτικά πεδία, δύο κατασκευαστές, get_...() | 25 |
β. get_current(), get_apparent_power(), set_pf() με έλεγχο και τιμή επιστροφής | 15 |
γ. Πίνακας αντικειμένων, έλεγχοι εισόδου, επιλογή κατασκευαστή, show_all, σύνολα, max_current | 25 |
δ. select_fuse, ποσοστό φόρτισης, Low pf, διόρθωση με set_pf και νέο συνολικό ρεύμα | 25 |
| Ποιότητα κώδικα (ενθυλάκωση, συμβάσεις, σχόλια) | 10 |
| Σύνολο | 100 |
#Εκτιμώμενος χρόνος
4–5 ώρες (α–β: περίπου 1.5 ώρα).
Ένα πρόγραμμα με μενού επιλογών για έναν πάροχο ηλεκτρικής ενέργειας: καταχώριση μετρητών, καταχώριση ενδείξεων, υπολογισμός λογαριασμού με κλιμακωτά τιμολόγια ανά kWh, συναγερμός υψηλής κατανάλωσης και συγκεντρωτική αναφορά.
#Μαθησιακοί στόχοι
| Ενότητα | Τι εξασκείται |
|---|---|
erg_1 | Ποσά σε λεπτά του ευρώ (int) και εμφάνισή τους με / και % |
erg_2 | Έλεγχοι εγκυρότητας, σύνθετες συνθήκες για τα κλιμάκια του τιμολογίου |
erg_3, erg_4 | Μενού με do-while, επανάληψη εισόδου μέχρι να δοθεί έγκυρη ένδειξη |
erg_5 | Πίνακας με μετρητή πλήθους στοιχείων (cnt) που γεμίζει σταδιακά |
erg_6, erg_7 | Βοηθητικές συναρτήσεις static, μεταβίβαση πίνακα αντικειμένων σε συναρτήσεις |
erg_8–erg_10 | Δύο κλάσεις με ιδιωτικά πεδία, υπερφορτωμένοι κατασκευαστές, get_...(), συναρτήσεις που αλλάζουν κατάσταση με έλεγχο, πίνακας αντικειμένων |
| Αγγλικές ενότητες | switch για το μενού, τριαδικός τελεστής, μετατροπή (float) |
#Εκφώνηση
Κάθε μετρητής έχει μοναδικό κωδικό, όνομα ιδιοκτήτη, προηγούμενη και τρέχουσα ένδειξη σε ακέραιες kWh. Η κατανάλωση της περιόδου είναι E = τρέχουσα − προηγούμενη (kWh). Ο λογαριασμός υπολογίζεται με κλιμακωτό τιμολόγιο:
| Κλιμάκιο | kWh της περιόδου | Τιμή |
|---|---|---|
| 1 | οι πρώτες 100 kWh | 0.12 €/kWh |
| 2 | οι επόμενες 200 kWh (101–300) | 0.15 €/kWh |
| 3 | ό,τι ξεπερνά τις 300 kWh | 0.20 €/kWh |
| Πάγιο | ανά λογαριασμό | 4.00 € |
Π.χ. για E = 250 kWh: 100 · 0.12 + 150 · 0.15 + 4.00 = 38.50 €. Οι τιμές είναι ενδεικτικές και θεωρείται ότι περιλαμβάνουν όλες τις χρεώσεις.
Για να μην εμφανίζονται σφάλματα στρογγυλοποίησης των float, όλα τα ποσά αποθηκεύονται ως ακέραιοι σε λεπτά του ευρώ: οι τιμές ως 12, 15, 20 λεπτά/kWh και το πάγιο ως 400 λεπτά. Επειδή και οι kWh είναι ακέραιες, κάθε λογαριασμός βγαίνει ακριβώς σε ακέραια λεπτά και μετατρέπεται σε μορφή 38.50 μόνο κατά την εμφάνιση.
α. Να ορίσετε μία κλάση Meter με ιδιωτικά πεδία: κωδικός, όνομα ιδιοκτήτη, προηγούμενη ένδειξη, τρέχουσα ένδειξη. Η κλάση να έχει δύο κατασκευαστές: Meter(int c, String o, int r), για μετρητή που εγκαθίσταται με αρχική ένδειξη r, και Meter(int c, String o), για καινούργιο μετρητή με ένδειξη 0. Και στους δύο, η προηγούμενη και η τρέχουσα ένδειξη αρχικοποιούνται στην ίδια τιμή. Να προσθέσετε συναρτήσεις πρόσβασης και τη get_consumption(). Στο main να δημιουργηθεί ένας πίνακας Meter[] arr = new Meter[50]; με μετρητή cnt, και να υλοποιηθεί το μενού με do-while και switch, με επιλογές 1:Register 2:Reading 3:Consumption 4:Bill 5:Report 0:Exit. Σε αυτό το ερώτημα αρκεί να λειτουργούν η επιλογή 1 και η 0. Η επιλογή 1 καταχωρεί μετρητή με μοναδικό κωδικό (αλλιώς Code exists) και, αν η αρχική ένδειξη είναι 0, χρησιμοποιεί τον κατασκευαστή με δύο παραμέτρους. Για άγνωστη επιλογή να εμφανίζεται Wrong choice.
β. Υλοποιήστε την επιλογή 2 (νέα ένδειξη): το πρόγραμμα διαβάζει κωδικό μετρητή (αλλιώς Not found) και τη νέα ένδειξη. Αν η νέα ένδειξη είναι μικρότερη από την τρέχουσα, απορρίπτεται με μήνυμα που αναφέρει την τρέχουσα ένδειξη και ζητείται ξανά. Αλλιώς η τρέχουσα ένδειξη γίνεται προηγούμενη και η νέα γίνεται τρέχουσα (συνάρτηση new_reading της κλάσης). Υλοποιήστε επίσης την επιλογή 3 (κατανάλωση ανά μετρητή, συνάρτηση show_meters).
γ. Ορίστε μία δεύτερη κλάση Tariff (βλ. Προδιαγραφές), που περιέχει τα όρια των κλιμακίων, τις τιμές σε λεπτά και το πάγιο. Ο κατασκευαστής χωρίς παραμέτρους δημιουργεί το τιμολόγιο του πίνακα, ενώ ένας δεύτερος κατασκευαστής δέχεται όλες τις τιμές. Υλοποιήστε την επιλογή 4 (λογαριασμός): για έναν μετρητή εμφανίζονται οι kWh και το κόστος κάθε κλιμακίου, το πάγιο και το σύνολο, χρησιμοποιώντας τη συνάρτηση money για τη μορφοποίηση.
δ. Προσθέστε συναγερμό υψηλής κατανάλωσης: αν η κατανάλωση της περιόδου ξεπερνά το όριο των 500 kWh, εμφανίζεται ALARM: high consumption αμέσως μετά την καταχώριση της ένδειξης, και η ένδειξη ALARM δίπλα στον μετρητή στην επιλογή 3 (με τον τριαδικό τελεστή). Υλοποιήστε την επιλογή 5 (συγκεντρωτική αναφορά): πλήθος μετρητών, συνολική κατανάλωση, μέση κατανάλωση ανά μετρητή (με (float)), συνολική χρέωση όλων των λογαριασμών, ο μετρητής με τη μεγαλύτερη κατανάλωση (συνάρτηση max_consumer) και το πλήθος των μετρητών σε συναγερμό.
#Προδιαγραφές
public class Meter
{
private int code;
private String owner;
private int prev; // προηγούμενη ένδειξη (kWh)
private int curr; // τρέχουσα ένδειξη (kWh)
public Meter(int c, String o, int r)
{
// ...
}
public Meter(int c, String o) // καινούργιος μετρητής, ένδειξη 0
{
// ...
}
public int get_code()
{
// ...
}
public String get_owner()
{
// ...
}
public int get_prev()
{
// ...
}
public int get_curr()
{
// ...
}
public int get_consumption() // curr - prev
{
// ...
}
public boolean new_reading(int r) // false αν r < curr, αλλιώς prev = curr και curr = r
{
// ...
}
}
public class Tariff
{
private int limit1; // τέλος 1ου κλιμακίου (kWh), π.χ. 100
private int limit2; // τέλος 2ου κλιμακίου (kWh), π.χ. 300
private int rate1; // λεπτά/kWh
private int rate2;
private int rate3;
private int fixed; // πάγιο σε λεπτά
public Tariff() // 100, 300, 12, 15, 20, 400
{
// ...
}
public Tariff(int l1, int l2, int r1, int r2, int r3, int f)
{
// ...
}
public int get_rate(int t) // τιμή του κλιμακίου t (1, 2 ή 3), με switch
{
// ...
}
public int get_fixed()
{
// ...
}
public int tier_kwh(int kwh, int t) // πόσες από τις kwh ανήκουν στο κλιμάκιο t
{
// ...
}
public int get_cost(int kwh) // συνολικό κόστος σε λεπτά, μαζί με το πάγιο
{
// ...
}
}
Συναρτήσεις static στην κλάση Test:
| Συνάρτηση | Περιγραφή |
|---|---|
public static String money(int cents) | Επιστρέφει το ποσό σε μορφή ευρώ με δύο δεκαδικά, π.χ. money(3850) επιστρέφει "38.50" και money(12) επιστρέφει "0.12". Χρησιμοποιεί μόνο /, % και ένωση συμβολοσειρών (προσοχή στο μηδενικό μπροστά όταν τα λεπτά είναι < 10). |
public static int find_meter(Meter[] arr, int cnt, int code) | Επιστρέφει τη θέση του μετρητή ή -1. |
public static void show_meters(Meter[] arr, int cnt, int thr) | Εμφανίζει κωδικό, ιδιοκτήτη, προηγούμενη → τρέχουσα ένδειξη, κατανάλωση και ALARM αν η κατανάλωση είναι > thr. |
public static int max_consumer(Meter[] arr, int cnt) | (δ) Επιστρέφει τη θέση του μετρητή με τη μεγαλύτερη κατανάλωση. |
Κανόνες εγκυρότητας: επιλογή μενού στο {0, …, 5}, μοναδικοί κωδικοί, αρχική ένδειξη ≥ 0, νέα ένδειξη ≥ τρέχουσας, έλεγχος χωρητικότητας πριν την καταχώριση (Meters full). Αν δεν υπάρχουν μετρητές, οι επιλογές 3–5 εμφανίζουν No meters (αποφυγή διαίρεσης με το μηδέν στη μέση κατανάλωση).
#Παράδειγμα εκτέλεσης
1:Register 2:Reading 3:Consumption 4:Bill 5:Report 0:Exit
Choice: 1
Enter code: 1001
Enter owner: Papadopoulos
Enter initial reading (kWh): 5230
Meter 1001 registered
Choice: 1
Enter code: 1002
Enter owner: Nikolaou
Enter initial reading (kWh): 12000
Meter 1002 registered
Choice: 1
Enter code: 1001
Code exists
Choice: 2
Enter code: 1001
Enter new reading (kWh): 5180
Rejected: lower than current reading (5230 kWh)
Enter new reading (kWh): 5480
Consumption: 250 kWh
Choice: 2
Enter code: 1002
Enter new reading (kWh): 12620
Consumption: 620 kWh
ALARM: high consumption
Choice: 3
1001 Papadopoulos 5230 -> 5480 kWh : 250 kWh
1002 Nikolaou 12000 -> 12620 kWh : 620 kWh ALARM
Choice: 4
Enter code: 1001
Tier 1: 100 kWh x 0.12 = 12.00
Tier 2: 150 kWh x 0.15 = 22.50
Tier 3: 0 kWh x 0.20 = 0.00
Fixed: 4.00
Total: 38.50 €
Choice: 4
Enter code: 1002
Tier 1: 100 kWh x 0.12 = 12.00
Tier 2: 200 kWh x 0.15 = 30.00
Tier 3: 320 kWh x 0.20 = 64.00
Fixed: 4.00
Total: 110.00 €
Choice: 5
Meters: 2 Total: 870 kWh Avg: 435.0 kWh
Billing: 148.50 €
Max: 1002 Nikolaou (620 kWh)
Alarms: 1
Choice: 7
Wrong choice
Choice: 0
Bye
Έλεγχος των αριθμών:
- 1001: E = 5480 − 5230 = 250 kWh. Κλιμάκιο 1: 100 · 12 = 1200 λεπτά. Κλιμάκιο 2: 150 · 15 = 2250. Κλιμάκιο 3: 0. Πάγιο 400. Σύνολο 3850 λεπτά = 38.50 €.
- 1002: E = 12620 − 12000 = 620 kWh > 500, άρα συναγερμός. Κλιμάκιο 1: 1200. Κλιμάκιο 2: 200 · 15 = 3000. Κλιμάκιο 3: (620 − 300) · 20 = 6400. Πάγιο 400. Σύνολο 11000 λεπτά = 110.00 €.
- Αναφορά: 250 + 620 = 870 kWh, μέσος όρος 870 / 2 = 435.0 kWh, συνολική χρέωση 3850 + 11000 = 14850 λεπτά = 148.50 €.
#Bonus (προαιρετικό)
Απεριόριστοι μετρητές και μενού που δεν «σπάει». Αντικαταστήστε τον πίνακα Meter[] με ArrayList<Meter>, ώστε να μην υπάρχει όριο μετρητών, και γράψτε μία συνάρτηση public static int read_int(Scanner input, String msg) που διαβάζει μία γραμμή, τη μετατρέπει με Integer.parseInt μέσα σε try/catch και ξαναρωτά αν δεν δοθεί αριθμός. Χρησιμοποιήστε τη για όλες τις ακέραιες εισόδους, ώστε καμία λανθασμένη πληκτρολόγηση (π.χ. 12,5 σε ένδειξη) να μην τερματίζει το πρόγραμμα. (Τα ArrayList και try/catch εμφανίζονται μόνο στις αγγλικές ενότητες.)
#Κριτήρια αξιολόγησης
| Κριτήριο | Μονάδες |
|---|---|
α. Κλάση Meter (ιδιωτικά πεδία, δύο κατασκευαστές, get_...()), πίνακας μετρητών, μενού με do-while/switch, καταχώριση με έλεγχο μοναδικότητας | 25 |
β. Νέα ένδειξη με new_reading και απόρριψη μικρότερης ένδειξης, show_meters | 20 |
γ. Κλάση Tariff (δύο κατασκευαστές, tier_kwh, get_cost), money, λογαριασμός με ανάλυση κλιμακίων | 25 |
δ. Συναγερμός με τριαδικό τελεστή, συγκεντρωτική αναφορά με max_consumer και (float) | 20 |
| Ποιότητα κώδικα (οργάνωση σε συναρτήσεις, ενθυλάκωση, συμβάσεις, σχόλια) | 10 |
| Σύνολο | 100 |
#Εκτιμώμενος χρόνος
6–8 ώρες (α–β: περίπου 3 ώρες).
Ένα ολοκληρωμένο πρόγραμμα παρακολούθησης ενός πιλοτικού φωτοβολταϊκού πάρκου, για παράδοση ως εργασία εξαμήνου: απογραφή πάνελ και αντιστροφέων, ημερήσια παραγωγή ενέργειας ενός μήνα, δείκτης απόδοσης (Performance Ratio), ανίχνευση βλαβών από μετρήσεις τάσης/ρεύματος και έσοδα με εγγυημένη τιμή πώλησης. Η άσκηση δεν αντικαθιστά την Άσκηση 4. Είναι μεγαλύτερη και προαιρετική, και είναι η μόνη με ενότητα επεκτάσεων που ξεπερνούν την ύλη.
#Μαθησιακοί στόχοι
Ο κορμός της άσκησης (ερωτήματα α–δ) μένει εξ ολοκλήρου μέσα στην ύλη και χρησιμοποιεί και τα δέκα φυλλάδια.
| Ενότητα | Τι εξασκείται |
|---|---|
erg_1 | Είσοδος/έξοδος, ποσά σε λεπτά του ευρώ (int) και εμφάνισή τους με / και % |
erg_2 | Έλεγχοι εγκυρότητας, χαρακτηρισμός ημερών χαμηλής απόδοσης και πάνελ σε βλάβη |
erg_3 | Επαναλήψεις για ανάγνωση και διάσχιση πινάκων |
erg_4 | Μενού με do-while, επανάληψη εισόδου μέχρι να δοθεί έγκυρη τιμή, μετρητής συνεχόμενων ημερών |
erg_5 | Πίνακας int[] ημερήσιας ενέργειας: άθροισμα, μέσος όρος, μέγιστο/ελάχιστο, μέτρηση με συνθήκη |
erg_6 | Συναρτήσεις με παραμέτρους και τιμή επιστροφής για κάθε υπολογισμό |
erg_7 | Μεταβίβαση πινάκων (αριθμών και αντικειμένων) σε συναρτήσεις, αποτελέσματα μέσω πίνακα int[] res |
erg_8 | Δύο κλάσεις, πίνακες αντικειμένων |
erg_9 | Ιδιωτικά πεδία, συνάρτηση που αλλάζει πεδίο μόνο με έγκυρες τιμές |
erg_10 | Δύο κατασκευαστές ανά κλάση, συναρτήσεις πρόσβασης get_...() |
| Αγγλικές ενότητες | switch, τριαδικός τελεστής ? :, .equals, μετατροπή (float), Math.round |
#Εκφώνηση
Το πιλοτικό φωτοβολταϊκό πάρκο της Σχολής αποτελείται από πάνελ και αντιστροφείς (inverters). Οι σχέσεις που χρησιμοποιούνται είναι όλες επιπέδου πρώτου έτους:
- Ισχύς πάρκου: P_park (kWp) = Σ (ονομαστική ισχύς πάνελ σε Wp) / 1000.
- Εμβαδόν πάνελ: A = Wp / (1000 · η) (m²). Τα 1000 W/m² είναι η ακτινοβολία των πρότυπων συνθηκών δοκιμής (STC), και η η είναι ο βαθμός απόδοσης του πάνελ (π.χ. 0.21).
- Λόγος DC/AC: P_park (kWp) / Σ (ονομαστική ισχύς αντιστροφέων σε kW). Τιμές έως 1.3 θεωρούνται αποδεκτές.
- Ενέργεια = ισχύς × χρόνος. Η θεωρητική ημερήσια ενέργεια είναι P_park · PSH, όπου PSH οι ώρες πλήρους ηλιοφάνειας της ημέρας (peak sun hours, π.χ. 5 h). Η θεωρητική ενέργεια για n ημέρες είναι P_park · PSH · n (kWh).
- Δείκτης απόδοσης: PR = (πραγματική ενέργεια / θεωρητική ενέργεια) · 100 (%).
- Ημέρα χαμηλής απόδοσης: ημέρα με παραγωγή μικρότερη από το 70% της θεωρητικής ημερήσιας ενέργειας.
- Έλεγχος βλάβης: σε μία επιθεώρηση μετριούνται τάση V και ρεύμα I κάθε πάνελ, οπότε P = V · I (W). Ένα πάνελ θεωρείται σε βλάβη αν η P του είναι μικρότερη από το 80% της μέσης P όλων των πάνελ.
- Εκτίμηση ισχύος AC: P_AC ≈ (Σ P των πάνελ) · (μέσος βαθμός απόδοσης των αντιστροφέων).
- Έσοδα: ενέργεια (kWh) × εγγυημένη τιμή πώλησης (λεπτά/kWh).
Η ημερήσια ενέργεια καταγράφεται σε ακέραιες kWh (όπως τη δείχνει ο μετρητής) και τα ποσά σε ακέραια λεπτά, ώστε τα έσοδα να βγαίνουν ακριβώς, χωρίς σφάλματα στρογγυλοποίησης των float. Για τον υπολογισμό της ηλικίας των πάνελ, το τρέχον έτος δηλώνεται σε μία μεταβλητή του main (int now = 2026;).
α. Απογραφή. Να ορίσετε τις κλάσεις Panel και Inverter (βλ. Προδιαγραφές), με ιδιωτικά πεδία, δύο κατασκευαστές η καθεμία και συναρτήσεις πρόσβασης. Στο main να δημιουργηθούν οι πίνακες Panel[] pan = new Panel[50]; και Inverter[] inv = new Inverter[10]; με μετρητές np και ni, και να υλοποιηθεί το μενού με do-while και switch, με επιλογές 1:Add panel 2:Add inverter 3:Inventory 4:Daily energy 5:Report 6:Fault check 7:Revenue 0:Exit. Σε αυτό το ερώτημα αρκεί να λειτουργούν οι επιλογές 1, 2, 3 και 0:
- Επιλογή 1: καταχώριση πάνελ με μοναδικό κωδικό (αλλιώς
Code exists). Αν ο βαθμός απόδοσης δοθεί0, χρησιμοποιείται ο κατασκευαστής με τις προεπιλογές (η = 0.20, έτος εγκατάστασης = τρέχον) και δεν ζητείται έτος. - Επιλογή 2: καταχώριση αντιστροφέα. Αν ο βαθμός απόδοσης δοθεί
0, χρησιμοποιείται η προεπιλογή 0.97. - Επιλογή 3: απογραφή, δηλαδή κάθε πάνελ (κωδικός, Wp, η, ηλικία), κάθε αντιστροφέας και η συνολική ισχύς του πάρκου σε kWp.
- Για άγνωστη επιλογή εμφανίζεται
Wrong choice.
β. Ημερήσια παραγωγή. Υλοποιήστε την επιλογή 4: το πρόγραμμα διαβάζει το πλήθος των ημερών nd (1–31) και την ενέργεια κάθε ημέρας (ακέραιος ≥ 0, αλλιώς Invalid value και επανάληψη) σε έναν πίνακα int[] days = new int[31];. Αν έχουν ήδη καταχωριστεί μετρήσεις, ρωτά πρώτα Overwrite measurements (y/n): (σύγκριση με .equals). Υλοποιήστε επίσης την επιλογή 5 (μηνιαία αναφορά) με: πλήθος ημερών, συνολική ενέργεια, μέση ημερήσια ενέργεια (με (float)), καλύτερη και χειρότερη ημέρα. Οι ημέρες εμφανίζονται με αρίθμηση από το 1. Κάθε μέγεθος υπολογίζεται από ξεχωριστή συνάρτηση που δέχεται τον πίνακα. Με τα α–β το πρόγραμμα είναι ήδη πλήρες και λειτουργικό.
γ. Απόδοση και διαστασιολόγηση. Επεκτείνετε την επιλογή 5: το πρόγραμμα διαβάζει τις ώρες πλήρους ηλιοφάνειας PSH (h) και εμφανίζει τη θεωρητική ενέργεια, τον δείκτη απόδοσης PR, το πλήθος των ημερών χαμηλής απόδοσης και τη μεγαλύτερη σειρά διαδοχικών ημερών χαμηλής απόδοσης (μήκος και ημέρα έναρξης, μέσω πίνακα int[] res, όπως στην Άσκηση 2). Επεκτείνετε επίσης την επιλογή 3 με το εμβαδόν κάθε πάνελ και το συνολικό εμβαδόν, τον λόγο DC/AC (με προειδοποίηση Inverter undersized αν ξεπερνά το 1.3) και έναν έλεγχο καλωδίωσης: αν το άθροισμα των πάνελ που δηλώνουν οι αντιστροφείς διαφέρει από το πλήθος των καταχωρισμένων πάνελ, εμφανίζεται Wiring mismatch, αλλιώς Wiring OK.
δ. Βλάβες και έσοδα. Υλοποιήστε την επιλογή 6 (έλεγχος βλάβης): για κάθε πάνελ το πρόγραμμα διαβάζει V και I, τα αποθηκεύει με τη set_measurement της κλάσης (απορρίπτονται αρνητικές τιμές) και στη συνέχεια εμφανίζει για κάθε πάνελ την P, το ποσοστό της ως προς τη μέση P και την ένδειξη OK ή FAULT (με τον τριαδικό τελεστή). Τέλος εμφανίζει τη μέση P, το όριο του 80%, τη συνολική ισχύ DC και την εκτίμηση ισχύος AC. Υλοποιήστε και την επιλογή 7 (έσοδα): το πρόγραμμα διαβάζει την εγγυημένη τιμή πώλησης σε λεπτά/kWh και εμφανίζει τα έσοδα της περιόδου με τη συνάρτηση money. Αν δεν υπάρχουν πάνελ ή μετρήσεις, οι επιλογές 5–7 εμφανίζουν No data.
#Προδιαγραφές
public class Panel
{
private int code;
private int wp; // ονομαστική ισχύς (Wp)
private float eff; // βαθμός απόδοσης, 0 < eff <= 1
private int year; // έτος εγκατάστασης
private float p_meas; // τελευταία μετρούμενη ισχύς P = V * I (W)
public Panel(int c, int w, float e, int y)
{
// ...
}
public Panel(int c, int w, int now) // eff = 0.20, year = now
{
// ...
}
public int get_code()
{
// ...
}
public int get_wp()
{
// ...
}
public float get_eff()
{
// ...
}
public int get_year()
{
// ...
}
public float get_p_meas()
{
// ...
}
public int get_age(int now) // now - year
{
// ...
}
public float get_area() // wp / (1000 * eff), σε m2
{
// ...
}
public boolean set_measurement(float v, float i) // false αν v < 0 ή i < 0, αλλιώς p_meas = v * i
{
// ...
}
}
public class Inverter
{
private int code;
private float kw; // ονομαστική ισχύς AC (kW)
private float eff; // βαθμός απόδοσης, 0 < eff <= 1
private int panels; // πλήθος πάνελ που τον τροφοδοτούν
public Inverter(int c, float k, float e, int n)
{
// ...
}
public Inverter(int c, float k, int n) // eff = 0.97
{
// ...
}
public int get_code()
{
// ...
}
public float get_kw()
{
// ...
}
public float get_eff()
{
// ...
}
public int get_panels()
{
// ...
}
}
Ο δεύτερος κατασκευαστής της Panel δέχεται το τρέχον έτος ως παράμετρο, γιατί η κλάση δεν έχει άλλον τρόπο να το γνωρίζει.
Συναρτήσεις static στην κλάση Test:
| Συνάρτηση | Περιγραφή |
|---|---|
public static int find_panel(Panel[] arr, int n, int code) | Θέση του πάνελ ή -1 (γραμμική αναζήτηση). |
public static int find_inverter(Inverter[] arr, int n, int code) | Θέση του αντιστροφέα ή -1. |
public static void show_inventory(Panel[] pan, int np, Inverter[] inv, int ni, int now) | Εμφανίζει την απογραφή (α) και, από το γ, εμβαδόν, λόγο DC/AC και έλεγχο καλωδίωσης. |
public static float park_kwp(Panel[] arr, int n) | Συνολική ισχύς του πάρκου σε kWp. |
public static float total_inv_kw(Inverter[] arr, int n) | (γ) Άθροισμα ονομαστικής ισχύος των αντιστροφέων σε kW. |
public static int total_energy(int[] d, int n) | (β) Άθροισμα των n πρώτων ημερών. |
public static float mean_energy(int[] d, int n) | (β) Μέση ημερήσια ενέργεια, με (float). |
public static int best_day(int[] d, int n) | (β) Θέση (από το 0) της ημέρας με τη μεγαλύτερη ενέργεια. Σε ισοπαλία κρατά την πρώτη. |
public static int worst_day(int[] d, int n) | (β) Αντίστοιχα για τη μικρότερη ενέργεια. |
public static float perf_ratio(int total, float kwp, float psh, int n) | (γ) Δείκτης απόδοσης PR σε %. |
public static int count_below(int[] d, int n, float lim) | (γ) Πλήθος ημερών με ενέργεια < lim. |
public static void longest_below(int[] d, int n, float lim, int[] res) | (γ) res[0] = μήκος της μεγαλύτερης σειράς διαδοχικών ημερών < lim, res[1] = θέση έναρξης (ή −1). |
public static float mean_power(Panel[] arr, int n) | (δ) Μέση μετρούμενη ισχύς των πάνελ. |
public static float mean_inv_eff(Inverter[] arr, int n) | (δ) Μέσος βαθμός απόδοσης των αντιστροφέων. |
public static String money(int cents) | (δ) Ποσό σε μορφή 5.40, μόνο με /, % και ένωση συμβολοσειρών (όπως στην Άσκηση 4). |
public static float round2(float x) | Math.round(x * 100) / 100.0f, μόνο για την εμφάνιση. |
Κανόνες εγκυρότητας: μοναδικοί κωδικοί, Wp > 0, βαθμός απόδοσης στο (0, 1] ή 0 για προεπιλογή, έτος εγκατάστασης στο [1990, now], ισχύς αντιστροφέα > 0, πλήθος πάνελ ανά αντιστροφέα ≥ 1, ημέρες στο [1, 31], ενέργεια ημέρας ≥ 0, PSH > 0, τιμή πώλησης > 0, έλεγχος χωρητικότητας πριν από κάθε καταχώριση (Panels full / Inverters full).
#Παράδειγμα εκτέλεσης (κορμός α–δ)
1:Add panel 2:Add inverter 3:Inventory 4:Daily energy 5:Report 6:Fault check 7:Revenue 0:Exit
Choice: 1
Enter code: 101
Enter nominal power (Wp): 450
Enter efficiency (0 for default 0.20): 0.21
Enter installation year: 2023
Panel 101 added
Choice: 1
Enter code: 102
Enter nominal power (Wp): 450
Enter efficiency (0 for default 0.20): 0
Panel 102 added
Choice: 1
Enter code: 103
Enter nominal power (Wp): 450
Enter efficiency (0 for default 0.20): 0.21
Enter installation year: 2023
Panel 103 added
Choice: 1
Enter code: 104
Enter nominal power (Wp): 450
Enter efficiency (0 for default 0.20): 0.21
Enter installation year: 2023
Panel 104 added
Choice: 1
Enter code: 101
Code exists
Choice: 2
Enter code: 201
Enter nominal power (kW): 1.5
Enter efficiency (0 for default 0.97): 0
Enter number of panels: 4
Inverter 201 added
Choice: 3
101 450 Wp eff 0.21 age 3 y area 2.14 m2
102 450 Wp eff 0.2 age 0 y area 2.25 m2
103 450 Wp eff 0.21 age 3 y area 2.14 m2
104 450 Wp eff 0.21 age 3 y area 2.14 m2
Inverter 201 1.5 kW eff 0.97 panels 4
Park: 4 panels 1.8 kWp area 8.68 m2
DC/AC ratio: 1.2 Wiring OK
Choice: 4
Enter number of days (1-31): 7
Day 1 (kWh): 8
Day 2 (kWh): 7
Day 3 (kWh): -5
Invalid value
Day 3 (kWh): 5
Day 4 (kWh): 4
Day 5 (kWh): 7
Day 6 (kWh): 6
Day 7 (kWh): 8
Choice: 5
Enter peak sun hours (h): 5
Days: 7 Total: 45 kWh Mean: 6.43 kWh
Best: day 1 (8 kWh) Worst: day 4 (4 kWh)
Theoretical: 63.0 kWh PR: 71.43%
Low days (< 6.3 kWh): 3
Longest low run: 2 days from day 3
Choice: 6
Panel 101 V (V): 38
Panel 101 I (A): 10.5
Panel 102 V (V): 37.5
Panel 102 I (A): 10.4
Panel 103 V (V): 36
Panel 103 I (A): 6
Panel 104 V (V): 38.2
Panel 104 I (A): 10
101 P = 399.0 W 115.07% of mean OK
102 P = 390.0 W 112.47% of mean OK
103 P = 216.0 W 62.29% of mean FAULT
104 P = 382.0 W 110.17% of mean OK
Mean: 346.75 W Limit (80%): 277.4 W
DC total: 1387.0 W AC (est.): 1345.39 W
Choice: 7
Enter feed-in tariff (cents/kWh): 12
Revenue: 45 kWh x 0.12 = 5.40 €
Choice: 9
Wrong choice
Choice: 0
Bye
Έλεγχος των αριθμών:
- Ηλικία: 2026 − 2023 = 3 έτη για τα πάνελ με έτος 2023. Το 102 πήρε τις προεπιλογές (η = 0.20, έτος 2026), άρα ηλικία 0.
- Εμβαδόν: 450 / (1000 · 0.21) = 2.1429 m² → 2.14, και 450 / (1000 · 0.20) = 2.25 m². Σύνολο 3 · 2.1429 + 2.25 = 8.6786 m² → 8.68.
- P_park = 4 · 450 / 1000 = 1.8 kWp. DC/AC = 1.8 / 1.5 = 1.2 ≤ 1.3. Πάνελ που δηλώνει ο αντιστροφέας: 4 = καταχωρισμένα πάνελ, άρα
Wiring OK. - Ενέργεια: 8 + 7 + 5 + 4 + 7 + 6 + 8 = 45 kWh. Μέση 45 / 7 = 6.4286 → 6.43 kWh. Καλύτερη ημέρα 1 (8 kWh, πρώτη από τις δύο ισοπαλίες), χειρότερη ημέρα 4 (4 kWh).
- Θεωρητική ημερήσια ενέργεια 1.8 · 5 = 9.0 kWh, για 7 ημέρες 63.0 kWh. PR = 45 / 63 · 100 = 71.43%.
- Όριο χαμηλής απόδοσης 0.7 · 9.0 = 6.3 kWh. Κάτω από αυτό: ημέρες 3 (5), 4 (4) και 6 (6), σύνολο 3. Διαδοχικές: {3, 4} μήκους 2 και {6} μήκους 1, άρα η μεγαλύτερη σειρά έχει 2 ημέρες από την ημέρα 3.
- Ισχύς: 38 · 10.5 = 399.0 W, 37.5 · 10.4 = 390.0 W, 36 · 6 = 216.0 W, 38.2 · 10 = 382.0 W. Σύνολο 1387.0 W, μέση 1387 / 4 = 346.75 W, όριο 0.8 · 346.75 = 277.4 W. Μόνο το 103 (216 W) είναι κάτω από το όριο. Ποσοστά: 399 / 346.75 = 115.07%, 390 / 346.75 = 112.47%, 216 / 346.75 = 62.29%, 382 / 346.75 = 110.17%.
- P_AC ≈ 1387 · 0.97 = 1345.39 W (ένας αντιστροφέας, άρα μέσος βαθμός απόδοσης 0.97).
- Έσοδα: 45 kWh · 12 λεπτά = 540 λεπτά = 5.40 €.
#Bonus (προαιρετικό)
Απεριόριστος αριθμός πάνελ με ArrayList<Panel>. Αντικαταστήστε τον πίνακα Panel[] με ArrayList<Panel> και προσθέστε μία επιλογή μενού για την αφαίρεση πάνελ (π.χ. μετά από αντικατάσταση λόγω βλάβης) με βάση τον κωδικό. Οι συναρτήσεις που δέχονταν Panel[] να δέχονται πλέον ArrayList<Panel> (χρήση size(), get(i), add(...), remove(i)). (Το ArrayList εμφανίζεται μόνο στις αγγλικές ενότητες.)
#Επεκτάσεις εκτός ύλης (προαιρετικές)
Τα παρακάτω ερωτήματα ξεπερνούν την ύλη του μαθήματος και προτείνονται μόνο για φοιτητές που θέλουν να προχωρήσουν. Το καθένα είναι ανεξάρτητο από τα υπόλοιπα και στηρίζεται μόνο στον κορμό α–δ, οπότε μπορεί να αφαιρεθεί οποιοδήποτε χωρίς να επηρεαστούν τα άλλα.
ε. static πεδία. (Δεν περιλαμβάνεται στα φυλλάδια erg_*.) Προσθέστε στην κλάση Panel ένα πεδίο private int id; που αποδίδεται αυτόματα από τον κατασκευαστή, με αύξουσα αρίθμηση κοινή για όλα τα αντικείμενα: private static int next_id = 1;. Προσθέστε επίσης έναν private static int count = 0;, που αυξάνεται σε κάθε δημιουργία πάνελ, και μία public static int get_count(). Εμφανίστε το id στην απογραφή και το Panel.get_count() στο τέλος της. Εξηγήστε σε ένα σχόλιο γιατί το count πρέπει να είναι static: αν ήταν κανονικό πεδίο, κάθε αντικείμενο θα είχε το δικό του αντίγραφο, που θα έμενε πάντα 1.
στ. Ταξινόμηση και δυαδική αναζήτηση. (Δεν περιλαμβάνεται στα φυλλάδια erg_*.) Γράψτε μία public static void sort_panels(Panel[] arr, int n) που ταξινομεί τα πάνελ κατά κωδικό σε αύξουσα σειρά. Αρκεί οποιαδήποτε απλή ταξινόμηση O(n²), π.χ. φυσαλίδας ή επιλογής, που ανταλλάσσει τις αναφορές στα αντικείμενα. Στη συνέχεια γράψτε μία public static int binary_search(Panel[] arr, int n, int code). Μετρήστε σε έναν μετρητή πόσες συγκρίσεις κάνει για τον ίδιο κωδικό η δυαδική αναζήτηση και πόσες η γραμμική find_panel, και εμφανίστε τα δύο πλήθη. Σημειώστε ότι η δυαδική αναζήτηση προϋποθέτει ταξινομημένο πίνακα.
ζ. Επαυξημένη for (for-each). (Δεν περιλαμβάνεται στα φυλλάδια erg_*.) Ξαναγράψτε δύο διασχίσεις πινάκων με τη μορφή for (Panel p : arr), π.χ. τις park_kwp και mean_power. Σημειώστε σε σχόλιο τους περιορισμούς: (1) δεν υπάρχει δείκτης θέσης, οπότε δεν μπορεί να χρησιμοποιηθεί π.χ. στις best_day και find_panel, που επιστρέφουν θέση, (2) η ανάθεση στη μεταβλητή p δεν αλλάζει τον πίνακα, (3) σε πίνακα που είναι μόνο μερικώς γεμάτος (np < pan.length) η επανάληψη φτάνει και στις κενές θέσεις (null), οπότε χρειάζεται έλεγχος if (p == null) break;.
η. Κληρονομικότητα. (Δεν περιλαμβάνεται στα φυλλάδια erg_*. Είναι η φυσική αντικειμενοστρεφής λύση και αντικείμενο ενός δεύτερου μαθήματος προγραμματισμού.) Τα Panel και Inverter μοιράζονται κωδικό, ονομαστική ισχύ και βαθμό απόδοσης. Ορίστε μία βασική κλάση Device με αυτά τα πεδία και κάντε τις δύο κλάσεις να την επεκτείνουν (extends), καλώντας τον κατασκευαστή της με super(...). Κάθε κλάση υπερσκελίζει μία συνάρτηση show() (ή toString()) για να εμφανίζει τα δικά της στοιχεία. Η απογραφή γίνεται πλέον με έναν πίνακα Device[] dev, που περιέχει και τα δύο είδη, και καλεί απλώς dev[i].show().
public class Device
{
private int code;
private float power; // Wp για πάνελ, kW για αντιστροφέα
private float eff;
public Device(int c, float p, float e)
{
// ...
}
public int get_code()
{
// ...
}
public float get_power() /* Χρειάζεται, γιατί τα πεδία της Device είναι private: η show() της Panel δεν μπορεί να γράψει power, αλλά μπορεί να καλέσει get_power(). */
{
// ...
}
public float get_eff()
{
// ...
}
public void show()
{
// ...
}
}
public class Panel extends Device
{
private int year;
public Panel(int c, int w, float e, int y)
{
super(c, w, e);
// ...
}
public void show() // υπερσκέλιση
{
// ...
}
}
θ. Αρχεία. (Δεν περιλαμβάνεται στα φυλλάδια erg_*.) Προσθέστε στην επιλογή 4 τη δυνατότητα να διαβάζονται οι ημερήσιες μετρήσεις από ένα αρχείο κειμένου measurements.txt (μία τιμή kWh ανά γραμμή) με Scanner πάνω σε File (new Scanner(new File("measurements.txt")), με hasNextInt() και όριο 31 τιμών). Η ανάγνωση γίνεται μέσα σε try/catch για την FileNotFoundException (εμφανίζεται File not found και το πρόγραμμα συνεχίζει). Προσθέστε επίσης τη δυνατότητα να γράφεται η μηνιαία αναφορά στο αρχείο report.txt με PrintWriter (με close() στο τέλος). Απαιτείται import java.io.*;.
ι. Αναδρομή. (Δεν περιλαμβάνεται στα φυλλάδια erg_*.) Γράψτε μία αναδρομική public static int sum_rec(int[] d, int n) που επιστρέφει το άθροισμα των n πρώτων στοιχείων: 0 αν n == 0, αλλιώς sum_rec(d, n - 1) + d[n - 1]. Επιβεβαιώστε ότι δίνει το ίδιο αποτέλεσμα με την total_energy (45 kWh στο παράδειγμα). Εξηγήστε σε σχόλιο γιατί εδώ προτιμάται η επαναληπτική εκδοχή: κάθε κλήση δεσμεύει χώρο στη στοίβα και έχει επιπλέον κόστος. Για 31 ημέρες αυτό δεν έχει σημασία, αλλά για πολύ μεγάλους πίνακες προκαλεί StackOverflowError, ενώ η απλή επανάληψη είναι και πιο ευανάγνωστη.
#Κριτήρια αξιολόγησης
Ο κορμός (α–δ) βαθμολογείται με 100 μονάδες. Οι επεκτάσεις εκτός ύλης δίνουν έως +20 μονάδες πάνω από το 100, ώστε η αφαίρεση οποιασδήποτε επέκτασης να μην αλλάζει τη βαθμολογία του κορμού.
| Κριτήριο | Μονάδες |
|---|---|
α. Κλάσεις Panel και Inverter (ιδιωτικά πεδία, δύο κατασκευαστές, get_...()), πίνακες αντικειμένων, μενού με do-while/switch, καταχώριση με ελέγχους, απογραφή | 20 |
β. Ημερήσια παραγωγή σε int[] με ελέγχους και .equals, αναφορά με total_energy, mean_energy, best_day, worst_day | 20 |
γ. PR, ημέρες χαμηλής απόδοσης, longest_below, εμβαδόν, λόγος DC/AC, έλεγχος καλωδίωσης | 25 |
δ. Έλεγχος βλάβης με set_measurement, P = V · I, τριαδικός τελεστής, εκτίμηση AC, έσοδα σε λεπτά με money | 25 |
| Ποιότητα κώδικα (οργάνωση σε συναρτήσεις, ενθυλάκωση, συμβάσεις, σχόλια) | 10 |
| Σύνολο κορμού | 100 |
Επεκτάσεις εκτός ύλης (πρόσθετες μονάδες): ε. static πεδία +3, στ. ταξινόμηση και δυαδική αναζήτηση +4, ζ. for-each +2, η. κληρονομικότητα +5, θ. αρχεία +4, ι. αναδρομή +2 | έως +20 |
#Εκτιμώμενος χρόνος
10–15 ώρες συνολικά: περίπου 8–10 ώρες για τον κορμό α–δ (α–β: περίπου 4 ώρες) και 2–5 ώρες για όσες επεκτάσεις εκτός ύλης επιλεγούν.
● = κύριος στόχος της άσκησης, ○ = χρησιμοποιείται δευτερευόντως
| Άσκηση | erg_1 | erg_2 | erg_3 | erg_4 | erg_5 | erg_6 | erg_7 | erg_8 | erg_9 | erg_10 | Αγγλικές ενότητες |
|---|---|---|---|---|---|---|---|---|---|---|---|
| 1. Αντιστάσεις και νόμος του Ohm | ● | ● | ● | ● | ● | switch, ? :, .equals, Math.round · bonus: try/catch | |||||
| 2. Δειγματοληψία μετρήσεων τάσης | ○ | ○ | ○ | ● | ○ | ● | Math.sqrt, Math.random(), (float), .equals · bonus: πίνακας 2 διαστάσεων | ||||
| 3. Φορτία ηλεκτρικής εγκατάστασης | ○ | ○ | ○ | ○ | ○ | ○ | ● | ● | ● | ● | Math.round, (float) · bonus: ArrayList |
| 4. Έξυπνος μετρητής κατανάλωσης (τελική) | ● | ● | ○ | ● | ● | ● | ● | ● | ● | ● | switch, ? :, (float) · bonus: ArrayList, try/catch |
| 5. Διαχείριση Φωτοβολταϊκού Πάρκου (προαιρετική εργασία εξαμήνου) | ● | ● | ● | ● | ● | ● | ● | ● | ● | ● | switch, ? :, .equals, (float), Math.round · bonus: ArrayList |
Με λίγα λόγια: η Άσκηση 1 καλύπτει τα erg_1–erg_4 και erg_6, η Άσκηση 2 τα erg_5 και erg_7 (με τα erg_2–erg_4 και erg_6 δευτερευόντως), η Άσκηση 3 τα erg_7–erg_10, η Άσκηση 4 συνδυάζει όλα τα φυλλάδια από το erg_1 έως το erg_10, και η προαιρετική Άσκηση 5 τα χρησιμοποιεί όλα ως κύριο στόχο σε ένα πρόγραμμα μεγέθους εργασίας εξαμήνου.
Θέματα εκτός ύλης (μόνο στην Άσκηση 5, ερωτήματα ε–ι, όλα προαιρετικά και ανεξάρτητα μεταξύ τους): static πεδία (ε), ταξινόμηση και δυαδική αναζήτηση (στ), επαυξημένη for (ζ), κληρονομικότητα με extends/super/υπερσκέλιση (η), ανάγνωση/εγγραφή αρχείων με File, PrintWriter και try/catch (θ), αναδρομή (ι). Καμία από τις Ασκήσεις 1–4 δεν χρησιμοποιεί αυτά τα θέματα.