You are on page 1of 39

‫מבוא למחשב בשפת ‪C‬‬

‫הרצאה ‪ :5‬לולאות‬

‫מבוסס על השקפים שחוברו ע"י שי ארצי‪ ,‬גיתית רוקנשטיין ז"ל‪,‬‬


‫איתן אביאור וסאהר אסמיר עבור הקורס "מבוא למדעי המחשב"‪.‬‬
‫עודכן ע"י דן רביב‪ .‬עדכון אחרון‪ :‬מרס ‪ 2015‬יחיאל קמחי‬

‫נכתב על‪-‬ידי טל כהן‪ ,‬נערך ע"י איתן אביאור‪ © .‬כל הזכויות שמורות לטכניון – מכון טכנולוגי לישראל‬
‫לולאות‬

‫• קטע קוד המתבצע מספר פעמים נקרא לולאה )‪.(loop‬‬


‫• מספר החזרות עשוי להיות‪:‬‬
‫קבוע‪,‬‬ ‫–‬
‫משתנה אך נקבע לפני תחילת הלולאה‪,‬‬ ‫–‬
‫משתנה תוך כדי ביצוע הלולאה‪,‬‬ ‫–‬
‫בלתי מוגבל‪.‬‬ ‫–‬
‫• כל חזרה על ביצוע הלולאה נקראת מחזור )‪.(iteration‬‬
‫• בשפת ‪ C‬קיימים שלושה פסוקים המאפשרים ביצוע לולאות‪:‬‬
‫– פסוק ‪while‬‬
‫– פסוק ‪do-while‬‬
‫– פסוק ‪for‬‬

‫בשפת ‪ ,C‬כוחם החישובי של שלושת סוגי הפסוקים שווה‪.‬‬


‫הנכון הוא לבחור בזה שמתאים יותר לחישוב שלנו‪.‬‬

‫‪5‬הרצאה‬ ‫מבוא למחשב בשפת ‪ .C‬כל הזכויות שמורות ©‬ ‫‪2‬‬


‫לולאת ‪while‬‬

‫הינה פסוק מהצורה‪:‬‬ ‫•‬


‫‪while ( expression ) statement‬‬

‫ערכו של הביטוי ‪ expression‬נבדק שוב ושוב‪ ,‬לפני‬ ‫•‬


‫‪expression‬‬ ‫כל ביצוע של הפסוק ‪.statement‬‬
‫‪0‬‬
‫‪≠0‬‬
‫כל עוד ערך‪-‬האמת של הביטוי ‪ expression‬הינו‬ ‫•‬
‫‪statement‬‬ ‫אמת‪ ,‬יתבצע הפסוק ‪ statement‬שוב ושוב‪.‬‬

‫אם מלכתחילה ערך‪-‬האמת של הביטוי ‪expression‬‬ ‫•‬


‫הינו שקר‪ ,‬הפסוק ‪ statement‬לא יתבצע כלל‪.‬‬

‫‪5‬הרצאה‬ ‫מבוא למחשב בשפת ‪ .C‬כל הזכויות שמורות ©‬ ‫‪3‬‬


‫דוגמה ‪ :1‬חישוב עצרת‬

‫‪int‬‬ ‫;‪n‬‬ ‫• פעולת העצרת מוגדרת‬


‫‪int‬‬ ‫;‪fact = 1‬‬ ‫כדלקמן‪:‬‬
‫‪int‬‬ ‫;‪i = 1‬‬
‫‪n‬‬
‫‪scanf("%d", &n); /*success?*/‬‬ ‫‪n!= ∏ i = 1× 2 × K× n‬‬
‫‪i =1‬‬

‫{ )‪while (i <= n‬‬ ‫• יש הגדרה טובה יותר‬


‫;‪fact *= i‬‬
‫;‪i++‬‬ ‫• להלן קטע תוכנית הקוראת‬
‫}‬ ‫מספר מהקלט‪ ,‬מחשבת את‬
‫העצרת שלו‪ ,‬ומדפיסה את‬
‫;)‪printf("%d! = %d", n, fact‬‬ ‫התוצאה‪:‬‬

‫‪5‬הרצאה‬ ‫מבוא למחשב בשפת ‪ .C‬כל הזכויות שמורות ©‬ ‫‪4‬‬


‫ חישוב עצרת‬:1 ‫דוגמה‬

int i = 1, fact = 1, n;
if (scanf("%d", &n) < 1) {
printf(“Input Error\n”); ‫• בדוק הצלחת הקלט‬
return 1;
}
if (n < 0 || n > 12) { /* int = 4B */
printf(“Input out of range\n”); ‫• בדוק ערך בתחום‬
return 1; ‫המתאים‬
}
while (i <= n) {
fact *= i;
i++;
}

printf("%d! = %d", n, fact);

‫הרצאה‬5 © ‫ כל הזכויות שמורות‬.C ‫מבוא למחשב בשפת‬ 5


‫דוגמה ‪ :1‬חישוב עצרת‬

‫;‪int i = 2, fact = 1, n‬‬ ‫• לא צריך לולאה עבור ‪0 ,1‬‬

‫{ )‪if (scanf("%d", &n) < 1‬‬


‫;)”‪printf(“Input Error\n‬‬
‫;‪return 1‬‬
‫}‬

‫{ )‪while (i <= n‬‬


‫• הגדל את ‪ i‬בעת הכפל‬
‫;‪fact *= i++‬‬
‫}‬

‫;)‪printf("%d! = %d", n, fact‬‬

‫‪5‬הרצאה‬ ‫מבוא למחשב בשפת ‪ .C‬כל הזכויות שמורות ©‬ ‫‪6‬‬


‫דוגמה ‪ :1‬חישוב עצרת‬

‫;‪int i = 1, fact = 1, n‬‬ ‫• התחל ב‪1-‬‬

‫{ )‪if (scanf("%d", &n) < 1‬‬


‫;)”‪printf(“Input Error\n‬‬
‫;‪return 1‬‬
‫}‬
‫• הגדל בעת הבדיקה‪ ,‬עם‬
‫{ )‪while (++i <= n‬‬ ‫הערך החדש‬
‫;‪fact *= i‬‬
‫• )לכן ‪ i‬מתחיל ב‪(1-‬‬
‫}‬
‫• לא מומלץ – ממש לא‬
‫;)‪printf("%d! = %d", n, fact‬‬

‫‪5‬הרצאה‬ ‫מבוא למחשב בשפת ‪ .C‬כל הזכויות שמורות ©‬ ‫‪7‬‬


‫דוגמה ‪ :2‬חישוב ממוצע – ניתוח ותכנון‬

‫• קלט‪ :‬סדרה של מספרים שלמים ≠ ‪ 0‬ובסיומם המספר ‪.0‬‬


‫• פלט‪ :‬ממוצע המספרים בסדרה )לא כולל ה‪.(0-‬‬

‫• הנחות‪ :‬הקלט חוקי‪:‬‬


‫– הסדרה מכילה רק מספרים שלמים ≠ ‪, 0‬‬
‫– יש לפחות מספר אחד בסדרה )מלבד ה‪ 0-‬המציין את הסוף(‪.‬‬

‫• אלגוריתם‪:‬‬
‫– ספירת מספר המספרים‪ ,‬וחישוב סכומם באופן איטרטיבי )על ידי‬
‫לולאה(‪.‬‬
‫– לאחר סיום הלולאה‪ :‬הסכום מחולק במספר המספרים = הממוצע‪.‬‬

‫‪5‬הרצאה‬ ‫מבוא למחשב בשפת ‪ .C‬כל הזכויות שמורות ©‬ ‫‪8‬‬


‫ חישוב ממוצע – מימוש‬:2 ‫דוגמה‬

#include <stdio.h>
int main()
{
int sum = 0, n = 0, value;
printf("Please insert a natural number: ");
scanf("%d", &value); /* Successful? Never trust users */
while (value) {
sum += value;
n++;
printf("Please insert a natural number: ");
scanf("%d", &value); /* Successful? */
}
printf("\nThe mean of the %d numbers is %.2f.\n",
n, (double)sum / n);
return 0;
}

‫הרצאה‬5 © ‫ כל הזכויות שמורות‬.C ‫מבוא למחשב בשפת‬ 9


‫לולאת ‪do-while‬‬

‫הינה פסוק מהצורה‪:‬‬ ‫•‬


‫‪statement‬‬ ‫;) ‪do statement while ( expression‬‬

‫ערכו של הביטוי ‪ expression‬נבדק שוב ושוב‪,‬‬ ‫•‬


‫‪≠0‬‬ ‫לאחר כל ביצוע של הפסוק ‪.statement‬‬
‫‪expression‬‬
‫כל עוד ערך‪-‬האמת של הביטוי ‪ expression‬הינו‬ ‫•‬
‫‪0‬‬
‫אמת‪ ,‬יתבצע הפסוק ‪ statement‬שוב ושוב‪.‬‬

‫אפילו אם כבר מלכתחילה ערך‪-‬האמת של הביטוי‬ ‫•‬


‫‪ expression‬הינו שקר‪ ,‬הפסוק ‪statement‬‬
‫יתבצע )לפחות פעם אחת(‪.‬‬

‫‪5‬הרצאה‬ ‫מבוא למחשב בשפת ‪ .C‬כל הזכויות שמורות ©‬ ‫‪10‬‬


‫ חישוב ממוצע – מימוש נוסף‬:2 ‫דוגמה‬

‫פלט מופיעים רק‬/‫פסוקי הקלט‬


#include <stdio.h>
‫פעם אחת בתוכנית‬
int main()
{
int sum = 0, n = 0; value;

do {
printf("Please insert a natural number: ");
scanf("%d", &value);
if ( value > 0 ) { ‫אבל בדיקת הערך מוכפלת‬
sum += value;
n++;
} ‫ נדירה‬do-while ‫בשימוש נכון לולאת‬
} while (value);
printf("\nThe mean of the %d numbers is %.2f.\n",
n, (double)sum / n);
return 0;
}

‫הרצאה‬5 © ‫ כל הזכויות שמורות‬.C ‫מבוא למחשב בשפת‬ 11


‫אתחול‬ ‫תנאי‬ ‫קידום‬ ‫לולאת ‪for‬‬
‫הינה פסוק מהצורה‪:‬‬ ‫•‬
‫‪for ( exp1; exp2; exp3 ) statement‬‬
‫‪exp1‬‬
‫בתחילה מחושב ‪) exp1‬פעם אחת בלבד(‪.‬‬ ‫•‬
‫אחר כך מתחיל ביצוע הלולאה‪ ,‬בדומה ללולאת ‪:while‬‬ ‫•‬
‫‪exp2‬‬ ‫– ערכו של הביטוי ‪ exp2‬נבדק שוב ושוב‪ ,‬לפני כל ביצוע של הפסוק‬
‫‪0‬‬ ‫‪.statement‬‬
‫‪≠0‬‬
‫– כל עוד ערך‪-‬האמת של הביטוי ‪ exp2‬הינו אמת‪ ,‬יתבצע הפסוק‬
‫‪ statement‬שוב ושוב‪.‬‬
‫‪statement‬‬
‫– אם כבר מלכתחילה ערך‪-‬האמת של הביטוי ‪ exp2‬הינו שקר‪,‬‬
‫הפסוק ‪ statement‬לא יתבצע כלל‪.‬‬
‫‪exp3‬‬ ‫בנוסף לכך‪ ,‬הביטוי ‪ exp3‬מחושב שוב ושוב‪ ,‬לאחר כל ביצוע‬ ‫•‬
‫של הפסוק ‪) statement‬לפני הבדיקה החוזרת של ‪.(exp2‬‬
‫כל אחד משלושת הביטויים יכול להיות ריק )בלי תלות בחבריו(‪:‬‬ ‫•‬
‫– אם הביטוי ‪ exp1‬ו‪/‬או ‪ exp3‬ריקים‪ ,‬אין מה לחשב את ערכם‪.‬‬
‫– אם הביטוי ‪ exp2‬ריק‪ ,‬ערכו אמת )שונה מלולאת ‪.(while‬‬
‫‪5‬הרצאה‬ ‫מבוא למחשב בשפת ‪ .C‬כל הזכויות שמורות ©‬ ‫‪12‬‬
‫ – דוגמאות‬for ‫לולאת‬

int n, i, fact;

scanf("%d", &n); :‫• חישוב עצרת‬


for (i = 1, fact = 1; i <= n; i++) {
fact *= i;
}

printf("%d! = %d", n, fact);

int sum, i;

n
:∑
2
for (i = 1, sum = 0; i <= n; i++) i ‫• חישוב הביטוי‬
sum += i * i; i =1

‫הרצאה‬5 © ‫ כל הזכויות שמורות‬.C ‫מבוא למחשב בשפת‬ 13


‫ – דוגמאות אלטרנטיביות‬for ‫לולאת‬

int n, i, fact; :‫חישוב עצרת‬


scanf("%d", &n);

for (i = 1, fact = 1; i <= n; fact *= i++)


; /* Empty */

printf("%d! = %d", n, fact);


n
: ∑ ‫חישוב הביטוי‬
i 2

i =1
int sum, i;

for (i = 1, sum = 0; i <= n; sum += i * i, i++)


; /* Empty */

‫הרצאה‬5 © ‫ כל הזכויות שמורות‬.C ‫מבוא למחשב בשפת‬ 14


‫דוגמה ‪ :3‬מציאת מינימום‬

‫• יש למצוא את המספר הקטן ביותר‪ ,‬מתוך סדרת מספרים נתונה באורך ‪.n‬‬
‫– נניח כי הערך של ‪ n‬נקלט )או חושב( לפני‪-‬כן‪.‬‬
‫• ניסיון ראשון‪:‬‬
‫;‪int min = 0‬‬
‫;‪int i‬‬
‫;‪int num‬‬

‫{ )‪for (i = 0; i < n; i++‬‬


‫;)‪scanf("%d", &num‬‬
‫)‪if (min > num‬‬
‫;‪min = num‬‬ ‫‪/* Found new minimum */‬‬
‫}‬

‫מה הבעיה בגרסה זו?‬

‫‪5‬הרצאה‬ ‫מבוא למחשב בשפת ‪ .C‬כל הזכויות שמורות ©‬ ‫‪15‬‬


‫דוגמה ‪ :3‬מציאת מינימום – תיקון‬

‫• הבעיה בגרסה הראשונה הייתה שהמשתנה ‪ min‬אותחל ל‪.0-‬‬


‫– אם כל המספרים בסדרה חיוביים‪ ,‬נקבל מינימום של ‪ 0‬במקום את המספר‬
‫הקטן בסדרה‪.‬‬
‫• ניסיון שני‪:‬‬
‫;‪int min, i‬‬
‫;‪int num‬‬

‫;)‪scanf("%d", &min‬‬

‫{ ) ‪for (i = 1; i < n; i++‬‬


‫;)‪scanf("%d", &num‬‬
‫)‪if (min > num‬‬
‫;‪min = num‬‬ ‫‪/* Found new minimum */‬‬
‫}‬

‫‪5‬הרצאה‬ ‫מבוא למחשב בשפת ‪ .C‬כל הזכויות שמורות ©‬ ‫‪16‬‬


‫דוגמה ‪ :3‬מציאת מינימום – תיקון‬

‫• הבעיה בגרסה השנייה הייתה שכפול פעולת הקלט‪.‬‬


‫– שכפול קוד הוא באג פוטנציאלי הוא באג‪.‬‬
‫• שיפור‪:‬‬

‫>‪#include <limits.h‬‬
‫;‪int min = INT_MAX, i‬‬

‫{ )‪for (i = 1; i < n; i++‬‬


‫;‪int num‬‬
‫;)‪scanf("%d", &num‬‬
‫)‪if (min > num‬‬
‫;‪min = num‬‬ ‫‪/* Found new minimum */‬‬
‫}‬

‫‪5‬הרצאה‬ ‫מבוא למחשב בשפת ‪ .C‬כל הזכויות שמורות ©‬ ‫‪17‬‬


‫דיוק בבדיקת תנאי הלולאה – דיון צדדי חשוב‬

‫• האם שתי הלולאות הבאות מבצעות אותה פעולה?‬


‫;‪for (x = 0.0‬‬ ‫;‪x != 3.0‬‬ ‫)‪x += 1.0/7.0‬‬ ‫{‬ ‫}‬ ‫אינסופית?‬
‫;‪for (x = 0.0‬‬ ‫< ‪x‬‬ ‫;‪3.0‬‬ ‫)‪x += 1.0/7.0‬‬ ‫{‬ ‫אחת יותר? }‬

‫• בגלל חוסר הדיוק בייצוג מספרים ממשיים‪ ,‬אנו עלולים "לפספס" את‬
‫הגבול המדויק של סיום הלולאה )בכל אחת מהשתיים לעיל!(‪.‬‬
‫;‪for (i = 0, x = 0.0‬‬ ‫< ‪i‬‬ ‫{ )‪21; i++, x += 1.0/7.0‬‬ ‫}‬
‫• כדי לוודא שאנו עוצרים את הלולאה נכון נעדיף איטרטורים שלמים‪.‬‬
‫– בדוגמה השלישית‪ ,‬ככל שנתקדם ערכי ‪ x‬יסטו מהערכים המצופים‬

‫;‪for (i = 0, x = 0.0‬‬ ‫< ‪i‬‬ ‫{ )‪21; i++, x = i / 7.0‬‬ ‫}‬

‫• הלולאה הרביעית נותנת את המיטב שאפשר לקבל!‬

‫‪5‬הרצאה‬ ‫מבוא למחשב בשפת ‪ .C‬כל הזכויות שמורות ©‬ ‫‪18‬‬


‫שקילות בין סוגי הלולאות‬

‫• בשפת ‪ ,C‬שלושת סוגי הלולאות – ‪ – for do-while while‬שקולים‪.‬‬


‫– כל מה שניתן לעשות בעזרת סוג אחד‪ ,‬ניתן לעשות בעזרת השניים האחרים‪.‬‬
‫• למשל‪ ,‬החלפת לולאת ‪ do-while‬בלולאת ‪:while‬‬

‫ראשית‪ statement ,‬יבוצע פעם אחת‪.‬‬ ‫‪.1‬‬


‫אחר‪-‬כך‪ ,‬נבדוק את ‪ ,cond‬ואם הוא מתקיים‪,‬‬ ‫‪.2‬‬ ‫{ ‪do‬‬
‫נבצע שוב את ‪.statement‬‬ ‫;‪statement‬‬
‫ושוב נבדוק את ‪ ,cond‬ואם הוא עדין מתקיים‬ ‫‪.3‬‬ ‫;)‪} while (cond‬‬
‫שוב נבצע את ‪ ,statement‬וכן הלאה‪.‬‬

‫;‪statement‬‬
‫בדיוק אותו דבר!‬
‫{ )‪while (cond‬‬
‫;‪statement‬‬
‫)שימו לב בהמשך(‬
‫}‬

‫‪5‬הרצאה‬ ‫מבוא למחשב בשפת ‪ .C‬כל הזכויות שמורות ©‬ ‫‪19‬‬


‫שקילות בין סוגי הלולאות – מורה נבוכים‬

‫• שאר השקילויות תוצגנה בשיעורי התרגול‪.‬‬


‫• באיזה סוג לולאה לבחור? תלוי מה אנו רוצים לעשות!‬
‫• למשל‪:‬‬
‫– אם ללולאה יש התחלה‪ ,‬התקדמות וקו‪-‬סיום ברורים‪ ,‬עדיף‬
‫להשתמש ב‪.for -‬‬
‫– אם ביטוי התנאי משתנה בצורה מורכבת זה יבוצע‬
‫בגוף הלולאה‪ ,‬ונעדיף להשתמש ב‪.while -‬‬
‫– השימוש ב‪ do-while -‬הוא חריג למדי )ראו בהמשך(‪.‬‬

‫‪5‬הרצאה‬ ‫מבוא למחשב בשפת ‪ .C‬כל הזכויות שמורות ©‬ ‫‪20‬‬


‫פסוקי ‪ continue‬ו‪break -‬‬

‫• אלה הם פסוקים מיוחדים לצורך שבירת רצף הביצוע של לולאה‪.‬‬

‫• ‪continue‬‬
‫– מפסיק את ביצוע האיטרציה הנוכחית של גוף הלולאה‪ ,‬בודק את התנאי‬
‫ומתחיל מייד את ביצוע האיטרציה הבאה‪.‬‬
‫• בלולאת ‪ for‬ביטוי הקידום )‪ (exp3‬יבוצע בכל זאת‪ ,‬כי הקידום שייך‬
‫לאיטרציה הבאה‪.‬‬

‫• ‪break‬‬
‫– מפסיק את ביצוע גוף הלולאה‪ ,‬ומדלג מייד לפסוק הבא לאחר הלולאה‪.‬‬
‫– כזכור‪ ,‬פסוק ‪ break‬משמש גם בתוך מבנה ‪ – switch‬גם שם הוא‬
‫מפסיק את פעולת המבנה‪ ,‬ומדלג מייד לפסוק הבא לאחריו‪.‬‬

‫‪5‬הרצאה‬ ‫מבוא למחשב בשפת ‪ .C‬כל הזכויות שמורות ©‬ ‫‪21‬‬


‫דוגמה פשוטה לשימוש ב‪continue -‬‬
‫• המשימה‪:‬‬
‫– קרא סדרה של ‪ 100‬מספרים מהקלט‪,‬‬
‫– חשב את הסכום של המספרים החיוביים שבסדרה‪.‬‬

‫{ )‪for (i = 0; i < 100; i++‬‬


‫;)‪scanf("%d",&num‬‬
‫)‪if (num > 0‬‬ ‫{ ) ‪for (i = 0; i < 100; i++‬‬
‫;‪sum += num‬‬ ‫;)‪scanf("%d",&num‬‬
‫}‬ ‫)‪if (num <= 0‬‬
‫;‪continue‬‬
‫;‪sum += num‬‬
‫}‬
‫• שני הקטעים שקולים‪.‬‬
‫• השימוש ב‪ continue-‬נוח יותר כאשר‪:‬‬
‫– ההמשך ארוך‪,‬‬
‫– במיוחד אם יש בהמשך עוד ועוד סיבות לפסוקי ‪ continue‬נוספים‪.‬‬
‫‪5‬הרצאה‬ ‫מבוא למחשב בשפת ‪ .C‬כל הזכויות שמורות ©‬ ‫‪22‬‬
‫דוגמה פשוטה לשימוש ב‪break -‬‬
‫• המשימה‪:‬‬
‫– קרא סדרה של עד ‪ 100‬מספרים חיוביים מהקלט )עצור כשמופיע מספר שלילי(‪,‬‬
‫– חשב את הסכום של המספרים החיוביים שבסדרה‪.‬‬
‫;‪num = 0‬‬
‫{ )‪for (i = 0; i < 100 && num >= 0; i++‬‬
‫;)‪scanf("%d",&num‬‬ ‫;‪for (i = 0; i < 100‬‬ ‫{ )‪i++‬‬
‫)‪if (num > 0‬‬ ‫;)‪scanf("%d",&num‬‬
‫;‪sum += num‬‬ ‫)‪if (num < 0‬‬
‫}‬ ‫;‪break‬‬
‫;‪sum += num‬‬
‫}‬
‫• שני הקטעים שקולים‪.‬‬
‫• השימוש ב‪ break-‬נוח יותר‪:‬‬
‫– ה‪ for-‬נקי יותר‪ ,‬כי התנאי מבטא את העיקר )וה‪ break-‬את החריג(‬
‫– ההמשך עשוי להיות ארוך‪,‬‬
‫– עשויות להיות בהמשך עוד ועוד סיבות לפסוקי ‪ break‬נוספים‪.‬‬
‫‪5‬הרצאה‬ ‫מבוא למחשב בשפת ‪ .C‬כל הזכויות שמורות ©‬ ‫‪23‬‬
‫לולאות אינסופיות‬

‫• לולאה אינסופית היא לולאה )מכל סוג( בה ערך ביטוי הבקרה הינו‬
‫תמיד אמת )שונה מ‪.(0-‬‬
‫• התוכנית נתקעת בתוך הלולאה‪ ,‬לא מתקדמת‪ ,‬ולא מסתיימת‪.‬‬

‫• שגיאות תכנות עלולות לגרום בטעות ללולאה אינסופית‪.‬‬


‫– במקרה כזה אין מנוס מעצירת התוכנית מבחוץ‪:‬‬
‫• באמצעות מערכת ההפעלה )אם מתאפשר(‪,‬‬
‫• ע"י הורדת מערכת ההפעלה באופן מבוקר )במקרים חמורים(‪,‬‬
‫• ע"י כיבוי המחשב )במקרים עוד יותר חמורים(‪.‬‬

‫) ‪while( 1‬‬ ‫} … {‬ ‫• ניתן לייצר לולאות אינסופיות במכוון‪ ,‬לדוגמה‪:‬‬


‫) ; ; (‪for‬‬ ‫} … {‬

‫• במידת הצורך‪ ,‬היציאה מלולאה אינסופית מכוונת תיעשה באמצעות‬


‫פסוק ‪) break‬או פסוק ‪.(return‬‬
‫‪5‬הרצאה‬ ‫מבוא למחשב בשפת ‪ .C‬כל הזכויות שמורות ©‬ ‫‪24‬‬
‫ חישוב ממוצע – המימוש הקודם‬:2 ‫דוגמה‬

#include <stdio.h>
int main() ‫פלט מופיעים רק‬/‫פסוקי הקלט‬
{ ‫פעם אחת בתוכנית‬
int sum = 0, cntr = 0; value;
do {
printf("Please insert a natural number: ");
if (scanf("%d", &value) < 1) {
printf(“Input Error\n”);
return 1;
}
‫אבל בדיקת הערך מוכפלת‬
if (value > 0) {
sum += value;
num ++;
} ‫ נדירה‬do-while ‫בשימוש נכון לולאת‬
} while (value);
printf("\nThe mean of the %d numbers is %.2f.\n",
num, (double)sum / num);
return 0;
}

‫הרצאה‬5 © ‫ כל הזכויות שמורות‬.C ‫מבוא למחשב בשפת‬ 25


‫ חישוב ממוצע – מימוש נוסף‬:2 ‫דוגמה‬

#include <stdio.h>
int main()
{ "‫לולאה "אינסופית‬
int sum = 0, cntr = 0;
while (1) { ‫ מוגבל ללולאה‬value
int value;
printf("Please insert a natural number: ");
if (scanf("%d", &value) < 1) {
printf(“Input Error\n”); ‫פלט‬/‫אין שכפול פסוקי הקלט‬
return 1;
}
if (value <= 0) break; ‫בדיקת ערך יחידה‬
sum += value;
num ++;
} do-while ‫לא צריך‬
printf("\nThe mean of the %d numbers is %.2f.\n",
num, (double)sum / num);
return 0;
}

‫הרצאה‬5 © ‫ כל הזכויות שמורות‬.C ‫מבוא למחשב בשפת‬ 26


‫עוד על שקילות בין סוגי הלולאות‬

‫• בשפת ‪ ,C‬שלושת סוגי הלולאות – ‪ – for do-while while‬שקולים‪.‬‬


‫– כל מה שניתן לעשות בעזרת סוג אחד‪ ,‬ניתן לעשות בעזרת השניים האחרים‬
‫– אבל לא תמיד מדובר בתרגום "מכני"!‬
‫• למשל‪ ,‬החלפת לולאת ‪ for‬בלולאת ‪:while‬‬

‫ראשית‪ exp1 ,‬יבוצע פעם אחת‪.‬‬ ‫‪.1‬‬


‫אחר‪-‬כך‪ ,‬נבדוק את ‪ ,exp2‬ואם הוא‬ ‫‪.2‬‬ ‫)‪for (exp1; exp2; exp3‬‬
‫מתקיים‪ ,‬נבצע את ‪.statement‬‬ ‫‪statement‬‬
‫נחשב את ‪ exp3‬ונעבור ל‪2-‬‬ ‫‪.3‬‬

‫;‪exp1‬‬
‫בדיוק אותו דבר!‬ ‫{ )‪while (exp2‬‬
‫‪statement‬‬
‫בתנאי ש‪statement -‬‬ ‫;‪exp3‬‬
‫!‬ ‫אינו מכיל ‪continue‬‬ ‫}‬

‫‪5‬הרצאה‬ ‫מבוא למחשב בשפת ‪ .C‬כל הזכויות שמורות ©‬ ‫‪27‬‬


‫דוגמה ‪ :4‬אלגוריתם לבדיקת ראשוניות‬

‫• בהינתן שלם חיובי ‪ ,n‬יש לקבוע האם הוא ראשוני‪.‬‬


‫• ‪ n‬איננו ראשוני אם ורק אם יש ל‪ n-‬מחלק בתחום‪.2, 3, …, n−1 :‬‬

‫;‪int is_prime = 1‬‬


‫;‪int n, i‬‬

‫;)‪scanf("%d", &n‬‬

‫{ )‪if (n == 1‬‬
‫;‪is_prime = 0‬‬
‫}‬
‫{ ‪else‬‬
‫)‪for (i = 2; i < n; ++i‬‬
‫)‪if (n % i == 0‬‬
‫;‪is_prime = 0‬‬
‫}‬

‫‪5‬הרצאה‬ ‫מבוא למחשב בשפת ‪ .C‬כל הזכויות שמורות ©‬ ‫‪28‬‬


‫בדיקת ראשוניות‪ :‬הצעות ייעול‬

‫לאחר שמסתבר ש‪ n-‬אינו מתחלק ב‪ ,2-‬כלומר הוא אי‪-‬זוגי‪,‬‬ ‫•‬


‫>‪#include <math.h‬‬ ‫ניתן לדלג על בדיקת שאר המחלקים הזוגיים‪.‬‬
‫‪...‬‬
‫מספיק לבדוק מחלקים אפשריים עד שורש ‪.(√n ) n‬‬ ‫•‬
‫;‪int is_prime = 1‬‬
‫;‪int n, i‬‬
‫‪...‬‬
‫{ ))‪if (n == 1 || (n != 2 && n % 2 == 0‬‬
‫;‪is_prime = 0‬‬
‫{ ‪} else‬‬
‫;)‪int sqrt_n = (int)(sqrt(n) + 0.5‬‬
‫)‪for (i = 3; i <= sqrt_n; i += 2‬‬
‫)‪if (n % i == 0‬‬
‫;‪is_prime = 0‬‬ ‫איזה חוסר יעילות נותר‬
‫}‬ ‫כאן בכל זאת?‬

‫‪5‬הרצאה‬ ‫מבוא למחשב בשפת ‪ .C‬כל הזכויות שמורות ©‬ ‫‪29‬‬


‫בדיקת ראשוניות‪ :‬הצעות ייעול‬

‫לאחר שמסתבר ש‪ n-‬אינו מתחלק ב‪ ,2-‬כלומר הוא אי‪-‬זוגי‪,‬‬ ‫•‬


‫>‪#include <math.h‬‬ ‫ניתן לדלג על בדיקת שאר המחלקים הזוגיים‪.‬‬
‫‪...‬‬
‫מספיק לבדוק מחלקים אפשריים עד שורש ‪.(√n ) n‬‬ ‫•‬
‫;‪int is_prime = 1‬‬
‫;‪int n, i‬‬
‫‪...‬‬
‫‪if‬‬ ‫{ ))‪(n == 1 || (n != 2 && n % 2 == 0‬‬
‫;‪is_prime = 0‬‬
‫{ ‪} else‬‬
‫;))‪int sqrt_n = (int)(sqrt(n) + 0.5‬‬
‫{ )‪for (i = 3; i <= sqrt_n; i += 2‬‬
‫{ )‪if (n % i == 0‬‬
‫;‪is_prime = 0‬‬ ‫‪ n‬הוא פריק – אין טעם להמשיך לבדוק‬
‫;‪break‬‬
‫}‬
‫}‬
‫}‬
‫‪5‬הרצאה‬ ‫מבוא למחשב בשפת ‪ .C‬כל הזכויות שמורות ©‬ ‫‪30‬‬
‫קצת מתורת המספרים ‪...‬‬

‫משפט "אלגוריתם החילוק" )החלקי( קובע כי לכל שני מספרים טבעיים‪d ≠ 0 ,n :‬‬ ‫•‬
‫קיימים שני מספרים טבעיים ‪ r ,q‬כך ש‪:‬‬
‫– מתקיים‪n = q × d + r :‬‬
‫– מתקיים‪0 ≤ r < d :‬‬
‫– יש רק זוג אחד ‪ q‬ו‪ r-‬המקיים את שני התנאים הללו‪.‬‬
‫המספרים המשתתפים נקראים בשמות‪:‬‬ ‫•‬
‫– ‪ n‬מחולק )‪(dividend‬‬
‫– ‪ d‬מחלק )‪(divisor‬‬
‫)‪(quotient‬‬ ‫– ‪ q‬מנה‬
‫– ‪ r‬שארית )‪(remainder‬‬
‫ביצוע החישוב‪:‬‬ ‫•‬
‫– שיטה ‪ :1‬החסר את ‪ d‬מ‪ n-‬שוב ושוב )כל עוד היתרה איננה שלילית(‪.‬‬
‫מספר החיסורים הוא המנה‪ ,‬והיתרה היא השארית‪.‬‬
‫– שיטה ‪ :2‬בצע אלגוריתם חילוק מהיר‪ ,‬למשל "חילוק ארוך" שלומדים בביה"ס‪.‬‬
‫• ניתן להרחיב את המשפט גם למספרים שליליים‪.‬‬
‫‪5‬הרצאה‬ ‫מבוא למחשב בשפת ‪ .C‬כל הזכויות שמורות ©‬ ‫‪31‬‬
‫עוד קצת מתורת המספרים ‪...‬‬

‫אם בחלוקת ‪ n‬ב‪ d-‬השארית )‪ (r‬שווה ל‪ ,0-‬נאמר כי ‪ d‬מחלק את ‪.n‬‬ ‫•‬


‫לכל מספר טבעי גדול מ‪ 1-‬יש לפחות שני מחלקים‪ ,1 :‬והוא עצמו‪.‬‬ ‫•‬
‫מספר טבעי גדול מ‪ 1-‬שאין לו מחלקים נוספים נקרא ראשוני‪.‬‬ ‫•‬
‫• ‪ d‬נקרא מחלק משותף של ‪ n‬ו‪ m-‬אם ורק אם הוא מחלק גם את ‪ n‬וגם את ‪.m‬‬

‫למה ‪ d :1‬עשוי לחלק את ‪ n‬רק אם ‪.d ≤ n‬‬ ‫•‬


‫למה ‪ :2‬מחלק משותף של ‪ n‬ו‪ m-‬חייב להיות קטן או שווה לשני המחולקים גם יחד‪.‬‬ ‫•‬
‫למה ‪ :3‬לכל שני מספרים טבעיים ‪ n‬ו‪ m-‬יש לפחות מחלק משותף אחד‪.1 :‬‬ ‫•‬
‫מסקנה‪ :‬לכל שני מספרים טבעיים ‪ n‬ו‪ m-‬יש מחלק משותף מקסימאלי‪.‬‬ ‫•‬

‫המחלק המשותף הגדול ביותר )‪ (Greatest Common Divisor‬של שני מספרים‬ ‫•‬
‫טבעיים ‪ n‬ו‪ m-‬מסומן ע"י‪.gcd(n, m) :‬‬

‫‪5‬הרצאה‬ ‫מבוא למחשב בשפת ‪ .C‬כל הזכויות שמורות ©‬ ‫‪32‬‬


‫חשיבותו של המחלק המשותף הגדול ביותר )‪(GCD‬‬

‫• המחלק המשותף הגדול ביותר של שני מספרים חשוב בכמה הקשרים‪,‬‬


‫למשל‪:‬‬
‫‪n‬‬
‫‪n‬‬ ‫)‪gcd( n, m‬‬ ‫– ע"מ לצמצם במהירות שבר‪:‬‬
‫=‬
‫‪m‬‬ ‫‪m‬‬
‫)‪gcd( n, m‬‬

‫– ע"מ למצוא מכפלה משותפת קטנה ביותר )‪ ,lcm(n, m‬למשל לצורך‬


‫חיבור שברים‪:‬‬
‫‪lcm(n, m) × gcd(n, m) = n × m‬‬

‫) ‪lcm(b, d‬‬ ‫) ‪lcm(b, d‬‬


‫×‪a‬‬ ‫×‪c‬‬
‫‪a‬‬ ‫‪c‬‬ ‫‪b‬‬ ‫‪d‬‬
‫‪+‬‬ ‫=‬ ‫‪+‬‬
‫‪b‬‬ ‫‪d‬‬ ‫) ‪lcm(b, d‬‬ ‫) ‪lcm(b, d‬‬
‫‪5‬הרצאה‬ ‫מבוא למחשב בשפת ‪ .C‬כל הזכויות שמורות ©‬ ‫‪33‬‬
‫חישוב המחלק המשותף הגדול ביותר )‪(GCD‬‬

‫• שיטת ביה"ס היסודי‪:‬‬


‫– פרק את שני המספרים ‪ n‬ו‪ m-‬לגורמים ראשוניים‪.‬‬
‫– מכפלת הגורמים הראשוניים המשותפים היא ה‪.gcd(n,m)-‬‬
‫– דוגמה‪:‬‬
‫‪n = 1350 = 2 × 33 × 52‬‬
‫‪m = 700 = 22 × 52 × 7‬‬
‫‪gcd(1350, 700) = 2 × 52 = 50‬‬
‫• מכפלת החיתוך של סדרות הגורמים הראשוניים היא ה‪.gcd-‬‬
‫• מכפלת האיחוד של סדרות הגורמים הראשוניים היא ה‪:lcm-‬‬
‫‪lcm(1350, 700) = 22 × 33 × 52 × 7 = 18,900‬‬
‫‪gcd(1350, 700) × lcm(1350, 700) = 23 × 33 × 54 × 7 = 945,000‬‬
‫‪gcd(n,m) × lcm(n,m) = n × m‬‬ ‫• שימו לב כי מתקיים‬

‫‪5‬הרצאה‬ ‫מבוא למחשב בשפת ‪ .C‬כל הזכויות שמורות ©‬ ‫‪34‬‬


‫פירוק מספר לגורמים ראשוניים‬
‫המשפט היסודי של האריתמטיקה קובע כי כל מספר טבעי גדול מ‪1-‬‬ ‫•‬
‫יכול להיכתב בצורה יחידה כמכפלה של גורמים ראשוניים )מסודרים(‪.‬‬
‫אלגוריתם לפירוק מספר טבעי ‪) n‬גדול מ‪ (1-‬לגורמיו הראשוניים‪:‬‬ ‫•‬
‫– התחל עם יתרה ‪ m‬שערכה ההתחלתי הוא ‪ ,n‬וסדרת גורמים ריקה‪.‬‬
‫– נסה שוב ושוב‪:‬‬
‫• לחלק את ‪ m‬בכל אחד מהמספרים )הראשוניים( ‪ p‬אשר בתחום ‪2..√m‬‬
‫• אם ‪ m‬אכן מתחלק ב‪:p-‬‬
‫– הוסף את ‪ p‬לסדרת הגורמים‪,‬‬
‫– חלק את היתרה ‪) m = m / p‬וגם תחום הסריקה ‪ 2..√m‬קטן בהתאם(‪.‬‬
‫– הוסף את היתרה ‪ m‬לסדרת הגורמים‪.‬‬

‫דוגמה עבור ‪:n = 90‬‬ ‫•‬


‫→ ‪90 = 2 × 45‬‬ ‫‪45 = 3 ×15‬‬ ‫→‬ ‫‪15 = 3 × 5 → 5 = 5‬‬
‫קושי‪ :‬האלגוריתם אינו מעשי עבור ‪ n‬גדול‪ .‬למשל‪ ,‬עבור מספר בן ‪ 18‬ספרות עלול‬ ‫•‬
‫להיווצר הצורך לעבור על כל המספרים )הראשוניים( עד ‪.1,000,000,000‬‬
‫– מסיבה זו‪ ,‬פירוק מספר לגורמים ראשוניים משמש בסיס לקריפטוגרפיה מודרנית‪.‬‬
‫‪5‬הרצאה‬ ‫מבוא למחשב בשפת ‪ .C‬כל הזכויות שמורות ©‬ ‫‪35‬‬
‫ועוד קצת מתורת המספרים ‪...‬‬
‫• עבור ‪ d ,m ,n‬ו‪ k-‬טבעיים הגדולים מ‪:0-‬‬
‫אם ‪ d‬מחלק את ‪ n‬אזי ‪ d‬מחלק את ‪ n+d‬ואת ‪.n–d‬‬ ‫–‬
‫אם ‪ d‬מחלק את ‪ n‬אזי ‪ d‬מחלק את ‪ n+kd‬ואת ‪.n–kd‬‬ ‫–‬
‫אם ‪ d‬מחלק את ‪ n‬ואת ‪ ,m‬אזי ‪ d‬מחלק את ‪ n+m‬ואת ‪,n–m‬‬ ‫–‬
‫ולכן גם את ‪) n % m‬גם אם ‪.(n > m‬‬
‫קבוצת המחלקים המשותפים של ‪ n‬ושל ‪m‬‬ ‫–‬
‫שווה לקבוצת המחלקים המשותפים של ‪ n–m‬ושל ‪,m‬‬
‫• ולכן גם לקבוצת המחלקים המשותפים של ‪ n % m‬ושל ‪.m‬‬
‫המחלק המשותף הגדול ביותר של ‪ n‬ושל ‪m‬‬ ‫–‬
‫שווה למחלק המשותף הגדול ביותר של ‪ n–m‬ושל ‪,m‬‬
‫• ולכן גם למחלק המשותף הגדול ביותר של ‪ n % m‬ושל ‪.m‬‬
‫)‪gcd(n, m) = gcd(n–m, m) = gcd(n % m, m‬‬
‫‪n>0‬‬ ‫‪→ gcd(n, n) = gcd(n, 0) = n‬‬
‫‪5‬הרצאה‬ ‫מבוא למחשב בשפת ‪ .C‬כל הזכויות שמורות ©‬ ‫‪36‬‬
‫האלגוריתם של אוקלידס לחישוב ‪GCD‬‬
‫שני מספרים טבעיים גדולים מ‪ n ,0-‬ו‪.m-‬‬ ‫• הקלט‪:‬‬
‫)‪.gcd(n,m‬‬ ‫• הפלט‪:‬‬
‫• שיטה ‪) 1‬האלגוריתם המקורי(‪:‬‬
‫– כל עוד ‪ n ≠ m‬חזור‪:‬‬
‫• אם ‪ n > m‬החלף את ‪ n‬ב‪.n–m :‬‬
‫אריסטו‬ ‫• ואם ‪ m > n‬החלף את ‪ m‬ב‪.m–n :‬‬
‫– ‪ n = m‬ועל כן‪.gcd(n,m) = gcd(n,n) = n :‬‬

‫• שיטה ‪) 2‬מתבססת על אלגוריתם חילוק(‪:‬‬


‫חזור‪:‬‬ ‫– כל עוד ‪m > 0‬‬
‫‪ n‬ב‪. n % m :‬‬ ‫• החלף את‬
‫‪ n‬ו‪) m -‬כעת מובטח ש‪.(n > m -‬‬ ‫• החלף בין‬
‫‪.gcd(n,m) = gcd(n,0) = n‬‬ ‫– ‪ m = 0‬ועל כן‪:‬‬
‫אוקלידס‬
‫האלגוריתם היה כנראה ידוע ל‪-‬אֹודֹוקְסּוס מ‪ְ -‬קנִידֶס )‪ 375) (Εὔδοξος ὁ Κνίδιος‬לפנה"ס(‪.‬‬ ‫•‬
‫ִיסטֹו )‪ (Ἀριστοτέλης‬רומז עליו בכתביו )‪ 330‬לפנה"ס(‪.‬‬ ‫ֲאר ְ‬ ‫•‬
‫אֹוקלִידֶס )‪ (Εὐκλείδης‬הציג אותו ביצירתו "יסודות" )ספר ‪ ,7‬משפט ‪ 300) (2‬לפנה"ס(‪.‬‬ ‫ְ‬ ‫•‬
‫‪5‬הרצאה‬ ‫מבוא למחשב בשפת ‪ .C‬כל הזכויות שמורות ©‬ ‫‪37‬‬
‫הדגמת האלגוריתם של אוקלידס )בשיטה ‪(2‬‬

‫דוגמה ‪2‬‬ ‫דוגמה ‪1‬‬

‫‪m‬‬ ‫‪n‬‬ ‫‪n = q ×m + r‬‬ ‫‪n‬‬ ‫‪m‬‬


‫‪100‬‬ ‫‪17‬‬ ‫‪700 = 0 × 1350 +700‬‬ ‫‪700‬‬ ‫‪1350‬‬
‫‪17‬‬ ‫‪15‬‬ ‫‪1350 = 1 × 700 + 650‬‬ ‫‪1350‬‬ ‫‪700‬‬

‫‪15‬‬ ‫‪2‬‬ ‫‪700 = 1 × 650 + 50‬‬ ‫‪700‬‬ ‫‪650‬‬

‫‪2‬‬ ‫‪1‬‬ ‫‪650 = 13 × 50 + 0‬‬ ‫‪650‬‬ ‫‪50‬‬


‫‪50‬‬ ‫‪0‬‬
‫‪1‬‬ ‫‪0‬‬

‫‪gcd(100, 17) = 1‬‬ ‫‪gcd(1350, 700) = 50‬‬

‫שני מספרים שאין להם מחלק משותף הגדול מ‪ 1-‬נקראים זרים‬


‫‪5‬הרצאה‬ ‫מבוא למחשב בשפת ‪ .C‬כל הזכויות שמורות ©‬ ‫‪38‬‬
‫קידוד האלגוריתם של אוקלידס‬
#include <stdio.h>
int main() (‫ )האלגוריתם המקורי‬1 ‫שיטה‬
{
unsigned int n, m; ‫ נותן אשליית בטיחות‬unsigned
int m, n;
scanf("%u%u", &n, &m); scanf("%d%d", &n, &m);
if (n == 0 || m == 0) {
printf("gcd = %u\n", n + m);
return m+n == 0 ? 1 : 0; /* gcd(0,0) is Error */
} if (m < 0) m = -m;
if (n < 0) n = -n;
for ‫לולאת‬
while (n != m) while (m != 0) { ‫איננה מתאימה‬
if (n > m) int t = m;
n -= m; m = n % m;
else n = t;
m -= n; }
printf("gcd = %u\n", n); printf("gcd = %d\n", n);
return 0;
} (‫ )שימוש באלגוריתם חילוק‬2 ‫שיטה‬
‫הרצאה‬5 © ‫ כל הזכויות שמורות‬.C ‫מבוא למחשב בשפת‬ 39

You might also like