מתכונת 2 במדעי המחשב - יסודות מדעי המחשב 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 · מעקב אחר לולאה עם מונה זוגיים
נתון מערך arr , המכיל מספרים שלמים:
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | |
|---|---|---|---|---|---|---|---|
| arr | 4 | 7 | 3 | 10 | 9 | 6 | 5 |
נתון קטע תוכנית, הכתוב בשפות Java ו־ C# :
Java
int count = 0;
for (int i = 0; i < arr.length; i++)
{
if (arr[i] % 2 == 0)
{
count++;
arr[i] = arr[i] / 2;
}
else
arr[i] = arr[i] * count;
}C#
int count = 0;
for (int i = 0; i < arr.Length; i++)
{
if (arr[i] % 2 == 0)
{
count++;
arr[i] = arr[i] / 2;
}
else
arr[i] = arr[i] * count;
}עקבו בעזרת טבלת מעקב אחר ביצוע קטע התוכנית, ורשמו את תוכן המערך arr לאחר ביצוע קטע התוכנית.
בטבלת המעקב יש לכלול עמודה עבור כל אחד מן המשתנים האלה: i , arr[i] , count .
כתבו דוגמה למערך arr בגודל 5 , שאף אחד מן התאים שלו אינו מכיל 0 , ושלאחר ביצוע קטע התוכנית יהיו בו בדיוק שלושה תאים המכילים 0 .
◂ פתרון מלא לשאלה 1
סעיף א
arr = {2, 7, 3, 5, 18, 3, 15}
מה נדרש: לעקוב אחרי ביצוע הלולאה על המערך הנתון (עמודות i , arr[i] , count) ולרשום את המערך בסוף.
רעיון: count סופר כמה מספרים זוגיים נמצאו עד עכשיו (כולל התא הנוכחי).
פתרון – טבלת מעקב (לפני הלולאה: count = 0)
| i | i < 7 ? | arr[i] זוגי? | count (בסוף הסיבוב) | arr[i] (בסוף הסיבוב) |
|---|---|---|---|---|
| 0 | true | 4 – כן | 1 | 4 / 2 = 2 |
| 1 | true | 7 – לא | 1 | 7 · 1 = 7 |
| 2 | true | 3 – לא | 1 | 3 · 1 = 3 |
| 3 | true | 10 – כן | 2 | 10 / 2 = 5 |
| 4 | true | 9 – לא | 2 | 9 · 2 = 18 |
| 5 | true | 6 – כן | 3 | 6 / 2 = 3 |
| 6 | true | 5 – לא | 3 | 5 · 3 = 15 |
| 7 | false | – | – | – |
המערך לאחר ביצוע קטע התוכנית:
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | |
|---|---|---|---|---|---|---|---|
| arr | 2 | 7 | 3 | 5 | 18 | 3 | 15 |
בדיקה: הקטע מדפיס 2 7 3 5 18 3 15, בדיוק כמו בטבלה.
טעות נפוצה: לבדוק את הזוגיות של הערך החדש בתא, או לחשוב שהתאים 7 ו־3 "לא טופלו" – הם כן הוכפלו, אבל ב־1, ולכן ערכם לא השתנה.
סעיף ב
למשל {7, 1, 3, 6, 5} (הופך ל־{0, 0, 0, 3, 5}). מתאים כל מערך ללא 0 שבו שלושת התאים הראשונים אי־זוגיים ו־arr[3] זוגי.
מה נדרש: מערך בגודל 5 בלי אפסים, שאחרי ביצוע הקטע יש בו בדיוק שלושה אפסים.
רעיון: מתי תא הופך ל־0?
לכן האפסים הם בדיוק התאים האי־זוגיים שמופיעים לפני הזוגי הראשון. כדי לקבל בדיוק שלושה אפסים: arr[0], arr[1], arr[2] אי־זוגיים, ו־arr[3] זוגי (אחרת יהיו ארבעה אפסים או יותר). arr[4] – כל מספר שאינו 0.
פתרון: למשל המערך:
| 0 | 1 | 2 | 3 | 4 | |
|---|---|---|---|---|---|
| arr | 7 | 1 | 3 | 6 | 5 |
| i | arr[i] זוגי? | count | arr[i] |
|---|---|---|---|
| 0 | 7 – לא | 0 | 7 · 0 = 0 |
| 1 | 1 – לא | 0 | 1 · 0 = 0 |
| 2 | 3 – לא | 0 | 3 · 0 = 0 |
| 3 | 6 – כן | 1 | 6 / 2 = 3 |
| 4 | 5 – לא | 1 | 5 · 1 = 5 |
אחרי הביצוע: 0 , 0 , 0 , 3 , 5 – בדיוק שלושה אפסים.
בדיקה: על {7,1,3,6,5} מתקבל 0 0 0 3 5, ועל {1,3,5,2,4} מתקבל 0 0 0 1 2. בנוסף בדקנו בתוכנית את כל המערכים בגודל 5 עם ערכים מ־5− עד 5 (ללא 0): יש בדיוק שלושה אפסים אם ורק אם שלושת התאים הראשונים אי־זוגיים ו־arr[3] זוגי.
טעות נפוצה: לבחור למשל {1, 3, 5, 7, 2} – כאן ארבעה תאים אי־זוגיים מופיעים לפני הזוגי הראשון, ולכן מתקבלים ארבעה אפסים ולא שלושה.
שאלה 2 · טמפרטורות במקרר חיסונים (20 נקודות)
חיסונים נשמרים במקרר מיוחד, ובכל שעה נמדדת הטמפרטורה במקרר (במעלות צלזיוס, מספר שלם).
הפרמטר low הוא מספר שלם המייצג את הטמפרטורה הנמוכה ביותר המותרת במקרר.
הפרמטר high הוא מספר שלם המייצג את הטמפרטורה הגבוהה ביותר המותרת במקרר (low < high).
כתבו פעולה חיצונית ששמה getDeviation בשפת Java או GetDeviation בשפת C# המקבלת מספר שלם – temp , המייצג טמפרטורה שנמדדה במקרר, ואת הפרמטרים low ו־ high .
הפעולה תחזיר את הסטייה של הטמפרטורה מן הטווח המותר, על פי התנאים האלה:
– אם הטמפרטורה נמוכה מ־ low , הפעולה תחזיר בכמה מעלות היא נמוכה מ־ low .
– אם הטמפרטורה גבוהה מ־ high , הפעולה תחזיר בכמה מעלות היא גבוהה מ־ high .
– אם שני התנאים שלעיל אינם מתקיימים (הטמפרטורה בטווח המותר, כולל הגבולות), הפעולה תחזיר 0 .
דוגמה: בעבור low = 2 , high = 8 : בעבור temp = 11 תחזיר 3 , בעבור temp = -1 תחזיר 3 , בעבור temp = 8 תחזיר 0 .
המערך temps הוא מערך מטיפוס שלם המכיל את הטמפרטורות שנמדדו במקרר במהלך משלוח של חיסונים, לפי סדר המדידות (כל תא במערך מציג מדידה אחרת).
כתבו פעולה חיצונית ששמה getAllDeviations בשפת Java או GetAllDeviations בשפת C# המקבלת את המערך temps ואת הפרמטרים low ו־ high .
הפעולה תחזיר מערך חדש מטיפוס שלם בגודל זהה למערך temps , המציג בכל תא את הסטייה של המדידה שנמצאת באותו האינדקס במערך temps (על פי סעיף א).
דוגמה: בעבור low = 2 , high = 8 , והמערך temps :
| אינדקס | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
|---|---|---|---|---|---|---|---|
| temps | 5 | 9 | 11 | 7 | 1 | -1 | 8 |
יוחזר המערך שלהלן:
| אינדקס | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
|---|---|---|---|---|---|---|---|
| 0 | 1 | 3 | 0 | 1 | 3 | 0 |
הערות: – יש להשתמש בפעולה שכתבתם בסעיף א.
– אין לשנות את המערך temps .
משלוח חיסונים נפסל אם מתקיים בו לפחות אחד מן התנאים האלה:
– יש מדידה שבה הסטייה גדולה מ־ 3 מעלות.
– יש שתי מדידות רצופות (בשני תאים סמוכים במערך) שבשתיהן הטמפרטורה מחוץ לטווח המותר.
כתבו פעולה חיצונית ששמה isSafe בשפת Java או IsSafe בשפת C# , המקבלת את המערך temps ואת הפרמטרים low ו־ high .
הפעולה תחזיר true אם המשלוח לא נפסל, אחרת הפעולה תחזיר false .
דוגמה: בעבור המערך הנתון בסעיף ב הפעולה תחזיר false , כי במדידות באינדקסים 1 ו־ 2 , שהן רצופות, הטמפרטורה מחוץ לטווח המותר (אף שאין במערך מדידה שבה הסטייה גדולה מ־ 3).
הערה: יש להשתמש בפעולה שכתבתם בסעיף ב.
◂ פתרון מלא לשאלה 2
סעיף א
temp < low → low − temp ; temp > high → temp − high ; אחרת 0
מה נדרש: פעולה המחזירה בכמה מעלות הטמפרטורה חורגת מן הטווח [low, high], או 0 אם היא בתוכו.
רעיון: אם temp < low – החריגה היא המרחק עד low, כלומר low − temp (מספר חיובי). אם temp > high – החריגה היא temp − high. אחרת הטמפרטורה בטווח (כולל הגבולות) ומחזירים 0.
פתרון: הקוד המלא – בלשונית Java / C#.
if (temp < low) return low - temp;
if (temp > high) return temp - high;
return 0;בדיקה: low = 2, high = 8: עבור 11 → 3, עבור -1 → 2 − (−1) = 3, עבור 2 → 0, עבור 8 → 0, עבור 5 → 0.
טעות נפוצה: להחזיר temp − low כאשר הטמפרטורה נמוכה מדי – מתקבל מספר שלילי (עבור -1: −3) במקום הסטייה החיובית 3.
public static int getDeviation(int temp, int low, int high)
{
if (temp < low)
return low - temp;
if (temp > high)
return temp - high;
return 0;
}סעיף ב
result[i] = getDeviation(temps[i], low, high) לכל i, במערך חדש
מה נדרש: מערך חדש באותו גודל, שבו כל תא הוא הסטייה של המדידה המתאימה.
רעיון: יוצרים מערך result בגודל temps.length ועוברים על כל האינדקסים: result[i] = getDeviation(temps[i], low, high). המערך temps רק נקרא.
פתרון: הקוד המלא – בלשונית Java / C#.
בדיקה: על המערך מהדוגמה (low = 2, high = 8) התקבל [0, 1, 3, 0, 1, 3, 0], כמו בשאלה, והמערך temps נשאר ללא שינוי.
public static int[] getAllDeviations(int[] temps, int low, int high)
{
int[] result = new int[temps.length];
for (int i = 0; i < temps.length; i++)
{
result[i] = getDeviation(temps[i], low, high);
}
return result;
}סעיף ג
על מערך הסטיות: אם dev[i] > 3, או dev[i] > 0 וגם dev[i − 1] > 0 (i > 0) – מחזירים false; אחרי הלולאה true
מה נדרש: להחזיר true אם אין מדידה עם סטייה גדולה מ־ 3 ואין שתי מדידות רצופות מחוץ לטווח.
רעיון: מקבלים את מערך הסטיות dev מ־ getAllDeviations. מדידה מחוץ לטווח היא מדידה שהסטייה שלה גדולה מ־ 0. עוברים על המערך: אם dev[i] > 3 – המשלוח נפסל. אם i > 0 ושני התאים dev[i] ו־ dev[i − 1] גדולים מ־ 0 – שתי מדידות רצופות מחוץ לטווח, והמשלוח נפסל. אם הלולאה הסתיימה בלי לפסול – מחזירים true.
פתרון: הקוד המלא – בלשונית Java / C#.
בדיקה (low = 2, high = 8):
| temps | סטיות | תוצאה |
|---|---|---|
| 5, 9, 11, 7, 1, -1, 8 | 0, 1, 3, 0, 1, 3, 0 | false (אינדקסים 1, 2) |
| 5, 11, 4, -1, 8 | 0, 3, 0, 3, 0 | true |
| 5, 12, 5 | 0, 4, 0 | false (סטייה 4) |
| 5, 6, 9, 1 | 0, 0, 1, 1 | false (אינדקסים 2, 3) |
| -2, 5 | 4, 0 | false (סטייה 4 באינדקס 0) |
טעות נפוצה: לבדוק dev[i + 1] בלולאה עד i < dev.length – חריגה מגבולות המערך בתא האחרון. לכן משווים כל תא לתא שלפניו ומתחילים את בדיקת הזוג מ־ i = 1 (התנאי i > 0).
public static boolean isSafe(int[] temps, int low, int high)
{
int[] dev = getAllDeviations(temps, low, high);
for (int i = 0; i < dev.length; i++)
{
if (dev[i] > 3)
return false;
if (i > 0 && dev[i] > 0 && dev[i - 1] > 0)
return false;
}
return true;
}שאלה 3 · כרטיס הגרלה (20 נקודות)
"כרטיס הגרלה" הוא מערך מטיפוס שלם שבו כל תא מכיל מספר שלם בין 1 ל־ 37 (כולל), וכל המספרים בכרטיס שונים זה מזה.
ממלאים כרטיס הגרלה תא אחר תא, החל מתא 0 , עד שכל התאים מלאים. בכל שלב:
i. מוגרל מספר שלם בין 1 ל־ 37 .
ii. המספר שהוגרל מוצב בתא הבא שיש למלא.
הערה: אם המספר שהוגרל כבר מופיע באחד התאים שמולאו, ההגרלה לא תיחשב (המספר לא יוצב), ומגרילים מספר חדש עבור אותו התא.
כתבו פעולה חיצונית ששמה fillTicket בשפת Java או FillTicket בשפת C# המקבלת מערך מטיפוס שלם – arr (שגודלו בין 1 ל־ 37) וממלאת אותו כ"כרטיס הגרלה".
הפעולה תדפיס את ערכי התאים לפי הסדר לאחר המילוי (כל ערך בשורה חדשה).
כתבו פעולה חיצונית ששמה fillTicket בשפת Java או FillTicket בשפת C# המקבלת מערך מטיפוס שלם – arr (שגודלו בין 1 ל־ 37) וממלאת אותו כ"כרטיס הגרלה".
הפעולה תדפיס את ערכי התאים לפי הסדר לאחר המילוי (כל ערך בשורה חדשה).
◂ פתרון מלא לשאלה 3
פתרון
לולאת while עם מונה count של התאים שמולאו; בכל סיבוב מגרילים num בין 1 ל־ 37 , מחפשים אותו בתאים 0..count-1 , ורק אם לא נמצא – arr[count] = num ו־ count++; בסוף מדפיסים את המערך, ערך בכל שורה.
מה נדרש: למלא את המערך במספרים אקראיים בין 1 ל־ 37 , שונים זה מזה, ואחר כך להדפיס אותו, ערך בכל שורה.
רעיון:
rnd.nextInt(37) + 1 ב־ Java או rnd.Next(37) + 1 ב־ C# (nextInt(37) נותן 0 עד 36).פתרון:
public static void fillTicket(int[] arr)
{
Random rnd = new Random();
int count = 0;
while (count < arr.length)
{
int num = rnd.nextInt(37) + 1;
boolean found = false;
for (int i = 0; i < count; i++)
{
if (arr[i] == num)
found = true;
}
if (!found)
{
arr[count] = num;
count++;
}
}
for (int i = 0; i < arr.length; i++)
{
System.out.println(arr[i]);
}
}(ב־ Java צריך import java.util.Random; בראש הקובץ. אפשר גם להגריל בעזרת (int)(Math.random() * 37) + 1 .)
בדיקה: על מערך בגודל 6 מתקבל למשל 37 30 28 8 34 23 – כל הערכים בין 1 ל־ 37 , שונים זה מזה, והשורות שמודפסות זהות לתאי המערך. בדיקה קשה יותר: מערך בגודל 37 – הכרטיס חייב להכיל בדיוק את כל המספרים 1 עד 37 . ובמערך בגודל 37 מתקבל תמיד סידור של כל 37 המספרים.
טעות נפוצה: לחפש את המספר בכל המערך (כולל תאים שעוד לא מולאו ומכילים 0) – זה עובד כאן רק במקרה, כי 0 אינו בתחום ההגרלה. טעות חמורה יותר: לקדם את count גם כאשר המספר כבר קיים – כך נשאר תא ריק (0) בכרטיס, או שמופיעים מספרים חוזרים.
public static void fillTicket(int[] arr)
{
Random rnd = new Random();
int count = 0;
while (count < arr.length)
{
int num = rnd.nextInt(37) + 1;
boolean found = false;
for (int i = 0; i < count; i++)
{
if (arr[i] == num)
found = true;
}
if (!found)
{
arr[count] = num;
count++;
}
}
for (int i = 0; i < arr.length; i++)
{
System.out.println(arr[i]);
}
}פרק שני (60 נקודות)
ענו על שתיים מן השאלות 4–6 (לכל שאלה – 30 נקודות).
שאלה 4 · מכולות של פולי קפה – מערך עצמים (30 נקודות)
חברה ליבוא קפה מקבלת שקים של פולי קפה ירוקים במכולות.
נתונה המחלקה Sack המייצגת שק של פולי קפה ולה שתי תכונות: ארץ המקור של הפולים – origin, ומשקל השק בקילוגרמים – weight (מספר ממשי חיובי).
public class Sack{
private String origin;
private double weight;
}בנוסף, נתונה המחלקה Container המייצגת מכולה ולה שתי תכונות: קוד המכולה – code, ומערך של עצמים sacks מטיפוס Sack בגודל מספר השקים במכולה.
public class Container{
private String code;
private Sack[] sacks;
}הניחו שקיימות פעולות get ו־set בשפת Java ופעולות Get ו־Set בשפת C# בעבור כל התכונות בשתי המחלקות.
לפניכם ממשק חלקי של המחלקה Container:
| תיאור הפעולה | כותרת הפעולה |
|---|---|
| פעולה בונה המקבלת את קוד המכולה ואת מספר השקים שבה, ומאתחלת את התכונות. המערך sacks מאותחל למערך ריק בגודל num | public Container(String code, int num) |
| פעולה המחזירה את המשקל הכולל (בקילוגרמים) של כל השקים במכולה. | Java – public double totalWeight()<br>C# – public double TotalWeight() |
| פעולה המקבלת משקל מרבי limit (בקילוגרמים). הפעולה מחזירה true אם המשקל הכולל של השקים במכולה גדול מ־limit, אחרת מחזירה false. | Java – public boolean isOverweight(double limit)<br>C# – public bool IsOverweight(double limit) |
ממשו את הפעולה הבונה של המחלקה Container.
ממשו את הפעולה isOverweight (בשפת C#: IsOverweight) במחלקה Container.
ניתן להשתמש בפעולה totalWeight של המחלקה Container מבלי לממש אותה.
הניחו כי איברי המערך sacks שונים מ־null.
מכולה יכולה לצאת למשלוח אם המשקל הכולל של השקים בה אינו גדול מהמשקל המרבי המותר.
כתבו פעולה חיצונית ששמה printReady (בשפת C#: PrintReady) המקבלת מערך של עצמים – port מטיפוס Container, המייצג את המכולות שבנמל, ומשקל מרבי מותר limit. הפעולה תדפיס את הקודים של כל המכולות שיכולות לצאת למשלוח.
חובה להשתמש בפעולה isOverweight של המחלקה Container.
הניחו כי איברי המערכים port ו־sacks שונים מ־null.
◂ פתרון מלא לשאלה 4
סעיף א
this.code = code; sacks = new Sack[num];
מה נדרש: לממש את הפעולה הבונה Container(code, num).
רעיון: הפעולה מציבה את הקוד בתכונה code ויוצרת מערך חדש של num הפניות לעצמים מטיפוס Sack (תאיו null – השקים עוד לא הוכנסו). כיוון ששם הפרמטר code זהה לשם התכונה, חובה לכתוב this.code = code;.
פתרון: הקוד המלא – בלשונית Java/C#.
בדיקה: אחרי new Container("TST-1", 4) התקבל (בשתי השפות): code = "TST-1", אורך sacks הוא 4, ו־sacks[3] הוא null.
טעות נפוצה: לכתוב code = code; – ההשמה היא של הפרמטר לעצמו, והתכונה code נשארת null.
public Container(String code, int num)
{
this.code = code;
this.sacks = new Sack[num];
}סעיף ב
return this.totalWeight() > limit;
מה נדרש: להחזיר true אם המשקל הכולל של המכולה גדול מ־limit.
רעיון: את המשקל הכולל מחשבת הפעולה totalWeight, שמותר להשתמש בה בלי לממש אותה. מזמנים אותה על העצם הנוכחי ומחזירים ישירות את תוצאת ההשוואה totalWeight() > limit (אין צורך ב־if).
פתרון: הקוד המלא – בלשונית Java/C#.
בדיקה (limit = 180):
| מכולה | משקלי השקים | משקל כולל | isOverweight(180) |
|---|---|---|---|
| BR-101 | 60, 60, 60.5 | 180.5 | true |
| ET-202 | 69, 69, 69, 69 | 276 | true |
| CO-303 | 70, 70, 35 | 175 | false |
| KE-404 | 59.9, 60, 60 | 179.9 | false |
| VN-505 | 100, 100, 100 | 300 | true |
טעות נפוצה: לכתוב >= – מכולה שמשקלה שווה בדיוק ל־limit אינה חורגת (ראו VN-505 עם limit = 300 בסעיף ג).
public boolean isOverweight(double limit)
{
return this.totalWeight() > limit;
}סעיף ג
לכל i: אם !port[i].isOverweight(limit) – מדפיסים port[i].getCode().
מה נדרש: פעולה חיצונית שמדפיסה את הקודים של המכולות שמשקלן אינו גדול מ־limit.
רעיון: עוברים על כל המכולות. מכולה יכולה לצאת למשלוח בדיוק כאשר isOverweight(limit) מחזירה false, ולכן התנאי הוא השלילה !port[i].isOverweight(limit). את limit מעבירים הלאה לפעולה של המכולה. הקוד מודפס בעזרת getCode(), כי התכונה code פרטית.
פתרון: הקוד המלא – בלשונית Java/C#.
בדיקה: על חמש המכולות מהטבלה בסעיף ב מודפסים:
טעות נפוצה: לשכוח את סימן השלילה ולהדפיס דווקא את המכולות החורגות.
public static void printReady(Container[] port, double limit)
{
for (int i = 0; i < port.length; i++)
{
if (!port[i].isOverweight(limit))
System.out.println(port[i].getCode());
}
}שאלה 5 · קרונות רכבת – מערך של עצמים (30 נקודות)
רכבת מורכבת מקרונות המחוברים זה לזה בשורה. יש קרונות נוסעים, קרונות משא, קרונות מכל (לנוזלים) וקרונות אחרים (קטר, קרון מזנון וכו').
נתונה המחלקה Wagon – קרון, ולה שלוש תכונות:
הניחו שיש פעולות get/Get ו־set/Set בעבור תכונות המחלקה. ניתן להשתמש בפעולות המחלקה מבלי לממש אותן.
נתון ממשק חלקי של המחלקה Wagon:
| תיאור הפעולה | כותרת הפעולה |
|---|---|
| פעולה בונה המקבלת ערכים בעבור תכונות המחלקה | public Wagon(int number, int seats, String cargo) |
| פעולה המחזירה true אם הקרון הוא קרון נוסעים, אחרת היא מחזירה false | Java – public boolean isPassenger()<br>C# – public bool IsPassenger() |
| פעולה המחזירה true אם הקרון הוא קרון משא, אחרת היא מחזירה false | Java – public boolean isFreight()<br>C# – public bool IsFreight() |
| פעולה המחזירה true אם הקרון הוא קרון מכל, אחרת היא מחזירה false | Java – public boolean isTanker()<br>C# – public bool IsTanker() |
| פעולה המחזירה true אם הקרון אינו קרון נוסעים, אינו קרון משא ואינו קרון מכל, אחרת היא מחזירה false | Java – public boolean isOther()<br>C# – public bool IsOther() |
נתונה המחלקה Train – רכבת, ולה תכונה אחת בלבד:
רכבת נקראת מאוזנת אם ההפרש בין כל שניים מבין שלושת המספרים – מספר קרונות הנוסעים, מספר קרונות המשא ומספר קרונות המכל – הוא לכל היותר 1.
לדוגמה, רכבת שיש בה 3 קרונות נוסעים, 2 קרונות משא ו־3 קרונות מכל היא מאוזנת. רכבת שיש בה 3 קרונות נוסעים, קרון משא אחד ו־2 קרונות מכל אינה מאוזנת.
כתבו פעולה פנימית במחלקה Train ששמה isBalanced בשפת Java או IsBalanced בשפת C#.
הפעולה תחזיר true אם הרכבת מאוזנת, אחרת הפעולה תחזיר false.
שימו לב: הקרונות האחרים אינם משפיעים על התשובה.
לצורך ניקיון מחלקים את הרכבת למקטעים של m קרונות רצופים: הקרונות בתאים 0 עד m−1 הם המקטע הראשון, הקרונות בתאים m עד 2m−1 הם המקטע השני, וכן הלאה.
כתבו פעולה פנימית במחלקה Train ששמה hasFullSection בשפת Java או HasFullSection בשפת C#, המקבלת מספר שלם m.
הפעולה תחזיר true אם קיים מקטע כך שכל הקרונות בו הם קרונות נוסעים. אחרת, הפעולה תחזיר false.
הניחו כי m חיובי ו־N מתחלק ב־m ללא שארית.
לדוגמה, בעבור הרכבת:
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
|---|---|---|---|---|---|---|---|
| משא | נוסעים | נוסעים | נוסעים | נוסעים | מכל | מכל | קטר |
hasFullSection(4) תחזיר false, ו־hasFullSection(2) תחזיר true (התאים 2–3).
◂ פתרון מלא לשאלה 5
סעיף 1
סופרים passenger/freight/tanker; max = המקסימום של השלושה, min = המינימום; מחזירים max - min <= 1.
מה נדרש: לספור את קרונות הנוסעים, המשא והמכל, ולבדוק שההפרש בין כל שניים מהם הוא לכל היותר 1.
רעיון:
(אפשר גם לבדוק ישירות שלוש השוואות בערך מוחלט: |p−f| ≤ 1 && |p−t| ≤ 1 && |f−t| ≤ 1.)
פתרון: הקוד המלא – בלשונית Java/C#.
בדיקה (נ – נוסעים, מ – משא, כ – מכל, א – אחר):
טעות נפוצה: לבדוק רק את ההפרשים בין מונים "שכנים" (נוסעים–משא ומשא–מכל) ולשכוח את ההפרש נוסעים–מכל: במקרה 3, 2, 1 שני ההפרשים האלה הם 1, אבל הרכבת אינה מאוזנת.
public boolean isBalanced()
{
int passenger = 0, freight = 0, tanker = 0;
for (int i = 0; i < wagons.length; i++)
{
if (wagons[i].isPassenger())
passenger++;
if (wagons[i].isFreight())
freight++;
if (wagons[i].isTanker())
tanker++;
}
int max = Math.max(passenger, Math.max(freight, tanker));
int min = Math.min(passenger, Math.min(freight, tanker));
return max - min <= 1;
}סעיף 2
לכל start = 0, m, 2m, ...: all = true; כל קרון בתאים start..start+m−1 שאינו isPassenger מאפס את all; אם all נשאר true – return true. בסוף return false.
מה נדרש: להחזיר true אם יש לפחות מקטע אחד (מבין המקטעים הקבועים 0..m−1, m..2m−1, ...) שבו כל הקרונות הם קרונות נוסעים.
רעיון: "קיים מקטע כך שלכל קרון בו ...":
פתרון: הקוד המלא – בלשונית Java/C#.
בדיקה:
טעות נפוצה: לחפש 4 קרונות נוסעים רצופים בכל מקום ברכבת (start++ במקום start += m). בדוגמה יש 4 קרונות נוסעים רצופים בתאים 1–4, אבל הם אינם מקטע אחד, ולכן התשובה בעבור m = 4 היא false.
public boolean hasFullSection(int m)
{
for (int start = 0; start < wagons.length; start += m)
{
boolean all = true; // נניח שכל קרונות המקטע הם קרונות נוסעים
for (int j = start; j < start + m; j++)
{
if (!wagons[j].isPassenger())
all = false;
}
if (all)
return true;
}
return false;
}שאלה 6 · מספרי הרשד (30 נקודות)
מספר שלם חיובי – num ייקרא "מספר הרשד" אם num מתחלק ללא שארית בסכום הספרות שלו.
דוגמה למספר הרשד: המספר 18 , כי סכום הספרות שלו הוא 1 + 8 = 9 , ו־ 18 מתחלק ב־ 9 ללא שארית.
דוגמה נוספת למספר הרשד: המספר 201 , כי סכום הספרות שלו הוא 3 , ו־ 201 מתחלק ב־ 3 ללא שארית (201 = 3 · 67).
דוגמה למספר שאינו מספר הרשד: המספר 19 , כי סכום הספרות שלו הוא 10 , ו־ 19 אינו מתחלק ב־ 10 ללא שארית.
כתבו פעולה חיצונית ששמה isHarshad בשפת Java או IsHarshad בשפת C#, המקבלת מספר שלם חיובי – num . הפעולה מחזירה true אם הוא מספר הרשד, אחרת היא מחזירה false .
כתבו פעולה חיצונית ששמה printHarshad בשפת Java או PrintHarshad בשפת C#, המקבלת שני מספרים שלמים חיוביים: low , high . הפעולה תדפיס את כל מספרי הרשד שבין שני מספרים אלה (כולל).
הניחו ש־ low קטן מ־ high או שווה לו.
הערה: אפשר להיעזר בפעולה שכתבתם בתת־סעיף (1).
דוגמה: בעבור low=10 ו־ high=21 הפעולה תדפיס 10 , 12 , 18 , 20 , 21 .
הסבר: המספר 10 הוא מספר הרשד (סכום הספרות 1), המספר 11 אינו מספר הרשד (סכום הספרות 2 ו־ 11 אי־זוגי), המספר 12 הוא מספר הרשד (סכום הספרות 3), וכן הלאה.
רצף של מספרי הרשד הוא כמה מספרים עוקבים שכולם מספרי הרשד. למשל, 20 , 21 הם רצף של שני מספרי הרשד.
יש השערה שלא קיים רצף של שישה מספרי הרשד עוקבים בין המספרים 10 עד 999,999 (כולל).
כתבו פעולה חיצונית ששמה noSixInRow בשפת Java או NoSixInRow בשפת C#, המחזירה true אם אכן מבין כל המספרים 10 עד 999,999 (כולל) אין שישה מספרי הרשד עוקבים. אחרת היא מחזירה false .
הערה: אפשר להיעזר בפעולה שכתבתם בתת־סעיף א(1).
◂ פתרון מלא לשאלה 6
סעיף א(1)
מחשבים את סכום הספרות sum (לולאת % 10 ו-/ 10 על עותק של num) ומחזירים num % sum == 0.
מה נדרש
לבדוק אם num מתחלק בסכום הספרות שלו.
רעיון
מפרקים את המספר לספרות בלולאה: הספרה הימנית היא temp % 10, ואחר כך מקצצים אותה בעזרת temp / 10, עד ש-temp מגיע ל-0. עובדים על עותק (temp) כי את num עצמו צריך בסוף לבדיקת החלוקה. num חיובי, ולכן sum ≥ 1 ואין חלוקה באפס.
פתרון
Java
// סעיף א(1)
public static boolean isHarshad(int num)
{
int sum = 0;
int temp = num;
while (temp > 0)
{
sum = sum + temp % 10;
temp = temp / 10;
}
return num % sum == 0;
}C#
// סעיף א(1)
public static bool IsHarshad(int num)
{
int sum = 0;
int temp = num;
while (temp > 0)
{
sum = sum + temp % 10;
temp = temp / 10;
}
return num % sum == 0;
}בדיקה
בין 1 ל-999 יש 212 מספרי הרשד.
טעות נפוצה: לפרק את num עצמו (num = num / 10) ואז לבדוק num % sum – בסוף הלולאה num כבר 0, והתשובה תמיד true.
// סעיף א(1)
public static boolean isHarshad(int num)
{
int sum = 0;
int temp = num;
while (temp > 0)
{
sum = sum + temp % 10;
temp = temp / 10;
}
return num % sum == 0;
}סעיף א(2)
for (i = low; i <= high; i++) if (isHarshad(i)) מדפיסים את i.
מה נדרש
להדפיס את כל מספרי הרשד בתחום [low, high], כולל הקצוות.
רעיון
לולאה על כל המספרים מ-low עד high (כולל), ולכל מספר קריאה לפעולה מסעיף א(1).
פתרון
Java
// סעיף א(2)
public static void printHarshad(int low, int high)
{
for (int i = low; i <= high; i++)
{
if (isHarshad(i))
{
System.out.println(i);
}
}
}C#
// סעיף א(2)
public static void PrintHarshad(int low, int high)
{
for (int i = low; i <= high; i++)
{
if (IsHarshad(i))
{
Console.WriteLine(i);
}
}
}בדיקה
טעות נפוצה: לכתוב i < high – אז high עצמו (למשל 21 בדוגמה) לא נבדק.
// סעיף א(2)
public static void printHarshad(int low, int high)
{
for (int i = low; i <= high; i++)
{
if (isHarshad(i))
{
System.out.println(i);
}
}
}סעיף ב
מונה run: גדל ב-1 בכל מספר הרשד ומתאפס במספר שאינו מספר הרשד; אם run מגיע ל-6 מחזירים false; בסוף מחזירים true. (התוצאה: true – הרצף הארוך ביותר בתחום הוא באורך 5, 131052–131056.)
מה נדרש
לבדוק אם בתחום 10–999,999 יש שישה מספרים עוקבים שכולם מספרי הרשד; להחזיר true אם אין רצף כזה.
רעיון
עוברים על המספרים לפי הסדר ומחזיקים מונה run – אורך הרצף הנוכחי של מספרי הרשד שמסתיים במספר הנוכחי. אם i הוא מספר הרשד – run גדל ב-1; אחרת – run מתאפס. ברגע ש-run מגיע ל-6 נמצא רצף כזה, ומחזירים false מיד. אם הלולאה הסתיימה – מחזירים true.
פתרון
Java
// סעיף ב
public static boolean noSixInRow()
{
int run = 0; // אורך הרצף הנוכחי של מספרי הרשד עוקבים
for (int i = 10; i <= 999999; i++)
{
if (isHarshad(i))
{
run++;
if (run == 6)
{
return false;
}
}
else
{
run = 0;
}
}
return true;
}C#
// סעיף ב
public static bool NoSixInRow()
{
int run = 0; // אורך הרצף הנוכחי של מספרי הרשד עוקבים
for (int i = 10; i <= 999999; i++)
{
if (IsHarshad(i))
{
run++;
if (run == 6)
{
return false;
}
}
else
{
run = 0;
}
}
return true;
}בדיקה
הפעולה מחזירה true. הרצף הארוך ביותר בתחום 10–999,999 הוא באורך 5, והוא מופיע פעמיים – 131052 עד 131056 ו-491424 עד 491428. (שימו לב: מחוץ לתחום יש רצפים ארוכים יותר – למשל 1 עד 10 הם עשרה מספרי הרשד עוקבים – ולכן ההשערה מנוסחת מ-10.)
טעות נפוצה: לשכוח לאפס את run כשמגיעים למספר שאינו מספר הרשד – אז נספרים כל מספרי הרשד בתחום ולא רצף של מספרים עוקבים.
// סעיף ב
public static boolean noSixInRow()
{
int run = 0; // אורך הרצף הנוכחי של מספרי הרשד עוקבים
for (int i = 10; i <= 999999; i++)
{
if (isHarshad(i))
{
run++;
if (run == 6)
{
return false;
}
}
else
{
run = 0;
}
}
return true;
}טבלת התשובות
| שאלה | סעיף | תשובה |
|---|---|---|
| 1 | א | arr = {2, 7, 3, 5, 18, 3, 15} |
| ב | למשל {7, 1, 3, 6, 5} (הופך ל־{0, 0, 0, 3, 5}). מתאים כל מערך ללא 0 שבו שלושת התאים הראשונים אי־זוגיים ו־arr[3] זוגי. | |
| 2 | א | temp < low → low − temp ; temp > high → temp − high ; אחרת 0 |
| ב | result[i] = getDeviation(temps[i], low, high) לכל i, במערך חדש | |
| ג | על מערך הסטיות: אם dev[i] > 3, או dev[i] > 0 וגם dev[i − 1] > 0 (i > 0) – מחזירים false; אחרי הלולאה true | |
| 3 | - | לולאת while עם מונה count של התאים שמולאו; בכל סיבוב מגרילים num בין 1 ל־ 37 , מחפשים אותו בתאים 0..count-1 , ורק אם לא נמצא – arr[count] = num ו־ count++; בסוף מדפיסים את המערך, ערך בכל שורה. |
| 4 | א | this.code = code; sacks = new Sack[num]; |
| ב | return this.totalWeight() > limit; | |
| ג | לכל i: אם !port[i].isOverweight(limit) – מדפיסים port[i].getCode(). | |
| 5 | 1 | סופרים passenger/freight/tanker; max = המקסימום של השלושה, min = המינימום; מחזירים max - min <= 1. |
| 2 | לכל start = 0, m, 2m, ...: all = true; כל קרון בתאים start..start+m−1 שאינו isPassenger מאפס את all; אם all נשאר true – return true. בסוף return false. | |
| 6 | א(1) | מחשבים את סכום הספרות sum (לולאת % 10 ו-/ 10 על עותק של num) ומחזירים num % sum == 0. |
| א(2) | for (i = low; i <= high; i++) if (isHarshad(i)) מדפיסים את i. | |
| ב | מונה run: גדל ב-1 בכל מספר הרשד ומתאפס במספר שאינו מספר הרשד; אם run מגיע ל-6 מחזירים false; בסוף מחזירים true. (התוצאה: true – הרצף הארוך ביותר בתחום הוא באורך 5, 131052–131056.) |