מבוא למחש ב ב שפת C הרצאה 5: לולאות מבוסס על השקפים שחוברו ע"י שי ארצי, גיתית רוקנשטיין ז"ל, איתן אביאור וסאהר אסמיר עבור הקורס "מבוא למדעי המחשב". עודכן ע"י דן רביב. עדכון אחרון: מרס 2015 יחיאל קמחי נכתב על-ידי טל כהן, נערך ע"י איתן אביאור. כל הזכויות שמורות לטכניון מכון טכנולוגי לישראל
לולא ות קטע קוד המתבצע מספר פעמים נקרא לולאה.(loop) מספר החזרות עשוי להיות: קבוע, משתנה אך נקבע לפני תחילת הלולאה, משתנה תוך כדי ביצוע הלולאה, בלתי מוגבל. כל חזרה על ביצוע הלולאה נקראת מחזור.(iteration) בשפת C קיימים שלושה פסוקים המאפשרים ביצוע לולאות: פסוק while פסוק do-while פסוק for בשפת C, כוחם החישובי של שלושת סוגי הפסוקים שווה. הנכון הוא לבחור בזה שמתאים יותר לחישוב שלנו. 2
לולאת while הינה פסוק מהצורה: while ( expression ) statement expression 0 ערכו של הביטוי expression נבדק שוב ושוב, כל ביצוע של הפסוק.statement לפנ י 0 statement כל עוד ערך-האמת של הביטוי expression הינו אמת, יתבצע הפסוק statement שוב ושוב. אם מלכתחילה ערך-האמת של הביטוי expression הינו שקר, הפסוק statement לא יתבצע כלל. 3
דוגמה 1: חי שוב ע צרת int n; int fact = 1; int i = 1; scanf("%d", &n); /*success?*/ while (i <= n) { fact *= i; i++; printf("%d! = %d", n, fact); פעולת העצרת מוגדרת כדלקמן: n n! = i = 1 2 K n i= 1 יש הגדרה טובה י ות ר להלן קטע תוכנית הקוראת מספר מהקלט, מחשבת את העצרת שלו, ומדפיסה את התוצאה: 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
דוגמה 1: חי שוב ע צרת int i = 2, fact = 1, n; לא צריך לול אה עב ור 0 1, if (scanf("%d", &n) < 1) { printf( Input Error\n ); return 1; while (i <= n) { fact *= i++; הגדל את i בעת הכפל printf("%d! = %d", n, fact); 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); 7
דוגמה 2: חי שוב מ מוצע ניתוח ותכנ ו ן קלט: סדרה של מספרים שלמים 0 ובסיומם המספר 0. פלט: ממוצע המספרים בסדרה (לא כולל ה- 0 ). הנחות: הקלט חוקי: הסדרה מכילה רק מספרים שלמים 0, יש לפחות מספר אחד בסדרה (מלבד ה- 0 המציין את הסוף). אלגוריתם: ספירת מספר המספרים, וחישוב סכומם באופן איטרטיבי (על ידי לולאה). לאחר סיום הלולאה: הסכום מחולק במספר המספרים = הממוצע. 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; 9
לולאת do-while statement 0 expression 0 הינה פסוק מהצורה: do statement while ( expression ); ערכו של הביטוי expression נבדק שוב ושוב, לאחר כל ביצוע של הפסוק.statement כל עוד ערך-האמת של הביטוי expression הינו אמת, יתבצע הפסוק statement שוב ושוב. אפילו אם כבר מלכתחילה ערך-האמת של הביטוי statement הינו שקר, הפסוק expression יתבצע (לפחות פעם אחת). 10
דוגמה 2: חי שוב מ מוצע מימוש נ וס ף #include <stdio.h> int main() { int sum = 0, n = 0; value; פסוקי הקלט/פ לט מופ יעים רק פעם אחת ב תו כני ת do { printf("please insert a natural number: "); scanf("%d", &value); אבל בדיקת הערך מוכפלת { ) 0 > value if ( sum += value; n++; בשימוש נכון לולאת do-while נדירה while (value); printf("\nthe mean of the %d numbers is %.2f.\n", n, (double)sum / n); return 0; 11
לולאת for קידום תנאי אתחול for ( exp 1 ; exp 2 ; exp 3 ) הינה פסוק מהצורה: statement exp 1 exp 2 0 statement exp 3 0 (פעם אחת בלבד). בתחילה מחושב exp 1 אחר כך מתחיל ביצוע הלולאה, בדומה ללולאת :while exp 2 נבדק שוב ושוב, לפני כל ביצוע של הפסוק ערכו של הביטוי.statement exp 2 הינו אמת, יתבצע הפסוק כל עוד ערך-האמת של הביטוי statement שוב ושוב. exp 2 הינו שקר, אם כבר מלכתחילה ערך-האמת של הביטוי לא יתבצע כלל. הפסוק statement בנוסף לכך, הביטוי exp 3 מחושב שוב ושוב, לאחר כל ביצוע של הפסוק statement (לפני הבדיקה החוזרת של.(exp 2 כל אחד משלושת הביטויים יכול להיות ריק (בלי תלות בחבריו): exp 3 ריקים, אין מה לחשב את ערכם. exp 1 ו/או אם הביטוי.(while ריק, ערכו אמת (שונה מלולאת exp 2 אם הביטוי 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; for (i = 1, sum = 0; i <= n; i++) sum += i * i; n : i= 1 i 2 חישוב הב יטו י 13
לולאת for דוגמ אות אלטר נט יביות int n, i, fact; scanf("%d", &n); חישוב עצרת: for (i = 1, fact = 1; i <= n; fact *= i++) ; /* Empty */ printf("%d! = %d", n, fact); int sum, i; n : i= 1 i 2 חישוב הב יטו י for (i = 1, sum = 0; i <= n; sum += i * i, i++) ; /* Empty */ 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 */ מה הבעיה בגרסה זו? 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 */ 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 */ 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) { הלו לאה הר ביע ית נות נת א ת המיטב שאפשר לקבל! 18
שקיל ות בין סוג י הלולא ות בשפת C, שלושת סוגי הלולאות while for do-while שקולים. כל מה שניתן לעשות בעזרת סוג אחד, ניתן לעשות בעזרת השניים האחרים. למשל, החלפת לולאת do-while בלולאת :while ראשית, statement יבוצע פעם אחת. אחר-כך, נבדוק את,cond ואם הוא מתקיים, נבצע שוב את.statement ושוב נבדוק את,cond ואם הוא עדין מתקיים שוב נבצע את,statement וכן הלאה..1.2.3 do { statement; while (cond); בדיוק אותו דבר! (שימו לב בהמשך) statement; while (cond) { statement; 19
שקיל ות בין סוג י הלולא ות מורה נבו כים שאר השקילויות תוצגנה בשיעורי התרגול. באיזה סוג לולאה לבחור? למשל: תלוי מה אנו רוצים לעשות! אם ללולאה יש התחלה, התקדמות וקו-סיום ברורים, להשתמש ב-.for אם ביטוי התנאי משתנה בצורה מורכבת זה יבוצע בגוף הלולאה, ונעדיף להשתמש ב-.while השימוש ב- do-while הוא חריג למדי עדיף (ראו בהמשך). 20
פסוקי continue ו- break אלה הם פסוקים מיוחדים לצורך שבירת רצף הביצוע של לולאה. continue מפסיק את ביצוע האיטרציה הנוכחית של גוף הלולאה, בודק את התנאי ומתחיל מייד את ביצוע האיטרציה הבאה. בלולאת for ביטוי הקידום ) 3 (exp יבוצע בכל זאת, כי הקידום שייך לאיטרציה הבאה. break מפסיק את ביצוע גוף הלולאה, ומדלג מייד לפסוק הבא לאחר הלולאה. כזכור, פסוק break משמש גם בתוך מבנה switch גם שם הוא מפסיק את פעולת המבנה, ומדלג מייד לפסוק הבא לאחריו. 21
דוגמה פשו טה לשי מוש ב- continue המשימה: קרא סדרה של 100 מספרים מהקלט, חשב את הסכום של המספרים החיוביים שבסדרה. 22 שני הקטעים שקולים. השימוש ב- continue נוח יותר כאשר: ההמשך ארוך, במיוחד אם יש בהמשך עוד ועוד סיבות לפסוקי for (i = 0; i < 100; i++) { scanf("%d",&num); if (num > 0) sum += num; continue נוס פים. for (i = 0; i < 100; i++ ) { scanf("%d",&num); if (num <= 0) continue; sum += num;
דוגמה פשו טה לשי מוש ב- break המשימה: קרא סדרה של עד 100 מספרים חיוביים מהק לט חשב את הסכום ש ל המספרים החיובי ים שבסדרה. שני הקטעים שקולים. השימוש ב- break נוח יותר: ה- for נקי יותר, כי התנאי מבטא את העיקר ) הו ההמשך עשוי להיות ארוך, עשויות להיות בהמשך עוד ועוד סיבות לפסוקי (עצור כשמופי ע מספר שליל י), num = 0; for (i = 0; i < 100 && num >= 0; i++) { break- את החריג) scanf("%d",&num); if (num > 0) sum += num; break נוספים. for (i = 0; i < 100; i++) { scanf("%d",&num); if (num < 0) break; sum += num; 23
לולא ות אי נסופ יות לולאה אינסופית היא לולאה (מכל סוג) בה ערך ביטוי הבקרה הינו תמיד אמת (שונה מ- 0 ). התוכנית נתקעת בתוך הלולאה, לא מתקדמת, ולא מסתיימת. שגיאות תכנות עלולות לגרום בטעות ללולאה אינסופית. במקרה כזה אין מנוס מעצירת התוכנית מבחוץ: באמצעות מערכת ההפעלה (אם מתאפשר), ע"י הורדת מערכת ההפעלה באופן מבוקר (במקרים חמורים), ע"י כיבוי המחשב (במקרים עוד יותר חמורים). ניתן ל ייצר ל ולא ות אי נסופ יו ת במכוו ן, { ) 1 while( לדוגמה: for( ; ; ) { במידת הצורך, היציאה מלולאה אינסופית מכוונת תיעשה באמצעות פסוק break (או פסוק.(return 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; 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) { אין שכפול פסוקי הקלט/פלט Error\n ); printf( Input 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; 26
עוד על ש קיל ות בין סוגי הלו לא ות בשפת C, שלושת סוגי הלולאות while for do-while שקולים. כל מה שניתן לעשות בעזרת סוג אחד, ניתן לעשות בעזרת השניים האחרים אבל לא תמיד מדובר בתרגום "מכני"! למשל, החלפת לולאת for בלולאת :while ראשית, exp1 יבוצע פעם אחת. אחר-כך, נבדוק את,exp2 ואם הוא מתקיים, נבצע את.statement נחשב את exp3 ונעבור ל- 2.1.2.3 for (exp1; exp2; exp3) statement! בדיוק אותו דבר! בתנאי ש- statement continue אינ ו מכיל exp1; while (exp2) { statement exp3; 27
דוגמה 4: אלג ו רית ם לבדיקת רא שו ני ו ת.2, 3,, n 1 בהינתן שלם חיובי n, יש לקבוע האם הוא ראשוני. n איננו ראשוני אם ורק אם יש ל- n מחלק בתחום: 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; 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; איזה חוסר יעילות נותר כאן בכל זאת? 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; 30
קצת מתורת המספרי ם... משפט "אלגוריתם החילוק" (החלקי) קובע כי לכל שני מספרים טבעיים: d 0 n, קיימים שני מספרים טבעיים r q, כך ש: n = q d + r מתקיים: 0 r < d מתקיים: יש רק זוג אחד q ו- r המקיים את שני התנאים הללו. המספרים המשתתפים נקראים בשמות: (dividend) מחולק n (divisor) מחלק d (quotient) מנה q (remainder) שארית r ביצוע החישוב: d מ- n שוב ושוב (כל עוד היתרה איננה שלילית). שיטה 1: החסר את מספר החיסורים הוא המנה, והיתרה היא השארית. שיטה 2: בצע אלגוריתם חילוק מהיר, למשל "חילוק ארוך" שלומדים בביה"ס. ניתן להרחיב את המשפט גם למספרים שליליים. 31
עוד קצת מתורת ה מספרים....m אם בחלוקת n ב- d השארית (r) שווה ל- 0, נאמר כי d מחלק את n. לכל מספר טבעי גדול מ- 1 יש לפחות שני מחלקים: 1, והוא עצמו. מספר טבעי גדול מ- 1 שאין לו מחלקים נוספים נקרא ראשוני. d נקרא מחלק משותף של n ו- m אם ורק אם הוא מחלק גם את n וגם את למה 1: d עשוי לחלק את n רק אם d. n למה 2: מחלק משותף של n ו- m חייב להיות קטן או שווה לשני המחולקים גם יחד. למה 3: לכל שני מספרים טבעיים n ו- m יש לפחות מחלק משותף אחד: 1. מסקנה: לכל שני מספרים טבעיים n ו- m יש מחלק משותף מקסימאלי. המחלק המשותף הגדול ביותר Divisor) (Greatest Common טבעיים n ו- m מסומן ע"י: (m.gcd(n, של שני מספרים 32
חשיבותו של המחלק המשותף הגדול ביותר (GCD) המחלק המשותף הגדול ביותר של שני מספרים חשוב בכמה הקשרים, למשל: n n gcd( n, m) ע"מ לצמצם במהירות שבר: = m m gcd( n, m) ע"מ למצוא מכפלה משותפת קטנה ביותר (m,lcm(n, למשל לצורך חיבור שברים: lcm( n, m) gcd( n, m) = n m a b + c d = lcm( b, d) a b lcm( b, d) + lcm( b, d) c d lcm( b, d) 33
חיש וב המחלק המשותף הגדול בי ותר (GCD) שיטת ביה"ס היסודי: פרק את שני המספרים n ו- m לגורמים ראשוניים. מכפלת הגורמים הראשוניים המשותפים היא ה-( gcd(n,m. n = 1350 = 2 3 3 5 2 m = 700 = 2 2 5 2 7 gcd(1350, 700) = 2 5 2 = 50 דוגמה: מכפלת החיתוך של סדרות הגורמים הרא שונ יים ה יא ה- gcd. מכפלת האיחוד של סדרות הגורמים הרא שונ יים ה יא ה- lcm : lcm(1350, 700) = 2 2 3 3 5 2 7 = 18,900 gcd(1350, 700) lcm(1350, 700) = 2 3 3 3 5 4 7 = 945,000 gcd(n,m) lcm(n,m) = n m שימו לב כ י מתקיים 34
פיר וק מספ ר לגו רמ ים ר אש ונ יי ם המשפט היסודי של האריתמטיקה קובע כי כל מספר טבעי גדול מ- 1 יכול להיכתב בצורה יחידה כמכפלה של גורמים ראשוניים (מסודרים). n (גדול מ- 1 ) לגורמיו הראשוניים: אלגוריתם לפירוק מספר טבעי m שערכה ההתחלתי הוא n, וסדרת גורמים ריקה. התחל עם יתרה נסה שוב ושוב: לחלק את m בכל אחד מהמספרי ם (הר אשוניים) p אשר בתחום 2.. m אם אכן מתחלק ב- p : m הוסף את חלק את היתרה p לסדרת הגורמים, m = m / p (וגם תחום הסריקה הוסף את הי תרה m לסדר ת הג ורמים. m..2 קטן בהתאם). דוגמה עבור = 90 :n 90 = 2 45 45 = 3 15 15 = 3 5 5 = 5 קושי: האלגוריתם אינו מעשי עבור n גדול. למשל, עבור מספר בן 18 ספרות עלול להיווצר הצורך לעבור על כל המספרים (הראשוניים) עד 1,000,000,000. מסיבה זו, פיר וק מספר לג ורמים ראשוניים משמש בסיס לקר יפטו גרפיה מו דרני ת. 35
ועו ד ק צת מתורת המספרים... עבור אם d m, n, ו- k טבעיים הגדולים מ- 0 : d מחלק את n אזי d מחלק את.nd ואת n+d אם d מחלק את n אזי d מחלק את.nkd ואת n+kd אם d מחלק את n ואת m, אזי d מחלק את ולכן גם את n % m (גם אם.(n > m,nm ואת n+m קבוצת המחלקים המשותפים של n ושל m שווה לקבוצת המחלקים המשותפים של nm ושל,m ולכן גם לקבוצת המחלקים המשותפים של.m ושל n % m המחלק המשותף הגדול ביותר של n ושל m שווה למחלק המשותף הגדול ביותר של nm ושל,m ולכן גם למחלק המשותף הגדול ביותר של.m ושל n % m gcd(n, m) = gcd(nm, m) = gcd(n % m, m) n > 0 gcd(n, n) = gcd(n, 0) = n 36
האלגו ריתם של או קלי דס לחי שוב GCD ו- m. n הקלט: הפלט: שני מספרים טבעיים גדולים מ- 0,.gcd(n,m) שיטה 1 (האלגוריתם המקורי): אריסטו כל עוד n m חזור: אם n > m החלף את n ב:.nm ואם m > n החלף את m ב:.mn.gcd(n,m) = gcd(n,n) = n ועל כן: n = m שיטה 2 (מתבססת על אלגוריתם חילוק): כל עוד > 0 m חזור: n ב:.n % m החלף את n ו- m (כעת מובטח ש- n). > m החלף בין.gcd(n,m) = gcd(n,0) = n ועל כן: m = 0 האלגוריתם היה כנראה ידוע ל-אוֹדוֹק סוּס מ-ק נ יד ס (ΕὔδοξοςὁΚνίδιος) (375 לפנה"ס). א ר יס טוֹ (Ἀριστοτέλης) רומז עליו בכתביו אוֹק ל יד ס (Εὐκλείδης) הציג אותו ביצירתו (330 לפנה"ס). "יסודות" (ספר 7, משפט אוקלידס (2 300) לפנה"ס). 37
הדגמת האלגור יתם של א וקל ידס (בש יטה 2) 2 דוגמה דוגמה 1 m n n = q m + r n m 100 17 17 15 15 2 2 1 1 0 gcd(100, 17) = 1 700 = 0 1350 +700 1350 = 1 700 + 650 700 = 1 650 + 50 650 = 13 50 + 0 700 1350 1350 700 700 650 650 50 50 0 gcd(1350, 700) = 50 שני מספרים שאין להם מחלק מ שותף הג דול מ- 1 נקראים זר ים 38
קיד וד ה אלגו ר יתם של א וקל ידס #include <stdio.h> int main() { unsigned int n, m; 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 */ while (n!= m) if (n > m) n -= m; else m -= n; printf("gcd = %u\n", n); return 0; שיטה 1 (האלגוריתם המקורי) if (m < 0) m = -m; if (n < 0) n = -n; while (m!= 0) { int t = m; m = n % m; n = t; unsigned נותן אשליית בטיחות printf("gcd = %d\n", n); לולאת for איננה מתאימה שיטה 2 (שימוש באלגוריתם חילוק) 39