מתכונת 6 במדעי המחשב - יסודות מדעי המחשב 899371
1–2 יח"ל · שאלון 899371 · 6 שאלות · 2:30 שעות · פתרון מלא לכל סעיף
מבחן מתכונת במדעי המחשב, יסודות מדעי המחשב, במבנה של בחינת הבגרות. כל שאלה בו בנויה כמו שאלה מבגרות אחרת. לכל סעיף יש תשובה ופתרון מלא, וכל קוד מופיע ב-Java וב-C#. פתרו בזמן של הבחינה, ורק אחר כך פתחו את הפתרונות.
הוראות הבחינה
א. משך הבחינה: שעתיים וחצי.
ב. מבנה השאלון ומפתח ההערכה: בשאלון זה שני פרקים.
פרק ראשון – (20×2) – 40 נקודות
פרק שני – (30×2) – 60 נקודות
סך הכול – 100 נקודות
ג. חומר עזר מותר בשימוש: כל חומר עזר, חוץ ממחשבון שיש בו אפשרות תכנוּת.
ד. הוראה מיוחדת:
את כל התוכניות שיש לכתוב בשפת מחשב כתבו בשפה אחת בלבד – Java או C#.
הערה: לא יוּרדוּ נקודות אם תכתבו בתוכניות אות גדולה במקום אות קטנה או להפך.
יש לכתוב במחברת הבחינה בלבד. יש לרשום "טיוטה" בראש כל עמוד המשמש טיוטה.
כתיבת טיוטה בדפים שאינם במחברת הבחינה עלולה לגרום לפסילת הבחינה.
השאלות בשאלון זה מנוסחות בלשון רבים, אף על פי כן על כל תלמידה וכל תלמיד להשיב עליהן באופן אישי.
בהצלחה!
השאלות
בשאלון זה שני פרקים.
יש לענות על שאלות משני הפרקים, לפי ההוראות בכל פרק.
הערה: בכל שאלה שנדרשת בה קליטה, אין צורך לבדוק את תקינות הקלט.
לפותרים בשפת Java: בכל שאלה שנדרשת בה קליטה, הניחו שבתוכנית כתובה ההוראה:
Scanner scan = new Scanner (System.in);
פרק ראשון (40 נקודות)
ענו על שתיים מן השאלות 1–3 (לכל שאלה – 20 נקודות).
שאלה 1 · מעקב אחר פעולה על מערך – הרצף העולה הארוך ביותר (20 נקודות)
לפניכם הפעולה what בשפת Java או What בשפת C# :
Java
public static int what(int[] arr)
{
int c = 1;
int m = 1;
for (int i = 1; i < arr.length; i++)
{
if (arr[i] > arr[i - 1])
c++;
else
c = 1;
m = Math.max(m, c);
}
return m;
}C#
public static int What(int[] arr)
{
int c = 1;
int m = 1;
for (int i = 1; i < arr.Length; i++)
{
if (arr[i] > arr[i - 1])
c++;
else
c = 1;
m = Math.Max(m, c);
}
return m;
}נתון המערך arr :
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | |
|---|---|---|---|---|---|---|---|---|---|
| arr | 6 | 8 | 3 | 5 | 5 | 7 | 9 | 12 | 4 |
עקבו בעזרת טבלת מעקב אחר הפעולה what(arr) בשפת Java או What(arr) בשפת C#, וכתבו מה הפעולה מחזירה.
בטבלת המעקב יש לכלול עמודה בעבור כל אחד מן המשתנים האלה: i , c , m .
◂ פתרון מלא לשאלה 1
פתרון
הפעולה מחזירה 4 (אורכו של הרצף הארוך ביותר של איברים צמודים שעולים ממש: 5, 7, 9, 12).
מה נדרש
לעקוב אחר ביצוע הפעולה על המערך {6, 8, 3, 5, 5, 7, 9, 12, 4} בטבלת מעקב עם העמודות i, c, m, ולכתוב את הערך שהיא מחזירה.
רעיון
פתרון – טבלת מעקב
בכל שורה רשומים ערכי c ו-m בסוף הסיבוב.
| שלב | i | arr[i - 1] | arr[i] | arr[i] > arr[i - 1] ? | c | m |
|---|---|---|---|---|---|---|
| אתחול | – | – | – | – | 1 | 1 |
| 1 | 1 | 6 | 8 | כן | 2 | 2 |
| 2 | 2 | 8 | 3 | לא | 1 | 2 |
| 3 | 3 | 3 | 5 | כן | 2 | 2 |
| 4 | 4 | 5 | 5 | לא (שווים) | 1 | 2 |
| 5 | 5 | 5 | 7 | כן | 2 | 2 |
| 6 | 6 | 7 | 9 | כן | 3 | 3 |
| 7 | 7 | 9 | 12 | כן | 4 | 4 |
| 8 | 8 | 12 | 4 | לא | 1 | 4 |
| סיום | 9 | – | – | – | 1 | 4 |
כאשר i = 9 התנאי i < 9 אינו מתקיים, הלולאה מסתיימת והפעולה מחזירה את m.
הפעולה מחזירה 4.
(הסבר נוסף: הפעולה מחזירה את אורכו של הרצף הארוך ביותר של איברים צמודים שכל אחד מהם גדול ממש מקודמו. כאן זה הרצף 5, 7, 9, 12 שבתאים 4–7.)
בדיקה
על המערך הנתון מודפס 4. הדפסת c ו-m בסוף כל סיבוב נתנה בדיוק את השורות שבטבלה. בדיקות נוספות: {1, 2, 3} ← 3, {3, 2, 1} ← 1, {4} ← 1.
טעות נפוצה: להמשיך את הרצף כשיש שני איברים שווים (5, 5 בתאים 3–4). התנאי הוא > ולא >=, ולכן בשלב 4 c חוזר ל-1, והרצף 3, 5, 5, 7, 9, 12 אינו נספר כרצף אחד באורך 6.
שאלה 2 · קו מילוי בקבוקים (20 נקודות)
במפעל משקאות ממלאים בקבוקים בקו מילוי אוטומטי.
הפרמטר target הוא מספר שלם חיובי המייצג את כמות המשקה הרצויה במ"ל בכל בקבוק.
הפרמטר tol הוא מספר שלם חיובי המייצג את הסטייה המותרת במ"ל מן הכמות הרצויה.
כתבו פעולה חיצונית ששמה getCheck בשפת Java או GetCheck בשפת C# המקבלת מספר שלם חיובי – volume , המייצג את כמות המשקה במ"ל בבקבוק מסוים, ואת הפרמטרים target ו־ tol .
הפעולה תחזיר מספר שלם על פי התנאים האלה:
– אם כמות המשקה בבקבוק קטנה מן הכמות הרצויה ביותר מ־ tol מ"ל, הפעולה תחזיר 1 (יש להשלים את המילוי).
– אם כמות המשקה בבקבוק גדולה מן הכמות הרצויה ביותר מ־ tol מ"ל, הפעולה תחזיר 2 (הבקבוק נפסל).
– אם שני התנאים שלעיל אינם מתקיימים, הפעולה תחזיר 0 (הבקבוק תקין).
המערך volumes הוא מערך מטיפוס שלם המכיל את כמויות המשקה במ"ל בבקבוקים שמולאו בקו (כל תא במערך מציג את הכמות בבקבוק אחר). המערך אינו מסודר בסדר מסוים.
כתבו פעולה חיצונית ששמה getAllChecks בשפת Java או GetAllChecks בשפת C# המקבלת את המערך volumes ואת הפרמטרים target ו־ tol .
הפעולה תחזיר מערך חדש מטיפוס שלם בגודל זהה למערך volumes , המציג בכל תא את המספר 0 או 1 או 2 בהתאם לבקבוק שנמצא באותו האינדקס במערך volumes (על פי התנאים בסעיף א).
דוגמה: בעבור target = 500 , tol = 5 , והמערך volumes :
| אינדקס | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
|---|---|---|---|---|---|---|---|
| volumes | 503 | 490 | 506 | 495 | 500 | 512 | 494 |
יוחזר המערך שלהלן:
| אינדקס | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
|---|---|---|---|---|---|---|---|
| 0 | 1 | 2 | 0 | 0 | 2 | 1 |
הערות: – יש להשתמש בפעולה שכתבתם בסעיף א.
– אין לשנות את המערך volumes .
מנהל הקו מקבל התראה אם פחות משלושה רבעים מן הבקבוקים תקינים.
כתבו פעולה חיצונית ששמה lineAlert בשפת Java או LineAlert בשפת C# , המקבלת את המערך volumes ואת הפרמטרים target ו־ tol .
הפעולה תחזיר true אם פחות משלושה רבעים מן הבקבוקים במערך volumes תקינים, אחרת הפעולה תחזיר false .
דוגמה: בעבור המערך הנתון בסעיף ב הפעולה תחזיר true , כי מבין שבעת הבקבוקים במערך רק שלושה תקינים (הבקבוקים באינדקסים 0 , 3 , 4), ושלושה רבעים מ־ 7 הם 5.25 .
הערה: יש להשתמש בפעולה שכתבתם בסעיף ב.
◂ פתרון מלא לשאלה 2
סעיף א
volume < target − tol → 1 ; volume > target + tol → 2 ; אחרת 0
מה נדרש: פעולה המסווגת בקבוק אחד: 1 (חסר), 2 (עודף – נפסל) או 0 (תקין).
רעיון: "קטנה ביותר מ־ tol מן הכמות הרצויה" פירושו target − volume > tol, כלומר volume < target − tol. באותו אופן "גדולה ביותר מ־ tol" פירושו volume > target + tol. בודקים את שני התנאים, ואם אף אחד מהם אינו מתקיים – מחזירים 0.
פתרון: הקוד המלא – בלשונית Java / C#.
if (volume < target - tol) return 1;
if (volume > target + tol) return 2;
return 0;בדיקה: target = 500, tol = 5: עבור 494 → 1, עבור 495 → 0 (סטייה של 5 בדיוק מותרת), עבור 505 → 0, עבור 506 → 2.
טעות נפוצה: לכתוב ≤ במקום < (או ≥ במקום >) – אז בקבוק עם סטייה של tol בדיוק, שהוא תקין, יסווג בטעות כחסר או כעודף.
public static int getCheck(int volume, int target, int tol)
{
if (volume < target - tol)
return 1;
if (volume > target + tol)
return 2;
return 0;
}סעיף ב
result[i] = getCheck(volumes[i], target, tol) לכל i, במערך חדש
מה נדרש: מערך חדש באותו גודל, שבו כל תא הוא תוצאת getCheck על התא המתאים ב־ volumes.
רעיון: יוצרים מערך result בגודל volumes.length ועוברים בלולאה על כל האינדקסים: result[i] = getCheck(volumes[i], target, tol). המערך volumes רק נקרא – לא משנים אותו.
פתרון: הקוד המלא – בלשונית Java / C#.
בדיקה: על המערך מהדוגמה (target = 500, tol = 5) התקבל [0, 1, 2, 0, 0, 2, 1], כמו בשאלה, והמערך volumes נשאר ללא שינוי.
public static int[] getAllChecks(int[] volumes, int target, int tol)
{
int[] result = new int[volumes.length];
for (int i = 0; i < volumes.length; i++)
{
result[i] = getCheck(volumes[i], target, tol);
}
return result;
}סעיף ג
סופרים תאים השווים ל־ 0 במערך מ־ getAllChecks ומחזירים ok * 4 < volumes.length * 3
מה נדרש: להחזיר true אם פחות משלושה רבעים מן הבקבוקים תקינים.
רעיון: בקבוק תקין בדיוק כאשר הערך שלו במערך מ־ getAllChecks הוא 0. סופרים את התאים השווים ל־ 0 (ok) ובודקים ok < ¾·length. כדי להימנע משברים ומחילוק שלמים כופלים את שני האגפים ב־ 4: ok * 4 < volumes.length * 3.
פתרון: הקוד המלא – בלשונית Java / C#.
בדיקה: בדוגמה 3 תקינים מתוך 7: 3·4 = 12 < 21 → true. נבדקו גם: 3 מתוך 4 → false (12 < 12 אינו מתקיים), 2 מתוך 4 → true, 6 מתוך 8 → false, 5 מתוך 7 → true (20 < 21).
טעות נפוצה: לכתוב ok < volumes.length * 3 / 4 – בחילוק שלמים 7·3 / 4 = 21 / 4 = 5, ואז 5 תקינים מתוך 7 (פחות מ־ 5.25) יחזירו false בטעות.
public static boolean lineAlert(int[] volumes, int target, int tol)
{
int[] checks = getAllChecks(volumes, target, tol);
int ok = 0;
for (int i = 0; i < checks.length; i++)
{
if (checks[i] == 0)
ok++;
}
return ok * 4 < volumes.length * 3;
}שאלה 3 · סיסמאות בספרייה – מערך סוגי תווים (20 נקודות)
בספריית בית הספר "דפים" כל תלמיד בוחר סיסמה לחשבון ההשאלה שלו. סיסמה מורכבת מאותיות באנגלית (קטנות או גדולות), מספרות ומתווים אחרים (כמו ! # $ _), ואין בה רווחים.
סיסמה חזקה היא סיסמה שאורכה 8 תווים לפחות, יש בה לפחות 2 ספרות, ויש בה לפחות תו אחר אחד (תו שאינו אות ואינו ספרה).
הערה: אין צורך לבדוק את תקינות הקלט.
לפותרים בשפת Java: הניחו שבתוכנית כתובה ההוראה:Scanner input = new Scanner(System.in);
כתבו פעולה בשם analyze (ב־ C# – Analyze) שתקבל מחרוזת pw המייצגת סיסמה.
הפעולה תחזיר מערך חד־ממדי של מספרים שלמים בגודל 3: בתא 0 – מספר האותיות בסיסמה, בתא 1 – מספר הספרות בסיסמה, ובתא 2 – מספר התווים האחרים בסיסמה.
לדוגמה, בעבור הסיסמה "Gal#2026" הפעולה תחזיר את המערך: 3,4,1
כתבו פעולה בשם checkPasswords (ב־ C# – CheckPasswords) שתקלוט את הסיסמאות שבחרו התלמידים.
הקלט מסתיים כאשר נקלטת סיסמה שאין בה אף אות.
הפעולה תדפיס את מספר הסיסמאות החזקות ואת מספר הסיסמאות שאינן חזקות. הסיסמה שבה הסתיים הקלט אינה נספרת.
דוגמה להדפסה: 4,9
ניתן להשתמש בפעולה שכתבתם בסעיף 1.
◂ פתרון מלא לשאלה 3
סעיף 1
int[3]; לכל תו: אות ⇒ kinds[0]++, ספרה ⇒ kinds[1]++, אחרת ⇒ kinds[2]++; מחזירים kinds.
מה נדרש: פעולה שמקבלת מחרוזת ומחזירה מערך בגודל 3: מספר האותיות (תא 0), מספר הספרות (תא 1) ומספר התווים האחרים (תא 2).
רעיון: עוברים על כל תווי המחרוזת. לכל תו בודקים לאיזה סוג הוא שייך בעזרת השוואת תווים: אות – בין 'a' ל־'z' או בין 'A' ל־'Z'; ספרה – בין '0' ל־'9'; אחרת – תו אחר. כאן מתאים if – else if – else, כי כל תו שייך לסוג אחד בדיוק. מגדילים את התא המתאים.
פתרון: הקוד המלא – בלשונית Java/C#. (ב־Java: pw.length() ו־pw.charAt(i); ב־C#: pw.Length ו־pw[i].)
בדיקה:
| pw | המערך המוחזר |
|---|---|
| Gal#2026 | 3,4,1 |
| x1!y2? | 2,2,2 |
| Shir_Lev99 | 7,2,1 |
| 12345 | 0,5,0 |
| #$% | 0,0,3 |
טעות נפוצה: לבדוק רק אותיות קטנות, או לכתוב c >= 0 && c <= 9 (מספרים) במקום '0' ו־'9' (תווים).
public static int[] analyze(String pw)
{
int[] kinds = new int[3]; // כל התאים מאותחלים ל-0
for (int i = 0; i < pw.length(); i++)
{
char c = pw.charAt(i);
if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'))
kinds[0]++; // אות
else if (c >= '0' && c <= '9')
kinds[1]++; // ספרה
else
kinds[2]++; // תו אחר
}
return kinds;
}סעיף 2
לולאת זקיף: כל עוד analyze(pw)[0] > 0 – אם length >= 8 וגם kinds[1] >= 2 וגם kinds[2] >= 1 אז strong++ אחרת weak++; מדפיסים strong,weak.
מה נדרש: לקלוט סיסמאות עד סיסמה שאין בה אף אות, ולהדפיס כמה מהסיסמאות שלפניה חזקות וכמה אינן חזקות.
רעיון: לולאת זקיף שהתנאי שלה נבדק בעזרת analyze:
ב־Java קולטים מחרוזת בלי רווחים עם input.next(); ב־C# עם Console.ReadLine().
פתרון: הקוד המלא – בלשונית Java/C#.
בדיקה: קלט: Gal#2026, tamar1234, Noa!5, Yoni$77$x, pass, 2580.
הפלט: 2,3. כשהסיסמה הראשונה היא #$% מודפס 0,0.
טעות נפוצה: לבדוק את התנאי של while על סיסמה שעוד לא נקלטה, או לספור גם את הסיסמה שעצרה את הקלט.
public static void checkPasswords()
{
int strong = 0, weak = 0;
String pw = input.next();
int[] kinds = analyze(pw);
while (kinds[0] > 0)
{
if (pw.length() >= 8 && kinds[1] >= 2 && kinds[2] >= 1)
strong++;
else
weak++;
pw = input.next();
kinds = analyze(pw);
}
System.out.println(strong + "," + weak);
}פרק שני (60 נקודות)
ענו על שתיים מן השאלות 4–6 (לכל שאלה – 30 נקודות).
שאלה 4 · חממה – רצפי שעות חמות וקרות
נתונה המחלקה Greenhouse , המייצגת חממה לגידול ירקות. בחממה מותקן חיישן המודד את הטמפרטורה פעם בשעה.
למחלקה ארבע תכונות:
הניחו שקיימות פעולות get/Get ו־ set/Set עבור תכונות המחלקה.
"שעה חמה" היא שעה שבה הטמפרטורה שנמדדה גבוהה מ־ high , ו"שעה קרה" היא שעה שבה הטמפרטורה שנמדדה נמוכה מ־ low .
"רצף" הוא סדרה של שעות הסמוכות זו לזו (תאים סמוכים במערך).
כתבו פעולה פנימית ששמה longestRun בשפת Java או LongestRun בשפת C# , המקבלת פרמטר – hot מטיפוס בוליאני, ומחזירה מספר שלם בהתאם לאפיון שלפניכם:
אם אין אף שעה מהסוג המבוקש, הפעולה תחזיר 0 .
לדוגמה, עבור חממה שבה low = 18 , high = 30 והמערך temps שלפניכם, אם hot הוא true הפעולה תחזיר 3 , ואם hot הוא false הפעולה תחזיר 2 .
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | |
|---|---|---|---|---|---|---|---|---|---|---|
| temps | 17 | 16 | 22 | 31 | 33 | 32 | 25 | 15 | 29 | 31 |
הסבר: התאים באינדקסים 9 , 5 , 4 , 3 מייצגים "שעות חמות". הרצף הארוך ביותר שלהן הוא באינדקסים 5–3 , ואורכו 3 .
התאים באינדקסים 7 , 1 , 0 מייצגים "שעות קרות". הרצף הארוך ביותר שלהן הוא באינדקסים 1–0 , ואורכו 2 .
כתבו פעולה פנימית ששמה isSafe בשפת Java או IsSafe בשפת C# , המחזירה true אם בחממה לא היה רצף של יותר משתי "שעות חמות" וגם לא היה רצף של יותר משתי "שעות קרות". אחרת הפעולה תחזיר false .
למשל, בדוגמה שלעיל הפעולה תחזיר false , כיוון שהיה רצף של 3 "שעות חמות".
חובה להיעזר בפעולה שכתבתם בסעיף א.
◂ פתרון מלא לשאלה 4
סעיף א
סורקים את המערך עם מונה לרצף הנוכחי: אם השעה מהסוג המבוקש (hot: temps[i] > high, אחרת temps[i] < low) מגדילים את המונה ומעדכנים מקסימום; אחרת מאפסים את המונה. בדוגמה: 3 ו־2.
מה נדרש: פעולה פנימית במחלקה Greenhouse שמחזירה את אורך הרצף הארוך ביותר של "שעות חמות" (hot = true) או של "שעות קרות" (hot = false).
רעיון: זו בעיית "רצף מקסימלי". מחזיקים שני משתנים:
count – אורך הרצף הנוכחי של שעות מהסוג המבוקש;max – אורך הרצף הארוך ביותר שנמצא עד כה.עוברים על כל התאים. שעה מהסוג המבוקש היא temps[i] > high כאשר hot הוא true, ו־temps[i] < low כאשר hot הוא false. אם השעה מהסוג המבוקש – מגדילים את count ובודקים אם הוא גדול מ־max; אחרת הרצף נקטע ומאפסים את count. אם אין אף שעה מתאימה, max נשאר 0. כיוון שהפעולה פנימית, ניגשים ישירות לתכונות temps , low , high.
פתרון: הקוד המלא – בלשונית Java/C# (שתי הפעולות של סעיפים א ו־ב כתובות בתוך המחלקה Greenhouse).
בדיקה: בדוגמה (low = 18, high = 30):
| i | temps[i] | חמה? | count (hot) | max (hot) | קרה? | count (cold) | max (cold) |
|---|---|---|---|---|---|---|---|
| 0 | 17 | – | 0 | 0 | כן | 1 | 1 |
| 1 | 16 | – | 0 | 0 | כן | 2 | 2 |
| 2 | 22 | – | 0 | 0 | – | 0 | 2 |
| 3 | 31 | כן | 1 | 1 | – | 0 | 2 |
| 4 | 33 | כן | 2 | 2 | – | 0 | 2 |
| 5 | 32 | כן | 3 | 3 | – | 0 | 2 |
| 6 | 25 | – | 0 | 3 | – | 0 | 2 |
| 7 | 15 | – | 0 | 3 | כן | 1 | 2 |
| 8 | 29 | – | 0 | 3 | – | 0 | 2 |
| 9 | 31 | כן | 1 | 3 | – | 0 | 2 |
מתקבל longestRun(true) = 3 ו־longestRun(false) = 2, כמו בשאלה. נבדק גם מערך שכולו "קר" (10 , 10 , 10 → 3, הרצף מסתיים בסוף המערך) ומערך עם טמפרטורות השוות בדיוק ל־low ול־high (18 , 30 , 24 → 0 לשני הסוגים).
טעות נפוצה: לעדכן את max רק כשהרצף נקטע – אז רצף שמסתיים בתא האחרון לא נבדק. כאן max מתעדכן בכל הגדלה של count, ולכן אין בעיה.
// סעיף א
public int longestRun(boolean hot)
{
int max = 0; // הרצף הארוך ביותר שנמצא עד כה
int count = 0; // אורך הרצף הנוכחי
for (int i = 0; i < temps.length; i++)
{
if ((hot && temps[i] > high) || (!hot && temps[i] < low))
{
count++;
if (count > max)
max = count;
}
else
count = 0; // הרצף נקטע
}
return max;
}
// סעיף ב
public boolean isSafe()
{
return longestRun(true) <= 2 && longestRun(false) <= 2;
}סעיף ב
return longestRun(true) <= 2 && longestRun(false) <= 2;
מה נדרש: פעולה פנימית שמחזירה true רק אם הרצף הארוך ביותר של "שעות חמות" וגם הרצף הארוך ביותר של "שעות קרות" אינם ארוכים משתי שעות, תוך שימוש בפעולה מסעיף א.
רעיון: longestRun(true) נותנת את הרצף החם הארוך ביותר ו־longestRun(false) את הרצף הקר הארוך ביותר. "לא היה רצף של יותר משתיים" שקול ל"הרצף הארוך ביותר הוא לכל היותר 2". שני התנאים צריכים להתקיים – לכן &&.
פתרון:
public boolean isSafe()
{
return longestRun(true) <= 2 && longestRun(false) <= 2;
}בדיקה: בדוגמה הרצף החם הארוך ביותר הוא 3 > 2 ולכן false. עבור 20 , 31 , 31 , 25 , 17 , 17 , 22 (low = 18, high = 30) שני הרצפים באורך 2 בדיוק – התקבל true.
טעות נפוצה: לכתוב < 2 – רצף של שתי שעות בדיוק מותר. או לכתוב || במקום && – מספיק שאחד הרצפים ארוך מדי כדי שהחממה לא תהיה "בטוחה".
// סעיף א
public int longestRun(boolean hot)
{
int max = 0; // הרצף הארוך ביותר שנמצא עד כה
int count = 0; // אורך הרצף הנוכחי
for (int i = 0; i < temps.length; i++)
{
if ((hot && temps[i] > high) || (!hot && temps[i] < low))
{
count++;
if (count > max)
max = count;
}
else
count = 0; // הרצף נקטע
}
return max;
}
// סעיף ב
public boolean isSafe()
{
return longestRun(true) <= 2 && longestRun(false) <= 2;
}שאלה 5 · מרכז הפצה – קרטונים וחומר ריפוד
המחלקה Size מייצגת מידות של תיבה, ולה שלוש תכונות:
הניחו שקיימות פעולות get/Get ו־ set/Set עבור תכונות המחלקה, ופעולה בונה המקבלת ערכים עבור תכונות המחלקה.
לפניכם ממשק המחלקה. אפשר להשתמש בפעולות המחלקה בלי לממש אותן.
| תיאור הפעולה | כותרת הפעולה |
|---|---|
| הפעולה מחזירה את הנפח של העצם הנוכחי בסמ"ק (אורך × רוחב × גובה). | Java – public int volume ()<br>C# – public int Volume () |
| הפעולה מחזירה true אם הנפח של העצם הנוכחי גדול מהנפח של העצם other , אחרת (כלומר אם הנפח שווה לו או קטן ממנו) הפעולה מחזירה false . | Java – public boolean biggerThan (Size other)<br>C# – public bool BiggerThan (Size other) |
המחלקה Shipment מייצגת חבילה במרכז הפצה של חנות מקוונת, ולה שלוש תכונות:
הניחו שקיימות פעולות get/Get ו־ set/Set עבור תכונות המחלקה, ושתכונות המחלקה אינן null . הניחו שהמוצר נכנס תמיד לקרטון שלו.
נתון מערך חד־ממדי shipments מטיפוס Shipment המכיל מידע על כל החבילות שנארזו במרכז ההפצה ביום מסוים. המערך אינו ממוין ואינו מכיל ערכי null .
כתבו פעולה חיצונית ששמה largestBox בשפת Java או LargestBox בשפת C# , המקבלת את המערך shipments , ומדפיסה את קוד החבילה שהקרטון שלה הוא בעל הנפח הגדול ביותר.
הניחו שאין שני קרטונים בעלי אותו נפח.
את החלל הריק בקרטון ממלאים בחומר ריפוד. עלות הריפוד נקבעת לפי נפח החלל הריק בקרטון:
כתבו פעולה פנימית במחלקה Shipment ששמה emptySpace בשפת Java או EmptySpace בשפת C# , המחזירה את נפח החלל הריק בקרטון (נפח הקרטון פחות נפח המוצר).
כתבו פעולה חיצונית ששמה fillerCost בשפת Java או FillerCost בשפת C# , המקבלת את המערך shipments , ומחזירה את העלות הכוללת של חומר הריפוד לכל החבילות.
אפשר להשתמש בפעולה שכתבתם בסעיף ב(1).
◂ פתרון מלא לשאלה 5
סעיף א
חיפוש מקסימום עם biggerThan על getBox(); מדפיסים shipments[maxIndex].getCode().
מה נדרש: למצוא במערך (לא ממוין) את החבילה שהקרטון שלה הגדול ביותר בנפחו, ולהדפיס את הקוד שלה.
רעיון: חיפוש מקסימום, שבו ההשוואה בין שני קרטונים נעשית בפעולה biggerThan של Size. שומרים את האינדקס של החבילה עם הקרטון הגדול ביותר עד כה (מתחילים מ־0); אם הקרטון של shipments[i] גדול ממנו – מעדכנים. בסוף מדפיסים את הקוד.
פתרון: הקוד המלא – בלשונית Java/C#. בשרשרת shipments[i].getBox().biggerThan(shipments[maxIndex].getBox()) מקבלים את עצם ה־Size של הקרטון ועליו מפעילים את biggerThan.
בדיקה: 5 חבילות עם קרטונים בנפחים 6000 , 24000 , 4000 , 5000 , 13500 – מודפס PK-42 (הקרטון 40×30×20 = 24000).
טעות נפוצה: להשוות את מידות המוצר (item) במקום את מידות הקרטון (box), או להשוות רק את האורך של הקרטונים במקום את הנפח.
// סעיף א
public static void largestBox(Shipment[] shipments)
{
int maxIndex = 0;
for (int i = 1; i < shipments.length; i++)
{
if (shipments[i].getBox().biggerThan(shipments[maxIndex].getBox()))
maxIndex = i;
}
System.out.println(shipments[maxIndex].getCode());
}סעיף ב(1)
return box.volume() - item.volume();
מה נדרש: פעולה פנימית ב־Shipment שמחזירה את נפח החלל הריק בקרטון.
רעיון: מפעילים את volume על הקרטון ועל המוצר ומחסירים.
פתרון:
public int emptySpace()
{
return box.volume() - item.volume();
}בדיקה: קרטון 30×20×10 = 6000 ומוצר 25×15×8 = 3000 → 3000; קרטון 30×30×15 = 13500 ומוצר 10×10×10 = 1000 → 12500.
// סעיף ב(1) – פעולה פנימית במחלקה Shipment
public int emptySpace()
{
return box.volume() - item.volume();
}סעיף ב(2)
לכל חבילה: space = emptySpace(); אם space > 8000 מוסיפים 12, אחרת אם space > 2000 מוסיפים 5; מחזירים את הסכום.
מה נדרש: סכום עלויות הריפוד של כל החבילות.
רעיון: לכל חבילה מחשבים space = emptySpace() ובודקים את המדרגות מהגבוהה לנמוכה: יותר מ־8000 → 12 שקלים; אחרת, יותר מ־2000 → 5 שקלים; אחרת – 0. סוכמים על כל המערך.
פתרון: הקוד המלא – בלשונית Java/C#.
בדיקה:
| חבילה | נפח קרטון | נפח מוצר | חלל ריק | עלות |
|---|---|---|---|---|
| PK-17 | 6000 | 3000 | 3000 | 5 |
| PK-42 | 24000 | 4000 | 20000 | 12 |
| PK-08 | 4000 | 2000 | 2000 | 0 |
| PK-93 | 5000 | 3888 | 1112 | 0 |
| PK-55 | 13500 | 1000 | 12500 | 12 |
הפעולה החזירה 29 = 5 + 12 + 0 + 0 + 12. נבדקו גם מקרי הגבול: חלל ריק של 8000 בדיוק → 5, וחלל ריק של 2001 → 5.
טעות נפוצה: לבדוק קודם space > 2000 – אז גם חלל של 20000 מקבל 5 שקלים ולא 12. או לטעות בגבולות (2000 בדיוק – ללא עלות; 8000 בדיוק – 5 שקלים).
// סעיף ב(2)
public static int fillerCost(Shipment[] shipments)
{
int sum = 0;
for (int i = 0; i < shipments.length; i++)
{
int space = shipments[i].emptySpace();
if (space > 8000)
sum += 12;
else if (space > 2000)
sum += 5;
// עד 2000 סמ"ק (כולל) – ללא עלות
}
return sum;
}שאלה 6 · מספר הצעדים לאפס (30 נקודות)
נתונה פעולה חיצונית ששמה maxDigit בשפת Java או MaxDigit בשפת C# , המקבלת מספר שלם שאינו שלילי – num ומחזירה את הספרה הגדולה ביותר במספר. אפשר להשתמש בפעולה בלי לממש אותה.
לדוגמה: עבור num = 1708 , הפעולה תחזיר 8 .
"מספר הצעדים לאפס" של מספר שלם שאינו שלילי מחושב באופן שלהלן:
בכל צעד מחסירים מן המספר את הספרה הגדולה ביותר שבו, וחוזרים על כך עד שמתקבל 0 . "מספר הצעדים לאפס" הוא מספר ההחסרות שבוצעו.
דוגמאות:
"מספר הצעדים לאפס" של המספר 0 הוא 0 .
"מספר הצעדים לאפס" של המספר 7 הוא 1 (7 − 7 = 0) .
"מספר הצעדים לאפס" של המספר 24 הוא 5 , כמפורט להלן:
24 − 4 = 20
20 − 2 = 18
18 − 8 = 10
10 − 1 = 9
9 − 9 = 0
כתבו פעולה חיצונית ששמה stepsToZero בשפת Java או StepsToZero בשפת C# המקבלת מספר שלם num שאינו שלילי ומחזירה את "מספר הצעדים לאפס" שלו.
"מספר הצעדים לאפס" יכול להיות זוגי (למשל 0 עבור המספר 0) או אי־זוגי (למשל 5 כמו בדוגמה השלישית שלעיל). יש הטוענים כי בטווח שבין 1 ל־ 9999 יש יותר מספרים שעבורם "מספר הצעדים לאפס" אי־זוגי ממספרים שעבורם "מספר הצעדים לאפס" זוגי.
כתבו פעולה חיצונית ששמה isCorrect בשפת Java או IsCorrect בשפת C# המחזירה true אם הטענה נכונה, ואחרת מחזירה false .
הערה: חובה להיעזר בפעולה שכתבתם בסעיף א(1).
נתונה פעולה חיצונית נוספת, ששמה isPrime בשפת Java או IsPrime בשפת C# , המקבלת מספר שלם שאינו שלילי – num , ומחזירה true אם num הוא מספר ראשוני, ואחרת מחזירה false (המספרים 0 ו־ 1 אינם ראשוניים).
אפשר להשתמש בפעולה בלי לממש אותה.
כתבו פעולה חיצונית ששמה onlyOnePrime בשפת Java או OnlyOnePrime בשפת C# המקבלת שני מספרים שלמים שאינם שליליים: num1 ו־ num2 . הפעולה מחזירה true אם בדיוק אחד מבין "מספר הצעדים לאפס" של num1 ו"מספר הצעדים לאפס" של num2 הוא מספר ראשוני, אחרת היא מחזירה false .
למשל עבור num1 = 24 ו־ num2 = 7 , הפעולה תחזיר true כי "מספר הצעדים לאפס" של num1 הוא 5 (ראשוני) ו"מספר הצעדים לאפס" של num2 הוא 1 (אינו ראשוני). עבור num1 = 24 ו־ num2 = 10 הפעולה תחזיר false , כי שני המספרים 5 ו־ 2 ראשוניים.
הערה: חובה להיעזר בפעולה שכתבתם בסעיף א(1).
◂ פתרון מלא לשאלה 6
סעיף א(1)
int steps = 0; while (num > 0) { num = num − maxDigit(num); steps++; } return steps;
מה נדרש: לספור כמה החסרות של הספרה הגדולה ביותר נדרשות כדי להגיע מ־ num ל־ 0.
רעיון: לולאת while כל עוד num > 0: מחסירים מ־ num את maxDigit(num) ומגדילים מונה צעדים. כאשר num > 0 יש בו לפחות ספרה אחת שאינה 0, ולכן maxDigit(num) ≥ 1 והמספר קטן בכל צעד – הלולאה מסתיימת. עבור num = 0 הלולאה לא מתבצעת ומוחזר 0.
פתרון:
public static int stepsToZero(int num)
{
int steps = 0;
while (num > 0)
{
num = num - maxDigit(num);
steps++;
}
return steps;
}בדיקה: stepsToZero(0) = 0, stepsToZero(7) = 1, stepsToZero(10) = 2 (10 → 9 → 0), stepsToZero(24) = 5, stepsToZero(57) = 11, stepsToZero(100) = 17.
טעות נפוצה: להחסיר את maxDigit של המספר המקורי בכל צעד (לחשב אותו פעם אחת לפני הלולאה) – הספרה הגדולה ביותר משתנה תוך כדי (ב־ 24: קודם 4, אחר כך 2, 8, 1, 9).
public static int stepsToZero(int num)
{
int steps = 0;
while (num > 0)
{
num = num - maxDigit(num);
steps++;
}
return steps;
}סעיף א(2)
סופרים זוגי/אי־זוגי של stepsToZero(i) עבור i = 1..9999 ומחזירים countOdd > countEven. התוצאה: 5002 זוגיים לעומת 4997 אי־זוגיים, ולכן הפעולה מחזירה false.
מה נדרש: לבדוק את הטענה על כל המספרים 1 עד 9999 ולהחזיר true אם יש יותר מספרים עם "מספר צעדים לאפס" אי־זוגי.
רעיון: עוברים בלולאה על כל i מ־ 1 עד 9999, מחשבים stepsToZero(i) (סעיף א(1)) וסופרים בשני מונים: זוגי ואי־זוגי. בסוף מחזירים countOdd > countEven.
פתרון:
public static boolean isCorrect()
{
int countEven = 0;
int countOdd = 0;
for (int i = 1; i <= 9999; i++)
{
if (stepsToZero(i) % 2 == 0)
countEven++;
else
countOdd++;
}
return countOdd > countEven;
}בדיקה: מתקבל countEven = 5002, countOdd = 4997, ולכן הפעולה מחזירה false – הטענה אינה נכונה (בהפרש קטן מאוד – אי אפשר לנחש את התשובה, ויש לחשב אותה).
טעות נפוצה: לבדוק את הזוגיות של i עצמו, או להחזיר countOdd >= countEven.
public static boolean isCorrect()
{
int countEven = 0;
int countOdd = 0;
for (int i = 1; i <= 9999; i++)
{
if (stepsToZero(i) % 2 == 0)
countEven++;
else
countOdd++;
}
return countOdd > countEven;
}סעיף ב
prime1 = isPrime(stepsToZero(num1)); prime2 = isPrime(stepsToZero(num2)); return (prime1 && !prime2) || (!prime1 && prime2);
מה נדרש: להחזיר true אם בדיוק אחד משני "מספרי הצעדים לאפס" ראשוני.
רעיון: מחשבים לכל מספר את מספר הצעדים שלו (סעיף א(1)) ובודקים בעזרת isPrime אם הוא ראשוני. "בדיוק אחד" = הראשון ראשוני והשני לא, או השני ראשוני והראשון לא. (אפשר גם לכתוב prime1 != prime2.)
פתרון:
public static boolean onlyOnePrime(int num1, int num2)
{
boolean prime1 = isPrime(stepsToZero(num1));
boolean prime2 = isPrime(stepsToZero(num2));
return (prime1 && !prime2) || (!prime1 && prime2);
}בדיקה: onlyOnePrime(24, 7) → true (5 ו־ 1); onlyOnePrime(24, 10) → false (5 ו־ 2, שניהם ראשוניים); onlyOnePrime(9, 0) → false (1 ו־ 0, אף אחד אינו ראשוני); onlyOnePrime(100, 7) → true (17 ו־ 1).
טעות נפוצה: לכתוב prime1 || prime2 – זה "לפחות אחד" ולא "בדיוק אחד", ונותן true גם עבור 24 ו־ 10.
public static boolean onlyOnePrime(int num1, int num2)
{
boolean prime1 = isPrime(stepsToZero(num1));
boolean prime2 = isPrime(stepsToZero(num2));
return (prime1 && !prime2) || (!prime1 && prime2);
}טבלת התשובות
| שאלה | סעיף | תשובה |
|---|---|---|
| 1 | - | הפעולה מחזירה 4 (אורכו של הרצף הארוך ביותר של איברים צמודים שעולים ממש: 5, 7, 9, 12). |
| 2 | א | volume < target − tol → 1 ; volume > target + tol → 2 ; אחרת 0 |
| ב | result[i] = getCheck(volumes[i], target, tol) לכל i, במערך חדש | |
| ג | סופרים תאים השווים ל־ 0 במערך מ־ getAllChecks ומחזירים ok * 4 < volumes.length * 3 | |
| 3 | 1 | int[3]; לכל תו: אות ⇒ kinds[0]++, ספרה ⇒ kinds[1]++, אחרת ⇒ kinds[2]++; מחזירים kinds. |
| 2 | לולאת זקיף: כל עוד analyze(pw)[0] > 0 – אם length >= 8 וגם kinds[1] >= 2 וגם kinds[2] >= 1 אז strong++ אחרת weak++; מדפיסים strong,weak. | |
| 4 | א | סורקים את המערך עם מונה לרצף הנוכחי: אם השעה מהסוג המבוקש (hot: temps[i] > high, אחרת temps[i] < low) מגדילים את המונה ומעדכנים מקסימום; אחרת מאפסים את המונה. בדוגמה: 3 ו־2. |
| ב | return longestRun(true) <= 2 && longestRun(false) <= 2; | |
| 5 | א | חיפוש מקסימום עם biggerThan על getBox(); מדפיסים shipments[maxIndex].getCode(). |
| ב(1) | return box.volume() - item.volume(); | |
| ב(2) | לכל חבילה: space = emptySpace(); אם space > 8000 מוסיפים 12, אחרת אם space > 2000 מוסיפים 5; מחזירים את הסכום. | |
| 6 | א(1) | int steps = 0; while (num > 0) { num = num − maxDigit(num); steps++; } return steps; |
| א(2) | סופרים זוגי/אי־זוגי של stepsToZero(i) עבור i = 1..9999 ומחזירים countOdd > countEven. התוצאה: 5002 זוגיים לעומת 4997 אי־זוגיים, ולכן הפעולה מחזירה false. | |
| ב | prime1 = isPrime(stepsToZero(num1)); prime2 = isPrime(stepsToZero(num2)); return (prime1 && !prime2) || (!prime1 && prime2); |