Глава 7.1. По-сложни цикли

След като научихме какво представляват и за какво служат по-простите цикли, сега предстои да се запознаем с по-сложни такива. Те ще разширят познанията ни и ще ни помагат в решаването на по-трудни и предизвикателни задачи. Видовете, които ще разгледаме, са:

  • Цикли със стъпка
  • While цикли
  • Do-while цикли
  • Безкрайни цикли

В настоящата тема ще разберем и какво представлява операторът break, както и как чрез него да прекъснем един цикъл. Също така, използвайки try-catch конструкцията, ще се научим да следим за грешки по време на изпълнението на програмата ни.

Видео

Гледайте видео-урок по тази глава тук: https://www.youtube.com/watch?v=kaJSrGrvakQ.

Цикли със стъпка

В главата "Повторения (цикли)" научихме как работи for цикълът и вече знаем кога и с каква цел да го използваме. В тази тема ще обърнем внимание на една определена и много важна част от конструкцията му, а именно стъпката.

Какво представлява стъпката?

Стъпката е тази част от конструкцията на for цикъла, която указва с колко да се увеличи или намали стойността на водещата му променлива. Тя се декларира последна в скелета на for цикъла.

Най-често е с размер 1 и в такъв случай, вместо да пишем i += 1 или i -= 1, можем да използваме операторите i++ или i--. Ако искаме стъпката ни да е различна от 1, при увеличение използваме оператора i += (размера на стъпката), а при намаляване i -= (размера на стъпката). При стъпка 10, цикълът би изглеждал по следния начин:

Следва поредица от примерни задачи, решението на които ще ни помогне да разберем по-добре употребата на стъпката във for цикъл.

Пример: числата от 1 до N през 3

Да се напише програма, която отпечатва числата от 1 до n със стъпка 3. Например, ако n = 100, то резултатът ще е: 1, 4, 7, 10, …, 94, 97, 100.

Нека видим какви са нужните стъпки, за да може нашата програма да се изпълни коректно:

  • Вземаме числото n от входа на конзолата.
  • Създаваме for цикъл с размер на стъпката 3.
  • В тялото на цикъла отпечатваме стойността на текущата стъпка.

Тестване в Judge системата

Тествайте решението си тук: https://judge.softuni.bg/Contests/Practice/Index/659#0.

Пример: числата от N до 1 в обратен ред

Да се напише програма, която отпечатва числата от n до 1 в обратен ред (стъпка -1). Например, ако n = 100, то резултатът ще е: 100, 99, 98, …, 3, 2, 1.

Да погледнем нужните стъпки за коректното изпълнение на нашата програма:

  • Вземаме числото n от входа на конзолата.
  • Създаваме for цикъл, като присвояваме int i = n .
  • Обръщаме условието на цикъла: i >= 1.
  • Дефинираме размера на стъпката: -1.
  • В тялото на цикъла отпечатваме стойността на текущата стъпка.

Тестване в Judge системата

Тествайте решението си тук: https://judge.softuni.bg/Contests/Practice/Index/659#1.

Пример: числата от 1 до 2^n с for цикъл

В следващия пример ще разгледаме ползването на обичайната стъпка с размер 1.

Да се напише програма, която отпечатва числата от 1 до 2^n (две на степен n). Например, ако n = 10, то резултатът ще е 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024.

Тестване в Judge системата

Тествайте решението си тук: https://judge.softuni.bg/Contests/Practice/Index/659#2.

Пример: четни степени на 2

Да се отпечатат четните степени на 2 до 2^n: 2^0, 2^2, 2^4, 2^8, …, 2^n. Например, ако n = 10, то резултатът ще е 1, 4, 16, 64, 256, 1024.

Ето какви са нужните стъпки, за да може нашата програма да се изпълни коректно:

  • Създаваме променлива num за текущото число, на която присвояваме начална стойност 1.
  • За стъпка на цикъла слагаме стойност 2.
  • В тялото на цикъла: oтпечатваме стойността на текущото число и увеличаваме текущото число според условието на задачата.

Тестване в Judge системата

Тествайте решението си тук: https://judge.softuni.bg/Contests/Practice/Index/659#3.

While цикъл

Следващият цикъл, с който ще се запознаем, се нарича while. Специфичното при него е, че продължава изпълнението си, докато дадено условие е истина. Като структура се различава от тази на for цикъла, но е също толкова ефикасен и нужен.

Какво представлява while цикълът?

While цикълът се използва, когато искаме да повтаряме извършването на определена логика, докато е в сила дадено условие. Под "условие", разбираме всеки израз, който връща true или false. Когато условието стане грешно, while цикълът прекъсва изпълнението си и програмата продължава с реализацията на останалия код. Конструкцията на цикъла изглежда по този начин:

Следва поредица от примерни задачи, решението на които ще ни помогне да разберем по-добре употребата на while цикъла.

Пример: редица числа 2k+1

Да се напише програма, която отпечатва всички числа ≤ n от редицата: 1, 3, 7, 15, 31..., като приемем, че всяко следващо число = предишно число * 2 + 1

Нека видим какви са нужните стъпки, за да може нашата програма да се изпълни коректно:

  • Създаваме променлива num за текущото число, на която присвояваме начална стойност 1.
  • За условие на цикъла слагаме текущото число <= n.
  • В тялото на цикъла: oтпечатваме стойността на текущото число и увеличаваме текущото число, използвайки формулата от условието на задачата.

Тестване в Judge системата

Тествайте решението си тук: https://judge.softuni.bg/Contests/Practice/Index/659#4.

Пример: число в диапазона [1...100]

Да се въведе цяло число в диапазона [1...100]. Ако въведеното число е невалидно, да се въведе отново. В случая, за невалидно число ще считаме всяко такова, което не е в зададения диапазон.

Нека видим и нужните стъпки за успешното изпълнение на програмата ни:

  • Създаваме променлива num, на която присвояваме целочислената стойност, получена от входа на конзолата.
  • За условие на цикъла слагаме израз, който е true, ако числото от входа не е в диапазона посочен в условието.
  • В тялото на цикъла: oтпечатваме съобщение със съдържание "Invalid number!" на конзолата, след което присвояваме нова стойност за num от входа на конзолата.
  • След като вече сме валидирали въведеното число, извън тялото на цикъла отпечатваме стойността на числото.

Тестване в Judge системата

Тествайте решението си тук: https://judge.softuni.bg/Contests/Practice/Index/659#5.

Най-голям общ делител (НОД)

Преди да продължим към следващата задача, е необходимо да се запознаем с определението за най-голям общ делител (НОД).

Определение за НОД: Най-голям общ делител на две естествени числа a и b е най-голямото число, което дели едновременно и a, и b без остатък. Например:

a b НОД
24 16 8
67 18 1
12 24 12
15 9 3
10 10 10
100 88 4

Алгоритъм на Евклид

В следващата задача ще използваме един от първите публикувани алгоритми за намиране на НОД - Алгоритъм на Евклид:

Докато не достигнем остатък 0:

  • Делим по-голямото число на по-малкото.
  • Вземаме остатъка от делението.

Псевдо код за алгоритъма на Евклид:

while b ≠ 0
  var oldB = b;
  b = a % b;
  a = oldB;
print а;

Пример: най-голям общ делител (НОД)

Да се въведат цели числа a и b и да се намери НОД(a, b).

Ето какви са нужните стъпки, за да може нашата програма да се изпълни коректно:

  • Създаваме променливи a и b, на които присвояваме целочислени стойности, взети от входа на конзолата.
  • За условие на цикъла слагаме израз, който е true, ако числото b е различно от 0.
  • В тялото на цикъла следваме указанията от псевдо кода:
    • Създаваме временна променлива, на която присвояваме текущата стойност на b.
    • Присвояваме нова стойност на b, която е остатъка от делението на a и b.
    • На променливата a присвояваме предишната стойност на променливата b.
  • След като цикълът приключи и сме установили НОД, го отпечатваме на екрана.

Тестване в Judge системата

Тествайте решението си тук: https://judge.softuni.bg/Contests/Practice/Index/659#6.

Do-while цикъл

Следващият цикъл, с който ще се запознаем, е do-while, в превод - прави-докато. По структура, той наподобява while, но има съществена разлика между тях. Тя се състои в това, че do-while ще изпълни тялото си поне веднъж. Защо се случва това? В конструкцията на do-while цикъла, условието винаги се проверява след тялото му, което от своя страна гарантира, че при първото завъртане на цикъла, кодът ще се изпълни, а валидацията ще се прилага върху всяка следваща итерация на do-while.

Следва обичайната поредица от примерни задачи, чиито решения ще ни помогнат да разберем по-добре do-while цикъла.

Пример: изчисляване на факториел

За естествено число n да се изчисли n! = 1 2 3 n. Например, ако n = 5, то резултатът ще бъде: 5! = 1 2 3 4 5 = 120

Ето какви са нужните стъпки, за да може нашата програма да се изпълни коректно:

  • Създаваме променливата n, на която присвояваме целочислена стойност взета от входа на конзолата.
  • Създаваме още една променлива - fact, чиято начална стойност е 1. Нея ще използваме за изчислението и съхранението на факториела.
  • За условие на цикъла ще използваме n > 1, тъй като всеки път, когато извършим изчисленията в тялото на цикъла, ще намаляваме стойността на n с 1.
  • В тялото на цикъла:
    • Присвояваме нова стойност на fact, която е резултат от умножението на текущата стойност на fact с текущата стойност на n.
    • Намаляваме стойността на n с -1.
  • Извън тялото на цикъла отпечатваме крайната стойност на факториела.

Тестване в Judge системата

Тествайте решението си тук: https://judge.softuni.bg/Contests/Practice/Index/659#7.

Пример: сумиране на цифрите на число

Да се сумират цифрите на цяло положително число n. Например, ако n = 5634, то резултатът ще бъде: 5 + 6 + 3 + 4 = 18

Ето какви са нужните стъпки, за да може нашата програма да се изпълни коректно:

  • Създаваме променливата n, на която присвояваме стойност, равна на въведеното от потребителя число.
  • Създаваме втора променлива - sum, чиято начална стойност е 0. Нея ще използваме за изчислението и съхранението на резултата.
  • За условие на цикъла ще използваме n > 0, тъй като след всяко изчисление на резултата в тялото на цикъла, ще премахваме последната цифра от n.
  • В тялото на цикъла:
    • Присвояваме нова стойност на sum, която е резултат от събирането на текущата стойност на sum с последната цифра на n.
    • Присвояваме нова стойност на n, която е резултат от премахването на последната цифра от n.
  • Извън тялото на цикъла отпечатваме крайната стойност на сумата.

n % 10: връща последната цифра на числото n.
n / 10: изтрива последната цифра на n.

Тестване в Judge системата

Тествайте решението си тук: https://judge.softuni.bg/Contests/Practice/Index/659#8.

Безкрайни цикли и операторът break

В настоящата глава се запознахме с различни видове цикли, като научихме какви конструкции имат те и как се прилагат. Следва да разберем какво е безкраен цикъл, кога възниква и как можем да прекъснем изпълнението му чрез оператора break.

Безкраен цикъл. Що е то?

Безкраен цикъл наричаме този цикъл, който повтаря безкрайно изпълнението на тялото си. При while и do-while циклите проверката за край е условен израз, който винаги връща true. Безкраен for възниква, когато липсва параметърът за край.

Ето как изглежда безкраен while цикъл:

А така изглежда безкраен for цикъл:

Оператор break

Научихме, че безкрайният цикъл изпълнява определен код до безкрайност, но какво става, ако желаем в определен момент при дадено условие, да излезем принудително от цикъла. Тук на помощ идва операторът break, в превод - спри, прекъсни.

Операторът break спира изпълнението на цикъла към момента, в който е извикан. Това означава, че текущата итерация няма да бъде завършена и съответно останалата част от кода в тялото на цикъла няма да се изпълни.

Пример: прости числа

В следващата задача се изисква да направим проверка за просто число. Преди да продължим към нея, нека си припомним какво са простите числа.

Определение: Едно число (n) е просто, ако се дели единствено на 1 и на себе си без остатък. Можем да приемем, че n е просто число, ако не се дели на число между 2 и n-1.

Примерни прости числа: 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, …

За разлика от тях, непростите (композитни) числа са такива числа, чиято композиция е съставена от прости числа.

Ето няколко примерни непрости числа:

  • 10 = 2 * 5
  • 21 = 3 * 7
  • 143 = 13 * 11

Алгоритъм за проверка дали число е просто: Проверяваме дали n се дели на 2, 3, …, n-1 без остатък.

  • Ако се раздели, значи е композитно.
  • Ако не се раздели, значи е просто.
Можем да оптимизираме алгоритъма, като вместо проверката да е до n-1, да се проверяват делителите до √𝒏

Пример: проверка за просто число. Оператор break

Да се провери дали едно число n е просто. Това ще направим като проверим дали n се дели на числата между 2 и √𝒏.

Ето какви са нужните стъпки, за да може нашата програма да се изпълни коректно:

  • Създаваме променливата n, на която присвояваме цяло число въведено от входа на конзолата.
  • Създаваме булева променлива isPrime с начална стойност true, ако числото е по-голямо или равно на две. Приемаме, че едно число е просто до доказване на противното.
  • Създаваме for цикъл, на който като начална стойност за променливата на цикъла задаваме 2, за условие текущата ѝ стойност <= √𝒏. Стъпката на цикъла е 1.
  • В тялото на цикъла проверяваме дали n, разделено на текущата стойност има остатък. Ако от делението няма остатък, то променяме isPrime на false и излизаме принудително от цикъла чрез оператор break.
  • В зависимост от стойността на isPrime отпечатваме дали числото е просто (true) или съответно съставно (false).

Тестване в Judge системата

Тествайте решението си тук: https://judge.softuni.bg/Contests/Practice/Index/659#9.

Пример: оператор break в безкраен цикъл

Условие: Да се напише програма, която проверява дали едно число n е четно, и ако е - да се отпечатва на екрана. За четно считаме число, което се дели на 2 без остатък. При невалидно число да се връща към повторно въвеждане и да се изписва съобщение, което известява, че въведеното число не е четно.

Ето какви са нужните стъпки, за да може нашата програма да се изпълни коректно:

  • Създаваме променлива n, на която присвояваме начална стойност 0.
  • Създаваме безкраен while цикъл, като за условие ще зададем true.
  • В тялото на цикъла:
    • Вземаме целочислена стойност от входа на конзолата и я присвояваме на n.
    • Ако числото е четно, излизаме от цикъла чрез break.
    • В противен случай извеждаме съобщение, което гласи, че числото не е четно. Итерациите продължават, докато не се въведе четно число.
  • Отпечатваме четното число на екрана.
  • Бележка - ще обясним конструкцията try...catch съвсем скоро!

Тестване в Judge системата

Тествайте решението си тук: https://judge.softuni.bg/Contests/Practice/Index/659#10.

Внимание, някои от тестовете е нормално да не минат, защото съдържат грешни числа (текст вместо число). С тях ще се справим по-късно, когато се запознаем с try-catch конструкцията.

Вложени цикли и операторът break

След като вече научихме какво са вложените цикли и как работи операторът break, е време да разберем как работят двете заедно. За по-добро разбиране, нека стъпка по стъпка да напишем програма, която трябва да направи всички възможни комбинации от двойка числа. Първото число от комбинацията е нарастващо от 1 до 3, а второто е намаляващо от 3 до 1. Задачата трябва да продължи изпълнението си, докато i + j не е равно на 2 (т.е. i = 1 и j = 1).

Желаният резултат е:

Грешно решение:

Ако оставим програмата ни по този начин, резултатът ни ще е

Защо се получава така? Както виждаме, в резултата липсва "1 1". Когато програмата стига до там, че i = 1 и j = 1, тя влиза в if проверката и изпълнява break операцията. По този начин се излиза от вътрешния цикъл, но след това продължава изпълнението на външния. i нараства, програмата влиза във вътрешния цикъл и принтира резултата.

Когато във вложен цикъл използваме оператора break, той прекъсва изпълнението само на вътрешния цикъл.

Какво е правилното решение? Един начин за решаването на този проблем е чрез деклариране на bool променлива, която следи за това, дали трябва да продължава въртенето на цикъла. Цялото решение на задачата:

По този начин, когато i + j = 2, програмата ще направи променливата hasToEnd = true и ще излезе от вътрешния цикъл. При следващото завъртане на външния цикъл, чрез if проверката, програмата няма да може да стигне до вътрешния цикъл и ще прекъсне изпълнението си.

Справяне с грешни данни: try-catch

Последното, с което ще се запознаем в тази глава, е как да "улавяме" грешни данни чрез конструкцията try-catch.

Какво е try-catch?

Try-catch е конструкция, която служи за проверка на обработваните данни и реагиране при изключения и грешки в тях.

Изключенията (exceptions) най-често представляват уведомление за дадено събитие, което нарушава нормалната работа на една програма. Грешки от типа exception прекъсват изпълнението на програмата ни. Когато настъпят, се казва, че изключението е "хвърлено" (throw exception). От там идва и израза "улавям изключение" (catch exception).

Конструкция на try-catch

Try-catch има различни видове конструкции, но за сега ще се запознаем само с най-основната:

В следващата задача ще видим нагледно, как да се справим в ситуация, в която потребителят въвежда вход, различен от число (например string вместо int), чрез try-catch.

Пример: Справяне с грешни числа чрез try-catch

Да се напише програма, която проверява дали едно число n е четно и ако е, да се отпечатва на екрана. При невалидно въведено число да се изписва съобщение, което известява, че въведения вход не е валидно число.

Ето какви са нужните стъпки, за да може нашата програма да се изпълни коректно:

  • Създаваме try-catch конструкция.
  • В try блока пишем програмната логика за взимане на потребителския вход и проверката за четност.
  • Ако хванем изключение при изпълнението на try блока, изписваме съобщение за невалидно въведено число.

Тестване в Judge системата

Тествайте решението си тук: https://judge.softuni.bg/Contests/Practice/Index/659#10.

Сега вече решението трябва да минава без грешки.

Задачи с цикли

В тази глава се запознахме с няколко нови вида цикли, с които могат да се правят повторения с по-сложна програмна логика. Да решим няколко задачи, използвайки новите знания.

Задача: числа на Фибоначи

Числата на Фибоначи в математиката образуват редица, която изглежда по следния начин: 1, 1, 2, 3, 5, 8, 13, 21, 34, ...

Формулата за образуване на редицата е:

F0 = 1
F1 = 1
Fn = Fn-1 + Fn-2

Примерен вход и изход

Вход (n) Изход Коментар
10 89 F(11) = F(9) + F(8)
5 8 F(5) = F(4) + F(3)
20 10946 F(20) = F(19) + F(18)
0 1
1 1

Да се въведе цяло число n и да се пресметна n-тото число на Фибоначи

Насоки и подсказки

Ето какви са нужните стъпки, за да може нашата програма да се изпълни коректно:

  • Създаваме променлива n, на която присвояваме целочислена стойност от входа на конзолата.
  • Създаваме променливите f0 и f1, на които присвояваме стойност 1, тъй като така започва редицата.
  • Създаваме for цикъл с условие текущата стойност i < n - 1.
  • В тялото на цикъла:
    • Създаваме временна променлива fNext, на която присвояваме следващото число в поредицата на Фибоначи.
    • На f0 присвояваме текущата стойност на f1.
    • На f1 присвояваме стойността на временната променлива fNext.
  • Извън цикъла отпечатваме числото n-тото число на Фибоначи.

Тестване в Judge системата

Тествайте решението си тук: https://judge.softuni.bg/Contests/Practice/Index/659#11.

Задача: пирамида от числа

Да се отпечатат числата 1...n в пирамида като в примерите:

Примерен вход и изход

Вход Изход Вход Изход
7 1
2 3
4 5 6
7
10 1
2 3
4 5 6
7 8 9 10

Насоки и подсказки

Ето какви са нужните стъпки, за да може нашата програма да се изпълни коректно:

  • Създаваме променлива n, на която присвояваме целочислена стойност от входа на конзолата.
  • Създаваме променлива num с начална стойност 1. Тя ще пази броя на отпечатаните числа. При всяка итерация ще я увеличаваме с 1 и ще я принтираме.
  • Създаваме външен for цикъл, който ще отговаря за редовете в таблицата. Наименуваме променливата на цикъла row и ѝ задаваме начална стойност 0. За условие слагаме row < n. Размерът на стъпката е 1.
  • В тялото на цикъла създаваме вътрешен for цикъл, който ще отговаря за колоните в таблицата. Наименуваме променливата на цикъла col и ѝ задаваме начална стойност 0. За условие слагаме col < row (row = брой цифри на ред). Размерът на стъпката е 1.
  • В тялото на вложения цикъл:
    • Проверяваме дали col > 1, ако да – принтираме разстояние. Ако не направим тази проверка, а директно принтираме разстоянието, ще имаме ненужно такова в началото на всеки ред.
    • Отпечатваме числото num в текущата клетка на таблицата и го увеличаваме с 1.
    • Правим проверка за num > n. Ако num е по-голямо от n, прекъсваме въртенето на вътрешния цикъл.
  • Отпечатваме празен ред, за да преминем на следващия.
  • Отново проверяваме дали num > n. Ако е по-голямо, прекъсваме изпълнението на програмата ни чрез break.

Тестване в Judge системата

Тествайте решението си тук: https://judge.softuni.bg/Contests/Practice/Index/659#12.

Задача: таблица с числа

Да се отпечатат числата 1…n в таблица като в примерите:

Примерен вход и изход

Вход Изход Вход Изход
3 1 2 3
2 3 2
3 2 1
4 1 2 3 4
2 3 4 3
3 4 3 2
4 3 2 1

Насоки и подсказки

Ето какви са нужните стъпки, за да може нашата програма да се изпълни коректно:

  • Създаваме променлива n, на която присвояваме целочислена стойност от входа на конзолата.
  • Създаваме for цикъл, който ще отговаря за редовете в таблицата. Наименуваме променливата на цикъла row и ѝ задаваме начална стойност 0. За условие слагаме row < n. Размерът на стъпката е 1.
  • В тялото на цикъла създаваме вложен for цикъл, който ще отговаря за колоните в таблицата. Наименуваме променливата на цикъла col и ѝ задаваме начална стойност 0. За условие слагаме col < n. Размерът на стъпката е 1.
  • В тялото на вложения цикъл:
    • Създаваме променлива num, на която присвояваме резултата от текущият ред + текущата колона + 1 (+ 1, тъй като започваме броенето от 0).
    • Правим проверка за num > n. Ако num е по-голямо от n, присвояваме нова стойност на num равна на два пъти n - текущата стойност за num. Това правим с цел да не превишаваме n в никоя от клетките на таблицата.
      • Отпечатваме числото от текущата клетка на таблицата.
  • Отпечатваме празен ред във външния цикъл, за да преминем на следващия ред.

Тестване в Judge системата

Тествайте решението си тук: https://judge.softuni.bg/Contests/Practice/Index/659#13.

Какво научихме от тази глава?

Можем да използваме for цикли със стъпка:

for (int i = 1; i <= n; i+=3) {
   System.out.println(i);
}

Циклите while/do-while се повтарят докато е в сила дадено условие:

int num = 1;
while (num <= n) {
   System.out.println(num++);
}

Ако се наложи да прекъснем изпълнението на цикъл, го правим с оператора break:

int n = 0;
while (true) {
   n = Integer.parseInt(scanner.nextLine());
   if (n % 2 == 0)  {
      break; // even number -> exit from the loop
   }
   System.out.println("The number is not even.");
}
System.out.println("Even number entered: {0}", n);

Вече знаем как да следим за грешки по време на изпълнението на програмата ни:

try {
   System.out.println("Enter even number: ");
   n = Integer.parseInt(scanner.nextLine());
} catch 
   System.out.println("Invalid number."); 
}
// Ако Integer.parseInt(…) гръмне, ще се изпълни catch { … } блокът

results matching ""

    No results matching ""