SEC-S20W5 Iteration in Programming: For, While, and Do-While Loops
17 comments
EN
About the problem that has passed from lesson to lesson several times.
“From 1 to 4329” or to make it easier to think, “From 1 to 100.” How can we, using only the operations +1
and x2
, reach exactly 100? Obviously, performing x2
will get us closer to the goal faster, but when should we stop and switch to +1
? After all, performing x2
seven times will overshoot, reaching 128, and it will take a long time to go from 64 to 100 using only +1
.
So, what's the conclusion? Clearly, x2
gets us closer to the goal faster than +1
, but it’s not clear which operation to choose at each step. That’s the challenge of the task.
Let's flip the task completely. Instead of going from 1 to 100, let's go from 100 to 1, not with +1
, but -1
, not x2
, but /2
. And here’s the magic – the complexity of the task is also reversed. It goes from complex to simple. Now it's not difficult to decide which operation to perform – divide by 2 or subtract 1. Since dividing by 2 moves us faster, we will divide by 2 when the number is even, and subtract 1 when it’s odd. That’s it. Now it looks obvious and easy. There’s a saying: "As easy as rolling off a log," and I liked the English equivalent "Easy peasy lemon squeezy."
Last time we were introduced to the first if then else
construction. It was an incomplete form, meaning without else
– if then
allows us to break the flow of the program by skipping some commands. Imagine a program with 8 commands: 1, 2, 3, 4, 5, 6, 7, 8. Using if
, you can organize the program’s flow so that it skips commands 3, 4, 5, and 6, and goes straight to command 7. That means commands 3, 4, 5, and 6 can be skipped.
1 ...
2 if(x>2)
3 ...
4 ...
5 ...
6 ...
7 ...
8 ...
The question arises – can we jump back? Meaning, can we repeat a certain series of commands? Yes, we can do this, for example, like this:
1 ...
2 ...
3 ...
4 ...
5 ...
6 ...
7 if(x>2)
8 ...
But how do we distinguish between an if
that skips commands forward and an if
that jumps back to repeat commands? That’s why the second type of if
is called while
, and the place where you should jump to repeat is marked as do
.
1 ...
2 ...
3 do ...
4 ...
5 ...
6 ...
7 while(x>2)
8 ...
These sections with repeated code are called
Loops
So, the previous illustrative example will now look like this:
1 ...
2 ...
3 do ...
4 ...
5 ...
6 ...
7 while(x>2)
8 ...
All the commands between do
and while
will repeat until the condition becomes false. However, this could lead to an infinite loop! To prevent this, the variables used in the condition must be changed within the loop.
Using a loop, it’s no longer a problem to reach 100, 1000, or even 1,000,000 with just one command +1
. In words, the algorithm will be: add one, check if it’s already 100, and repeat. The corresponding flowchart will be:
Let’s write the code for this:
#include <iostream>
using namespace std;
int main()
{
int x = 1;
do
{
x = x + 1;
} while (x < 100);
cout << "100 reached, x = " << x << endl;
return 0;
}
This type of loop is called a postcondition loop, meaning the condition is checked after the actions are performed. The actions will be executed at least once, regardless of the condition.
What will this code output?
#include <iostream>
using namespace std;
int main()
{
int x = 100;
do
{
x = x + 1;
} while (x < 100);
cout << "100 reached, x = " << x << endl;
return 0;
}
In this case, x
is already 100, but x = x + 1
will still execute, and x
will reach 101. This type of loop is not suitable for this program.
We need to check the condition before performing the addition.
Precondition loop
There is another type of loop, a precondition loop.
#include <iostream>
using namespace std;
int main()
{
int x = 100;
while (x < 100)
{
x = x + 1;
}
cout << "100 reached, x = " << x << endl;
return 0;
}
It’s similar to an incomplete if
without else
. That is, if the condition is not met, the block of code will not be executed.
1) Precondition loop
while(condition)
{
block of code
}
2) Postcondition loop
do
{
block of code
} while(condition);
3) Counter loop
Usually, I start discussing loops with this type. In C language, it is written in this form: for(initialization; condition; step)
.
- Initialization is done only once when organizing the loop process.
- Condition – the loop continues to execute as long as this condition is true.
- Step – variables that change are usually placed here, to ensure the loop terminates.
Nowadays, it’s more accurate to call this a for loop rather than a counter loop. For example, in Pascal, the loop only existed in two forms: for i := 1 to 10 do
– a loop with a step of 1 from 1 to 10. for i := 10 downto 1 do
– a loop with a step of -1 from 10 to 1. Note the limitation of Pascal – the step could only be +1 or -1
. In Basic, the loop step was specified via STEP
: FOR i=1 TO 5 STEP 0.1
, and could be positive, negative, integer, or real. I wonder – could the step in Basic be set as a variable and changed within the loop? I'll have to check that out someday.
Nowadays, the for
loop has more possibilities and can look like this: for(;;)
– an infinite loop. for(;x<100;)
– equivalent to the while(x<100)
loop. for(int a=23, b=-5; a>0 || b>0; a=b-1, b++)
– this example shows that multiple variables can be declared and initialized in the first section, the condition can be complex, and several operations can be performed in the step section.
These are equally valid forms of loops, meaning any task can be solved with them. Some are just more suitable for specific problems than others.
Examples with loops.
Example #1: task with a postcondition loop
Write a program to input a number from 10 to 20.
The number should be requested from the user at least once, and if it's incorrect, repeat the input.
do
{
cout<<"Enter a number from 10 to 20: ";
cin>> x;
} while(x < 10 || x > 20);
Let’s check it (mentally executing the code): for example, if the user enters x = 4
, the left operand in the while
statement will be true, and the loop will repeat. Similarly, if the user enters 34, the second operand will be true, and the loop will repeat. But if the user enters 15, neither operand will be true, so the entire condition in the while
statement will be false, and the loop will exit.
Example #2: task with a counter loop
A simple task is generating different sequences. For example, here’s a fragment of code that generates even numbers up to 36.
for(int k=2; k<=36; k+=2)
{
cout<<k<<" ";
}
Sometimes, the loop step should be 1
, i.e., k++
, and the initial value k=1
:
for(int k=1; k<=9; k++)
{
cout<<"x ";
}
How, then, to output even numbers from the previous task?
How can we calculate the final value of k? You can do it like this:
for(int k=1; k<=18; k++)
{
cout << 2*k << " ";
}
In this case, while the variable k runs through the values 1, 2, 3... up to 18, multiplying by 2 will yield even numbers. So, 2k will iterate through the even numbers: 2, 4, 6, 8, ... And if in the first case the boundary for k was 36, now we can say that the boundary for 2k is 36.
k | cout |
---|---|
1 | 2 |
2 | 4 |
3 | 6 |
4 | 8 |
5 | 10 |
.... | .... |
18 | 36 |
We'll get the same output as in the first version, but it's more convenient to start at 2 and increment the counter by 2 at each step. However, in the future, it will often be necessary to iterate from 1 with a step of 1.
for(int k=1; 2*k<=36; k++)
{
cout << 2*k << " ";
}
To be even more precise, we should start from 0, with a step of 1, because in programming, everything often begins from zero. For homework, it's preferable to use a step of 1 and start from zero to receive a higher grade. But you can do it as you wish; the grade will just be slightly lower.
Homework
1⳾ Why are loops necessary in programming? Which type of loop did you like the most?
2⳾In task #8 from the last lesson, the condition for determining if buildings are on the same side of the street should have been written as:
if(n%2 == m%2)
instead of:
if(n%2==0 && m%2==0 || n%2==1 && m%2==1)
Explain both conditions in words. Which one is better and why?
3⳾Output the number 33 exactly 33 times using each type of loop.
4⳾Output numbers from 11 to 111: 11, 21, 31... 101, 111, but skip the number 51 (using one type of loop). (Tasks 1-4: 1 point)
5⳾Output the multiplication table for a random number, for example, for 0:
1 x 0 = 0
2 x 0 = 0
3 x 0 = 0
...
10 x 0 = 0
6⳾Output the squares of numbers: 1, 4, 9, 16, 25, 36, 49... 400.
7⳾Output the powers of some number: 1, 2, 4, 8, 16, 32, 64, 128... less than 3000.
8⳾How many even four-digit numbers are there that are not divisible by 3? (Tasks 5-8: 1.5 points)
Rules for Submission
You can publish your work in any language, in any community, or simply in your own blog. Please provide a link to your work in the comments below this text.
To help me quickly find, check, and grade your work, leave a comment with the link to your submission under this post. In your work, include the tag #sec20w5sergeyk.
Recommend that two or three of your friends join as well.
Submit your work between Monday, 07 October 2024, and Sunday, 13 October 2024.
All your works will be evaluated, and I will select the top five submissions.
UA
Про задачу що переходила з уроку в урок вже декілька раз.
"Від 1 до 4329" або щоб простіше думати "Від 1 до 100", як виконуючи лиш операції +1
та x2
дійти рівно до 100. Ясно що виконуючи x2
ми рухатимемося до цілі швидше, але як вчасно зупинитися, коли слід переходити на +1
? Адже виконавши 7 раз x2
буде перебір, ми дійдемо до 128. але від попереднього кроку 64 до 100 +1
дуже довго.
Тобто який висновок? - очевидно що x2
швидше наближає до цілі ніж +1
, але невідомо яку ж операцію обирати на кожному кроці. У цьому й складність задачі.
Давайте перевернемо все в задачі до навпаки. Будемо йти не від 1 до 100, а від 100 до 1, не +1
а -1
, не x2
- а /2
. І ось воно чудо - складність задачі теж розвернулася на протилежну, вона із складної стала простою. Тепер нам не важко вирішувати яку ж операцію виконувати - ділити на 2 чи віднімати 1. Так як діленням на 2 рухатися швидше - то будемо ділити на 2 коли число парне, та віднімати 1 коли воно не парне. І все. Все виглядає тепер дуже очевидним і простим. Є така приказка - "як дурню з гори котитися", шукав до неї англомовний відповідник мені сподобалося "Easy peasy lemon squeezy".
Минулого разу ми познайомилася з першою конструкцією if then else
Вона в неповній формі, тобто без else
- if then
дозволяє порушити хід виконання програми перестрибнувши, оминувши, не виконавши деякі команди. Уявіть що в програмі 8 команд, 1,2,3,4,5,6,7,8. Використавши if
можна організувати хід виконання програми так що вона омине команди 3,4,5,6 та відразу перейде до 7ї. Тобто команди 3,4,5,6 можна пропустити/перестрибнути.
1 ...
2 if(x>2)
3 ...
4 ...
5 ...
6 ...
7 ...
8 ...
Виникає питання а чи не можна перестрибнути назад? тобто повторити певну серію команд.
Можна це зробити, наприклад якось так:
1 ...
2 ...
3 ...
4 ...
5 ...
6 ...
7 if(x>2)
8 ...
Але як тоді if
що стрибає вперед і пропускає команди, відрізнити від if
який стрибає назад та повторює команди?
Тому цей другий if
називається while
, а місце до якого слід перестрибнути для повтору позначають do
.
1 ...
2 ...
3 ...
4 ...
5 ...
6 ...
7 if(x>2)
8 ...
Такі от ділянки з повторенням коду називаються
Цикли
Отже наведений мною попередній ілюстративний приклад тепер запишеться так
1 ...
2 ...
3 do ...
4 ...
5 ...
6 ...
7 while(x>2)
8 ...
І всі команди що знаходяться між do
та while
буде повторюватися до тих під, доки істинна умова, доки виконується умова. Але це ж призведе до нескінченної кількості повторів!! Отже щоб не відбулося зациклювання змінні що використовуються в умові мають змінюватися в циклі.
Використавши цикл, тепер не проблема лиш однією командою '+1' дійти від 1 до 100, до 1000 - чи навіть до 1'000'000.
Словесно алгоритм буде таким: додати одиницю, перевірити чи вже 100, і повторити.
Відповідно буде така блок схема:
Складемо код для цього
#include <iostream>
using namespace std;
int main()
{
int x = 1;
do
{
x = x + 1;
} while (x < 100);
cout << "100 reached, x = " << x << endl;
return 0;
}
Цей варіант циклу є циклом з післяумовою
, тобто умова перевіряється вже після виконання дій. А дії виконаються принаймні один раз, незалежно від умови.
Що виведе даний код?
#include <iostream>
using namespace std;
int main()
{
int x = 100;
do
{
x = x + 1;
} while (x < 100);
cout << "100 reached, x = " << x << endl;
return 0;
}
В цьому випадку х вже 100, а x=x+1 все рівно виконається і x досягне 101. Такий вид циклу не підходить до такої програми.
Нам треба перевірити умову перш ніж виконувати додавання.
цикл з передумовою
Є ще один варіант циклу, цикл з передумовою
.
#include <iostream>
using namespace std;
int main()
{
int x = 100;
while (x < 100)
{
x = x + 1;
}
cout << "100 reached, x = " << x << endl;
return 0;
}
Це схоже на неповний if
без else
, Тобто якщо умова не виконується то і блок коду виконуватися не буде.
1) цикл з передумовою
while(умова)
{
блок коду
}
2) цикл з післяумовою
do
{
блок коду
}while(умова);
3) цикл з лічильником
Зазвичай розповідаючи про цикли я починаю з нього,
на мові C він запишеться за такою формою
for(ініціалізація; умова виконання; крок_зміни)
- ініціалізація виконується лиш один раз про організації циклічного процесу
- умова виконання - умова при правдивості якої цикл продовжує виконання.
- крок зміни - тут зазвичай розміщуються змінні які змінюються для того щоб цикл завершився
зараз його напевне правильніше називати цикл for
ніж цикл з лічильником, наприклад в мові Pascal такий цикл був лиш у двох варіантах
for i := 1 to 10 do
був цикл з кроком 1 від 1 до 10
for i := 10 downto 1 do
був цикл з кроком -1 від 10 до 1
зверніть увагу на обмеженість мови Pascal - крок лише +1 or -1
а на мові Basic крок циклу задавався через STEP: FOR i=1 TO 5 STEP 0.1
і міг бути додатним, від'ємним, цілим або дійсним. Цікаво - колись перевірю чи можна було в Бейсику задати крок змінною, та змінювати його в циклі))
в сучасності ж цикл for
має більше можливостей і може виглядати так:
for(;;)
- вічний цикл
for(;x<100;)
- аналог циклу while(x<100)
for(int a=23, b=-5; a>0 || b>0; a=b-1, b++=)
- цим прикладом я хотів показати що при оголошенні та ініціалізації змінна в першому розділі може бути не одна, умова може бути не простою.
Це абсолютно рівноправні форми циклу, тобто будь яку задачу можна розв'язати. Просто деякі підходять більше, деякі менше.
Приклади з циклами.
Приклад #1: задача на цикл з післяумовою
Написати програму вводу числа від 10 до 20
Число слід запитувати у користувача як мінімум один раз, а при його некоректності повторити введення.
do
{
cout<<"Enter number from 10 to 20 ";
cin>> x;
}while(x<10 || x>20);
Перевіряємо(подумки, виконуючи код в голові) - наприклад користувач введе x=4
, тоді в операторі while буде істинним лівий операнд і цикл повториться,
аналогічно при вводі 34 - тут буде істинним другий операнд і цикл повториться. Але при вводі 15, ні перший ні другий операнди не будуть істиною, тому вся умова в операторі while буде хибною і цикл перерветься.
Приклад #2: задача на цикл з лічильником
саме прості завдання це генерація різних послідовностей.
ось наприклад фрагмент коду що згенерує парні числа до 36.
for(int k=2; k<=36; k+=2)
{
cout<<k<<" ";
}
часто бувають такі ситуації що крок циклу має бути 1
тобто k++
, а початкове значення k=1
. Як же тоді вивести парні числа з попередньої задачі?
Як же обчислити кінцеве значення k - можна так:
for(int k=1; k<=18; k++)
{
cout<<2*k<<" ";
}
Тобто в той час як змінна k пробігає значення 1,2,3... до 18, то помноживши на 2 одержимо парні числа. Тобто 2k пробіжить по парним числам, 2, 4 ,6 8....
І якщо межа у першому варіанті межа для k
kбула 36, то тепер можна сказати що в другому випадку межа для 2k - 36
k | cout |
---|---|
1 | 2 |
2 | 4 |
3 | 6 |
4 | 8 |
5 | 10 |
.... | .... |
18 | 36 |
Одержимо однаковий результат виконання, зручніше перший варіант від 2
- по 2
, тобто починаємо від 2, та кожного кроку збільшуємо лічильник на 2.
Але в подальшому треба буде часто користуватися від 1
- по 1
for(int k=1; 2*k<=36; k++)
{
cout<<2*k<<" ";
}
Навіть скажу ще точніше від 0
- по 1
-- - адже в комп'ютері все починається з 0
.
В домашній роботі робити крок 1 і починити з нуля бажано, для вищого балу. Але можете робити як виходить, бал буде трохи нижчий..
Домашня робота.
1⳾ Для чого цикли в програмуванні? Який із видів циклів більше сподобався?
2⳾ В задачі №8 минулого уроку умову визначення чи знаходяться будинки на одній стороні вулиці слід було записати так:
if( n%2 == m%2 )
замість if( n%2==0 && m%2==0 || n%2==1 && m%2==1)
- поясніть словами обидві умови. Яка краща і чим?
3⳾ Виведіть 33 рази число 33 кожним видом циклу.
4⳾ Виведіть числа від 11 до 111: 11, 21,31....101, 111 - але число 51 не виводьте (одним видом циклу) (1-4 - 1 бал)
5⳾. Виведіть таблицю множення на випадкове число, наприклад на 0:
1 x 0 = 0
2 x 0 = 0
3 x 0 =0
....
10 x 0 = 0
6⳾ Виведіть квадрати чисел. 1,,4,9,16,25,36,49...400
7⳾ Виведіть степені якогось числа: 1 2 4 8 16 32 64 128... менші за 3000.
8⳾ Скільки парних чотирицифрових чисел не діляться на 3
(5-8 1.5 бали)
Правила проведення
Публікувати можна на будь-якій мові, в будь якій спільноті чи просто у власному блозі, посилання на вашу роботу додайте сюди коментарем
Щоб я швидко знайшов, перевірив та оцінив ваші роботи залиште посилання в коментарі під цим текстом а в роботі поставите тег #sec20w5sergeyk
Порекомендуйте прийняти участь своїм двом-трьом друзям.
Роботи слід опублікувати з Monday 07 Oct 24 to Sunday 13 Oct 24
Всі Ваші роботи будуть мною оцінені та відібрані п'ять кращих робіт
Comments