ΤΕΣΤ (ΑΣΚΗΣΕΙΣ) ONLINE
31 Tests / Διαγωνίσματα

Ενότητα 7 – Δραστηριότητες – Σελίδα 87-88 – ΠΛΗΡΟΦΟΡΙΚΗ Γ΄ ΓΥΜΝΑΣΙΟΥ – Απαντήσεις – Λύσεις

(Πηγή: Βιβλίο Μαθητή, Πληροφορική Γ΄ Γυμνασίου, Κεφάλαιο 7 «Επιστημονικός Προγραμματισμός» — ΤΕΛΕΥΤΑΙΟ κεφάλαιο του βιβλίου (94 σελίδες σύνολο) ΚΑΙ τελευταίο κεφάλαιο ολόκληρης της σειράς Πληροφορικής Γυμνασίου (Α΄+Β΄+Γ΄, 25/25 κεφάλαια), σελ. 87-88. Το κεφάλαιο (§7.1 Εισαγωγή, §7.2 Αλγοριθμική επίλυση εξισώσεων, §7.3 Η δύναμη της εκθετικής μετάδοσης) περιέχει ΑΚΡΙΒΩΣ 3 «Δραστηριότητα 1-3», όλες τοπικά αριθμημένα κουτιά ενσωματωμένα μέσα στη θεωρία της §7.2 — ξεχωριστές από την ήδη υπάρχουσα Φάση-1 lyseis του §7.4 «Άσκηση» (kefalaio-7.4_lyseis.json/.md, book_page 90). Στη σελ. 89 (εντός §7.3) υπάρχει ένα ακόμη κουτί με 6 αριθμημένες ερωτήσεις, ΧΩΡΙΣ όμως πλαίσιο/τίτλο «Δραστηριότητα» (επιβεβαιωμένο οπτικά) — άρα δεν καταγράφεται εδώ. Οι εκφωνήσεις είναι αυτούσιες από το σχολικό βιβλίο.)

Δραστηριότητες Κεφαλαίου 7

Δραστηριότητα 1 (τροποποίηση προγράμματος brute force για τρεις νέες εξισώσεις) (σελ. 87)

Τροποποιήστε το παραπάνω πρόγραμμα ώστε να λύνει τις εξισώσεις:

α) 26x – 260 = 0

β) 10x² – 50x + 60 = 0

γ) x³ – 9x² + 26x – 24 = 0

Σκέψη / Μεθοδολογία: «Το παραπάνω πρόγραμμα» αναφέρεται στο δεύτερο κουτί κώδικα της σελίδας (αυτό που λύνει σωστά την x²-5x+6=0 με τη μορφή if x*x-5*x+6==0:), όχι στο πρώτο παράδειγμα της Εικόνας 7.3 (10x+5=105) που στο τυπωμένο βιβλίο εμφανίζεται με το γνωστό σχήμα if 10*x+5==5: χωρίς να λύνει πραγματικά τη δική του εξίσωση 105 — και οι δύο Δραστηριότητες 1 και 2 ζητούν εξισώσεις ήδη διατυπωμένες στη μορφή «...=0», ίδια μορφή με το σωστό δεύτερο παράδειγμα. Η μέθοδος παραμένει η εξαντλητική αναζήτηση: δοκιμάζουμε κάθε ακέραιο x από 1 έως 1.000.000 και τυπώνουμε όσους μηδενίζουν το αριστερό μέλος της εξίσωσης. Και οι τρεις εξισώσεις εδώ έχουν θετικές ακέραιες λύσεις μέσα σε αυτό το εύρος, άρα το πρόγραμμα τις εντοπίζει κανονικά, χωρίς καμία τροποποίηση στο εύρος του βρόχου.

Λύση:

α) 26x – 260 = 0:

for x in range(1, 1000001):
    if 26*x - 260 == 0:
        print("x =", x)

Εκτέλεση: τυπώνει μία μόνο γραμμή, «x = 10» (επαληθεύεται άμεσα και με άλγεβρα: 26x=260 ⇒ x=10).

β) 10x² – 50x + 60 = 0:

for x in range(1, 1000001):
    if 10*x*x - 50*x + 60 == 0:
        print("x =", x)

Εκτέλεση: τυπώνει δύο γραμμές, «x = 2» και «x = 3» (η εξίσωση απλοποιείται σε x²-5x+6=0, δηλαδή (x-2)(x-3)=0 — ακριβώς το ήδη λυμένο παράδειγμα του κυρίως κειμένου, πολλαπλασιασμένο επί 10).

γ) x³ – 9x² + 26x – 24 = 0:

for x in range(1, 1000001):
    if x**3 - 9*x**2 + 26*x - 24 == 0:
        print("x =", x)

Εκτέλεση: τυπώνει τρεις γραμμές, «x = 2», «x = 3», «x = 4» (η εξίσωση παραγοντοποιείται ως (x-2)(x-3)(x-4)=0).

Και οι τρεις εκτελέσεις επαληθεύτηκαν με πραγματική εκτέλεση Python 3, με το ίδιο εύρος range(1, 1000001) που χρησιμοποιεί το βιβλίο — καμία λύση δεν χρειάστηκε αρνητικούς αριθμούς ή δεκαδικά, άρα το αρχικό πρόγραμμα δουλεύει αμετάβλητο εκτός από τη γραμμή της συνθήκης if.

Τελική απάντηση: α) 26x-260=0 → x = 10. β) 10x²-50x+60=0 → x = 2 και x = 3. γ) x³-9x²+26x-24=0 → x = 2, x = 3 και x = 4. Σε κάθε περίπτωση αλλάζει μόνο η γραμμή της συνθήκης if μέσα στον ίδιο βρόχο for x in range(1, 1000001), ώστε να ελέγχει το αριστερό μέλος της αντίστοιχης εξίσωσης (γραμμένης στη μορφή «...=0»).

Πρόσεξε:

  • Ο εκθέτης γράφεται στην Python με ** (π.χ. x**3, όχι x^3 όπως στα Μαθηματικά) — το x² του βιβλίου στη γλώσσα Python γίνεται x*x ή x**2.
  • Το εύρος range(1, 1000001) παραμένει το ίδιο σε όλα τα ερωτήματα, γιατί όλες οι ζητούμενες λύσεις είναι θετικοί ακέραιοι μέσα σε αυτό το διάστημα — δεν χρειάζεται καμία επέκταση του εύρους εδώ (σε αντίθεση με τη Δραστηριότητα 2).
  • Η εξίσωση (β) 10x²-50x+60=0 έχει τον συντελεστή 10 μπροστά, αλλά αυτό ΔΕΝ αλλάζει τις λύσεις της (x=2,3) σε σχέση με την απλοποιημένη x²-5x+6=0 — πολλαπλασιασμός και των δύο μελών με σταθερά δεν αλλάζει τις ρίζες.

Δραστηριότητα 2 (τέσσερις εξισώσεις χωρίς εντοπίσιμη ακέραια λύση στο brute force) (σελ. 87)

Τροποποιήστε το παραπάνω πρόγραμμα ώστε να λύνει τις εξισώσεις:

α) 4x + 20 = 0

β) 2x – 1 = 0

γ) 4x – 1 = 0

δ) x² – 2 = 0

Τι πρόβλημα παρατηρείτε κατά την εκτέλεσή τους; Γιατί συμβαίνει αυτό; Ποια λύση προτείνετε σε κάθε περίπτωση;

Σκέψη / Μεθοδολογία: Λύνουμε πρώτα κάθε εξίσωση με άλγεβρα, ΠΡΙΝ τρέξουμε το πρόγραμμα, ώστε να ξέρουμε τι να περιμένουμε: α) 4x=-20 ⇒ x=-5 (αρνητικός ακέραιος)· β) 2x=1 ⇒ x=0,5 (μη ακέραιος)· γ) 4x=1 ⇒ x=0,25 (μη ακέραιος)· δ) x²=2 ⇒ x=±√2≈±1,41421356 (άρρητος, δεν γράφεται ποτέ ακριβώς ως πεπερασμένο δεκαδικό). Το πρόγραμμα-πρότυπο ελέγχει ΜΟΝΟ ακέραιες τιμές x από 1 έως 1.000.000 (θετικές). Άρα ΚΑΜΙΑ από τις 4 πραγματικές λύσεις δεν μπορεί ποτέ να εντοπιστεί από αυτόν τον βρόχο, ανεξάρτητα από το πόσο τον επεκτείνουμε προς τα πάνω.

Λύση:

α) 4x+20=0:

for x in range(1, 1000001):
    if 4*x + 20 == 0:
        print("x =", x)

Εκτέλεση: ΔΕΝ τυπώνεται τίποτα. Η πραγματική λύση είναι x=-5, αρνητικός αριθμός, ενώ ο βρόχος range(1, 1000001) ελέγχει μόνο θετικούς ακέραιους (1,2,3,...,1000000) — ποτέ δεν φτάνει στο -5.

β) 2x-1=0:

for x in range(1, 1000001):
    if 2*x - 1 == 0:
        print("x =", x)

Εκτέλεση: ΔΕΝ τυπώνεται τίποτα. Η πραγματική λύση είναι x=0,5, δηλαδή δεκαδικός αριθμός· το range() παράγει ΜΟΝΟ ακέραιους (1,2,3,...), άρα το 0,5 δεν ελέγχεται ποτέ.

γ) 4x-1=0:

for x in range(1, 1000001):
    if 4*x - 1 == 0:
        print("x =", x)

Εκτέλεση: ΔΕΝ τυπώνεται τίποτα, για τον ίδιο λόγο — η λύση x=0,25 δεν είναι ακέραιος.

δ) x²-2=0:

for x in range(1, 1000001):
    if x*x - 2 == 0:
        print("x =", x)

Εκτέλεση: ΔΕΝ τυπώνεται τίποτα. Εδώ το πρόβλημα είναι ακόμα πιο θεμελιώδες: η λύση √2=1,41421356… είναι άρρητος αριθμός, δηλαδή έχει άπειρα μη περιοδικά δεκαδικά ψηφία — ΔΕΝ γράφεται ποτέ ακριβώς ούτε καν ως δεκαδικός αριθμός πεπερασμένης ακρίβειας, πόσο μάλλον ως ακέραιος.

Τι πρόβλημα παρατηρείτε / Γιατί συμβαίνει αυτό (κοινή διάγνωση και στις 4 περιπτώσεις): ο βρόχος for x in range(1, 1000001) ελέγχει ΜΟΝΟ θετικούς ακέραιους αριθμούς. Όποτε η πραγματική λύση μιας εξίσωσης είναι αρνητική, δεκαδική ή άρρητη, το πρόγραμμα δεν θα την εντοπίσει ΠΟΤΕ, όσο μεγάλο εύρος κι αν δοκιμάσουμε προς τα πάνω — το πρόβλημα δεν είναι το «πλήθος» των δοκιμών αλλά το «είδος» των τιμών που δοκιμάζονται.

Ποια λύση προτείνετε σε κάθε περίπτωση:

  • α) Επεκτείνουμε το εύρος ώστε να περιλαμβάνει και αρνητικούς ακέραιους, π.χ. for x in range(-1000000, 1000001).
  • β)/γ) Ο βρόχος πρέπει να δοκιμάζει και δεκαδικούς αριθμούς με ένα μικρό βήμα (π.χ. x = x + 0.1 σε βρόχο while) και να ελέγχει την ισότητα με ανοχή σφάλματος (π.χ. abs(2*x - 1) < 0.0001) αντί για ==, αφού η ακριβής αριθμητική ισότητα με δεκαδικά είναι αναξιόπιστη λόγω σφαλμάτων στρογγυλοποίησης (floating point).
  • δ) Επειδή η λύση είναι άρρητη, καμία εξαντλητική αναζήτηση (ούτε με ακέραιους ούτε με δεκαδικό βήμα) δεν θα δώσει ποτέ την ΑΚΡΙΒΗ τιμή — προτείνεται είτε αλγεβρική επίλυση (x = math.sqrt(2) στην Python) είτε αναζήτηση με ανοχή σφάλματος που θα δώσει μια ΠΡΟΣΕΓΓΙΣΤΙΚΗ λύση (π.χ. x=1,41421356, που ικανοποιεί την εξίσωση με πολύ μικρό σφάλμα).

Τελική απάντηση: Καμία από τις 4 εξισώσεις δεν εμφανίζει καμία λύση όταν τρέξουμε το πρόγραμμα, γιατί ο βρόχος for x in range(1, 1000001) δοκιμάζει ΜΟΝΟ θετικούς ακέραιους αριθμούς, ενώ οι πραγματικές λύσεις είναι: α) x=-5 (αρνητικός), β) x=0,5 (μη ακέραιος), γ) x=0,25 (μη ακέραιος), δ) x=±√2≈±1,4142 (άρρητος). Προτεινόμενες λύσεις: για την (α) επεκτείνουμε το εύρος ώστε να περιλαμβάνει αρνητικούς αριθμούς· για τις (β)/(γ) δοκιμάζουμε δεκαδικές τιμές με μικρό βήμα και ελέγχουμε ισότητα με ανοχή σφάλματος (abs(...) < όριο) αντί για ==· για τη (δ) καμία εξαντλητική αναζήτηση δεν βρίσκει ποτέ την ακριβή άρρητη λύση — χρειάζεται αλγεβρική επίλυση (π.χ. math.sqrt) ή αποδοχή προσεγγιστικής λύσης με ανοχή σφάλματος.

Πρόσεξε:

  • Το «καμία έξοδος» ΔΕΝ σημαίνει ότι το πρόγραμμα έχει σφάλμα (bug) — εκτελείται κανονικά, απλώς καμία τιμή του εύρους δεν ικανοποιεί ποτέ τη συνθήκη, οπότε το σώμα του if δεν εκτελείται καμία φορά.
  • Η χρήση == για σύγκριση δεκαδικών αριθμών είναι γενικά επικίνδυνη πρακτική στον προγραμματισμό, ακόμα κι όταν θεωρητικά «θα έπρεπε» να ταιριάζουν, λόγω της πεπερασμένης ακρίβειας αναπαράστασης των δεκαδικών (floating point) στον υπολογιστή.
  • Η πρόταση λύσης είναι εξ ορισμού ενδεικτική/ανοιχτού τύπου — υπάρχουν κι άλλες αποδεκτές τεχνικές (π.χ. αλγεβρική επίλυση με τον τύπο των ριζών αντί για brute force, χρήση της βιβλιοθήκης sympy), αρκεί να εξηγούν σωστά γιατί αποτυγχάνει η αρχική μέθοδος.

Δραστηριότητα 3 (γιατί το άνω όριο είναι 1.000.001 + εκτέλεση τριών προγραμμάτων range()) (σελ. 88)

Να διερευνήσετε γιατί στα παραπάνω προγράμματα στη γλώσσα Python η εντολή επανάληψης έχει άνω όριο 1.000.001 και όχι 1.000.000.

Στο περιβάλλον προγραμματισμού Thonny ή σε κάποιον online διερμηνευτή της Python δοκιμάστε να εκτελέσετε τα παρακάτω προγράμματα και σχολιάστε τα αποτελέσματά τους.

for x in range(1, 1000001):
    if x*x-5*x+6 == 0:
        print( "x = ", x )

for x in range(5, 12):
    print( "x = ", x )

for x in range(12):
    print( "x = ", x )

Σκέψη / Μεθοδολογία: Το κλειδί είναι ότι στην Python η συνάρτηση range(αρχή, τέλος) παράγει τιμές από το «αρχή» ΜΕΧΡΙ ΚΑΙ το «τέλος-1» — το δεξί όριο ΔΕΝ συμπεριλαμβάνεται ποτέ (είναι «ανοιχτό» διάστημα στα δεξιά). Άρα αν θέλουμε ο βρόχος να ελέγξει και τον ακέραιο 1.000.000 ο ίδιος, πρέπει το δεύτερο όρισμα να είναι 1.000.001 (1.000.000+1), όχι 1.000.000. Με ένα μόνο όρισμα, το range(N) ισοδυναμεί με range(0, N) — δηλαδή ξεκινά αυτόματα από το 0.

Λύση:

Επαλήθευση με κώδικα:

print(1000000 in range(1, 1000001))   # True
print(1000000 in range(1, 1000000))   # False

Το τελευταίο στοιχείο του range(1, 1000001) είναι πράγματι το 1.000.000, ενώ το τελευταίο στοιχείο του range(1, 1000000) θα ήταν το 999.999 — δηλαδή αν το βιβλίο είχε γράψει 1.000.000 ως άνω όριο, ο αριθμός 1.000.000 ΠΟΤΕ δεν θα ελεγχόταν από το πρόγραμμα, και μια εξίσωση με μοναδική λύση ακριβώς x=1.000.000 δεν θα εντοπιζόταν.

Πρόγραμμα 1for x in range(1, 1000001): if x*x-5*x+6==0: print("x = ", x). Εκτέλεση (πραγματική, Thonny/Python 3): τυπώνει ακριβώς δύο γραμμές:

x =  2
x =  3

Πρόκειται για την ίδια εξίσωση x²-5x+6=0 του κυρίως κειμένου της §7.2, με τις ήδη γνωστές λύσεις 2 και 3.

Πρόγραμμα 2for x in range(5, 12): print("x = ", x). Εκτέλεση: τυπώνει 7 γραμμές, τις τιμές 5, 6, 7, 8, 9, 10, 11 (από το 5 μέχρι το 11, αφού το 12 αποκλείεται ως δεξί όριο).

Πρόγραμμα 3for x in range(12): print("x = ", x). Εκτέλεση: τυπώνει 12 γραμμές, τις τιμές 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 (η μονή παράμετρος 12 παίζει τον ρόλο του «τέλος», ενώ η «αρχή» παίρνει αυτόματα την προεπιλεγμένη τιμή 0 — γι' αυτό η λίστα ξεκινά από το 0, όχι το 1).

Σύγκριση Προγραμμάτων 2 και 3: το range(5,12) και το range(12) έχουν το ΙΔΙΟ δεξί όριο (12, αποκλειστικό) αλλά διαφορετική αρχή (5 έναντι της προεπιλογής 0) — αυτό εξηγεί γιατί το Πρόγραμμα 3 έχει 5 περισσότερες γραμμές εξόδου από το Πρόγραμμα 2 (τις τιμές 0,1,2,3,4 που προηγούνται του 5).

Τελική απάντηση: Το άνω όριο είναι 1.000.001 (όχι 1.000.000) επειδή το range(αρχή, τέλος) στην Python ΑΠΟΚΛΕΙΕΙ πάντα το δεύτερο όριο· άρα, για να ελεγχθεί ο ίδιος ο αριθμός 1.000.000, το όρισμα «τέλος» πρέπει να είναι 1.000.001. Εκτέλεση προγραμμάτων: το 1ο τυπώνει «x = 2» και «x = 3» (λύσεις της x²-5x+6=0)· το 2ο [range(5,12)] τυπώνει τους αριθμούς 5 έως 11· το 3ο [range(12)] τυπώνει τους αριθμούς 0 έως 11, επειδή η μονή παράμετρος του range() ορίζει μόνο το τέλος και η αρχή γίνεται αυτόματα 0.

Πρόσεξε:

  • Δεν πρόκειται για ιδιοτροπία ή σύμβαση του βιβλίου — είναι θεμελιώδης κανόνας της γλώσσας Python: range(a, b) παράγει πάντα a, a+1, …, b-1, ΠΟΤΕ το ίδιο το b.
  • Το range(N) με μία μόνο παράμετρο ΔΕΝ σημαίνει «από το 1 έως το Ν» αλλά «από το 0 έως το Ν-1» — συνηθισμένη πηγή λαθών (off-by-one) σε αρχάριους προγραμματιστές.
  • Το ίδιο μοτίβο του «+1» στο άνω όριο θα το ξαναδούμε στη §7.3 (εκθετική μετάδοση ιού) όποτε θέλουμε ο βρόχος να «αγγίξει» ακριβώς μια συγκεκριμένη οριακή τιμή.
 ΣΧΟΛΙΚΟ ΒΙΒΛΙΟ