Округлення, як відомо, це  математична операція, яка полягає в заміні числа α наближеним числом α1 із меншою кількістю значущих цифр. Число α1 вибирають так, щоб похибка округлення була якомога меншою.

 

У різних галузях застосовують різні методи округлення.

 

На уроках математики в школі традиційно округлюють методом «Округлення до найближчого цілого» (англ. rounding) — це найбільш часто вживаний метод округлення, при якому число округлюється до цілого, модуль різниці з яким у цього числа мінімальний. У загальному випадку, коли число в десятковій системі округляють до N-ого знаку, правило може бути сформульовано таким чином:

якщо N+1 знак < 5, тоді N-ий знак зберігають, а N+1 та всі наступні обнуляють;

якщо N+1 знак ≥ 5, тоді N-ий знак збільшують на одиницю, а N+1 та всі наступні обнуляють;

Наприклад: 11,9 → 12; −0,9 → −1; −1,1 → −1; 2,5 → 3.

 

Давайте напишемо ще кілька прикладів на даний тип округлення:

Округлюємо до цілого:

1,5 → 2

2,5 → 3

3,5 → 4

4,5 → 5

5,5 → 6

6,5 → 7

 

Такий метод округлення використовує Python версії 2 і старі програмні компілятори, наприклад Turbo Pascal.

 

Але це не єдиний метод округлення. Відповідно до стандарту IEEE 754 (і 1985, і 2008 року),  основним для комп'ютерних обчислень є інший метод. Він зветься округленням до найближчого парного, банківським, статистичним, данським, Гаусовим округленням. Вважається, що банківське округлення дозволяє зменшити похибки при роботі з великими масивами даних. Подробиці можна дізнатися, завітавши на кілька років на правильну спеціальність гарного університету. Даний метод на сьогодні популярний:  наприклад, .NET Math.Round  так робить по замовчуванню,  цей метод використовує для функції стандартного округлення round() PascalABC.Net  і Python 3.

 

Синтаксично округлення в Python 3 здійснюється функцією round(number, ndigits), де number – число що округлюється, а ndigits - кількість знаків після коми. Наприклад:

 

>>>round(2.137, 2)

2.14

>>>round(1.5)

2

 

Ось результат такого округлення:

1,5 → 2

2,5 → 2

3,5 → 4

4,5 → 4

5,5 → 6

6,5 → 6

 

Саме так округлює функція round() в Python 3.

 

Але є і великі підводні камені, пов’язані з представленням в пам’яті комп’ютера дійсних чисел. Ось приклад:

 

>>>round(2.15, 1)

 

З точки зору округлення до найближчого парного, результат повинен бути 2.2

Python 3 вважає інакше:

 

>>>round(2.15, 1)

2.1

 

Це пов’язано з тим, як представлено число 2.15 в машинному поданні, давайте переглянемо, як воно виглядає для, наприклад, 30 знаків після коми:

 

>>> '%0.30f' % 2.15

'2.149999999999999911182158029987'

 

Тепер все зрозуміло, в такому випадку логіка округлення  Python 3 цілком зрозуміла.

 

Але сюрпризи при округленні дійсних чисел можуть з’являтися цілком несподівано, наприклад:

 

>>> round(2.15, 1)

2.1

>>> round(2.1500000000000001, 1)

2.1

>>> round(2.150000000000001, 1)

2.2

 

Як бачимо, камінців вистачає. Але round() – не єдина функція округлення.

 

В Python 3, в модулі math реалізовано ще декілька методів округлення:

 

math.ceil(x) – округлення до найближчого більшого числа

math.floor(x) - округлення до найближчого меншого числа

math.trunc(x) - усікає значення X до цілого.

 

Ось приклади, що ілюструють роботу даних функцій:

 

>>> import math

>>> math.ceil(1.4)

2

>>> math.floor(1.4)

1

>>> math.trunc(1.4)

1

>>> math.trunc(1.9)

1

>>> math.floor(-1.9)

-2

>>> math.trunc(-1.9)

-1

 

 

Усікти значення до цілого можна і без використання модуля math:

 

>>> int(1.1)

1

>>> int(1.9)

1

>>> int(-1.1)

-1

>>> int(-1.9)

-1

 

А для естетів, перфекціоністів, тих, хто хоче забезпечити абсолютну точність, контролювати стратегію округлення і системно керувати золотою рибкою, Python 3 має модуль decimal

 

Цей неймовірний інструмент має  аж вісім варіантів округлення:

 

ROUND_CEILING - Округлення в сторону позитивної нескінченності. Наприклад, число 2.52 буде округлено до 2.6, а число -2.58 до -2.5

 

ROUND_DOWN - Округлення в сторону нуля. Наприклад, число 2.58 буде округлено до 2.5, а число -2.58 - до -2.5

 

ROUND_FLOOR - Округлення в бік негативної нескінченності. Наприклад, число 2.52 буде округлено до 2.5, а число -2.58 до -2.6

 

ROUND_HALF_DOWN - Округлення в сторону від нуля, якщо частина, що округлюється,  більше половини останнього значущого розряду, в іншому випадку округлення буде виконано в сторону нуля. Наприклад, число 2.58 буде округлено до 2.6, число 2.55 буде округлено до 2.5, а число -2.58 до -2.6

 

ROUND_HALF_EVEN - Те ж, що і ROUND_HALF_DOWN, тільки якщо частина, що округлюється дорівнює точно половині останнього значущого розряду, результат округлюється вниз, якщо попередня цифра парна, і вгору - якщо попередня цифра непарна. Наприклад, число 2.65 буде округлено до 2.6, число 2.55 також буде округлено до 2.6

 

ROUND_HALF_UP - Те ж, що і ROUND_HALF_DOWN, тільки якщо частина, що округлюється, дорівнює точно половині останнього значущого розряду, результат округлюється в бік від нуля. Наприклад, число 2.55 буде округлено до 2.6, а число -2.55 до -2.6

 

ROUND_UP - Округлення в бік від нуля. Наприклад, число 2.52 буде округлено до 2.6, а число -2.52 - до -2.6

 

ROUND_05UP - Округлення в бік від нуля, якщо останній значущий розряд містить цифру 0 або 5, в іншому випадку округлення виконується в сторону нуля. Наприклад, число 2.54 буде округлено до 2.6, число 2.64 також буде округлено до 2.6

 

Звичайно, всі ці варіанти з’явилися в decimal не просто так  - вони мають цілком прикладний сенс. З таким інструментарієм можна реалізувати багато чого, в тому числі і популярне шкільне округлення методом «Округлення до найближчого цілого»:

 

import decimal as decimal

print(decimal.Decimal('2.5').quantize(decimal.Decimal('1'), rounding=decimal.ROUND_HALF_UP))

print(decimal.Decimal('3.5').quantize(decimal.Decimal('1'), rounding=decimal.ROUND_HALF_UP))

 

out:

3

4

 

Ну і можна, звичайно, написати і власну функцію округлення, якщо на то є причини. В будь-якому варіанті тема округлення дійсних чисел в Python 3 має чимало непростих аспектів. І програмістам необхідно уважно враховувати дані особливості як на етапі проектування інформаційних систем, так і при розробці стратегій їх тестування.

 

 


 

Розберемо задачу 8358 з e-olymp. Джерело задачі —  2018 Azerbaijan School Competition, II Stage, April 8, Problem K. На e-olimp на дату написання статті задача опублікована лише російською, тому перекладаю сам:

 

Середнє значення – 1

(https://www.e-olymp.com/uk/problems/8358)

 

Проект "Середня вага школяра школи" вирішили виконати Мамед з Самедом. Що вони будуть робити з цим числом, вони не розкривають. Вони попросили зважитися всіх учнів школи і занесли результати в таблицю. Допоможіть їм підрахувати середню вагу учнів. Але вони просять, щоб учнів з найбільшою і з найменшим вагою не враховувати. Єдине їх упущення, вони не підрахували загальну кількість учнів, але це, звичайно, не завадить вам підрахувати то, що вони просять.

 

Вхідні дані

У кількох рядках задані ваги учнів в кілограмах, розділених пропусками (одним або кількома) або символом кінця рядка. Читати ваги учнів до кінця введення.

 

Вихідні дані

Середня вага учнів школи без урахування учнів з найбільшою і найменшою вагою. Відповідь виводити з точністю до кілограм.

 

Ліміт часу 1 секунда

Ліміт використання пам'яті 128 MB

 

Вхідні дані # 1

40 23 27

  59 68 23 84 27

53 46

 

Вихідні дані # 1

46

 


 

В першу чергу треба врахувати, що через невідому кількість рядків вхідних даних, єдиним способом отримати  вхідні дані – це використати введення з файлу. Будемо читати рядки з файлу до тих пір, поки не закінчиться файл. Пропоную кожний рядок вхідних даних записувати в тимчасовий список, а далі елементи цього тимчасового списку додавати до основного списку. Код цієї частини програми:

 

 

inputfile = open('input.txt', 'r')

outputfile = open('output.txt', 'w')

weights = []

for line in inputfile:

    lst = [int(x) for x in line.split()]

    weights.extend(lst)

 

Далі ми можемо використати list comprehension з умовою, наприклад, так ефектно:

 

weights = [x for x in weights if x != max(weights) and x != min(weights)]

 

В результаті в новому списку weights будуть лише потрібні дані, всі максимальні і мінімальні ваги в новий список  weights не попадуть. Але так робити не треба, тому що це неоптимально по часу і непрофесійно на співбесіді на роботу. Річ в тому, що при кожній ітерації циклу, проходить порівняння значення елементу списку з максимальним і мінімальним елементом списку. І кожного разу python  буде обраховувати максимальне і мінімальне значення. Так як список у нас протягом обробки циклом не змінюється, то буде правильним визничити максимальне і мінімальне значення списку до циклічного перебору:

 

max_weights =  max(weights)

min_weights =  min(weights)

weights = [x for x in weights if x != max_weights and x != min_weights]

 

Зверніть увагу на інформативність змінних. В олімпіадному програмуванні часто поспішають і так змінні не називають, а дарма. Рекомендую назви змінним давати розумно, у професійному програмуванню так і роблять.

 

Ну і на останок, в умові задачі є округлення, це в Python дуже дискутивна тема. Як мінімум, нагадаю, що  Python версії 2 виконує так зване «арифметичне округлення», як Pascal, C++ і як на уроках математики, а Python версії 3 при використанні функції round() виконує так зване «банківське округлення» або «округлення до парного». В даній задачі тести обрані таким чином, що при здачі в Python версії 3 ніяких проблем немає, про округлення в Python 3 на «Плетиві» буде окрема стаття, а тим, хто про дива округлення чує вперше, банківське округлення в Python 3 (і не лише там) виглядає так:

>>> round(1.5)

2

>>> round(2.5)

2

>>> round(3.5)

4

>>> round(4.5)

4

>>> round(5.5)

6

>>> round(6.5)

6

>>> round(7.5)

8

>>> round(8.5)

8

 

І не забувайте прибиратися після себе. Принцип простий. Ви відкрили якийсь файли? Ви відкрили, вам і закривати. 

Документація третього Python вимагає так:

If you’re not using the with keyword, then you should call f.close() to close the file and immediately free up any system resources used by it. If you don’t explicitly close a file, Python’s garbage collector will eventually destroy the object and close the open file for you, but the file may stay open for a while. Another risk is that different Python implementations will do this clean-up at different times.

Успіхів! 

 


 

Розбираємо задачу.

Степан і похід в магазин

https://www.e-olymp.com/uk/problems/7670

 

Сьогодні Степан чекає в гості свого друга Василя. Щоб підготуватися до зустрічі, Степану необхідно відвідати два магазини, розташованих поряд з його будинком.

 

 

Від будинку до першого магазину веде доріжка довжини d1 метрів, а до другого магазину веде доріжка довжини d2 метри. Також існує доріжка, яка безпосередньо сполучає два магазини один з одним, довжиною d3 метри.

Допоможіть Степану обчислити мінімальну відстань, яку йому буде потрібно пройти, щоб відвідати обидва магазини і повернутися додому.

Степан завжди стартує зі свого будинку. Він повинен відвідати обидва магазини, переміщаючись тільки за наявними трьома доріжками, і повернутися назад додому. При цьому його абсолютно не бентежить, якщо йому доведеться відвідати один і той же магазин або пройти по одній і тій же доріжці більше одного разу. Єдине його завдання - мінімізувати сумарну пройдену відстань.

Вхідні дані

У першому рядку вхідних даних знаходяться 3 цілих числа d1d2d3(1 ≤ d1, d2, d3 ≤ 108) - довжини доріжок.

d1 - довжина доріжки, що з'єднує будинок Степана і перший магазин;

d2 - довжина доріжки, що з'єднує будинок Степана і другий магазин;

d3 - довжина доріжки, що з'єднує два магазина.

Вихідні дані

Виведіть мінімальну кількість метрів, яку доведеться пройти Степану, щоб відвідати обидва магазини і повернутися додому.

Ліміт часу 0.1 секунда
Ліміт використання пам'яті 64 MiB
Вхідні дані #1
10 20 30
Вихідні дані #1
60
Джерело ACM-ICPC Ukraine 2016, Перший етап Україна, 16 квітня 2016 року
 
 

 
Прекрасна задача на логіку. Давайте спочатку розберем, як взагалі може ходити Степан. Розпишемо всі його можливі маршрути:
 
Отже - перший машрут. Це коли Степан йде по колу.  Тоді відстань буде d1+d3+d2.
Якщо він іде по колу, але в зворотньому напрямку,  то його шлях буде d2+d3+d1. 
Так як при обох цих вариантах відстань буде однаковою, то будемо вважати ці маршрути як один.
 
Записуємо, перший маршрут ми визначили. 
m1 = d1+d3+d2
 
Давайте подумаємо, як ще може ходити  Степан.
 
Ще один варіант: він може піти в перший магазин, потім повернутися додому, потім піти в другий магазин і знову повернутися додому. 
 
Записуємо цей маршрут:
m2 = d1+d1+d2+d2
Можна математично гарно це записати:
m2 = (d1+d2)*2
 
Наступний варіант:
Степан іде в перший магазин, потім в другий магазин. І повертається назад тією ж дорогою. 
Записуємо цей маршрут:
m3 = d1+d3+d3+d1
Можна математично гарно це записати:
m3 = (d1+d3)*2
 
 
І останній варіант:
Степан іде в другий магазин, потім в перший магазин. І повертається назад тією ж дорогою. 
Записуємо цей маршрут:
m4 = d2+d3+d3+d2
Можна математично гарно це записати:
m4 = (d2+d3)*2
 
 
В результаті аналізу ми сформували чотири маршрути.
Нам треба визначити найменший. 
Відповідна функція python вміє шукати найменше серед і елементів списку і просто змінних:
 
result = min(m1,m2,m3,m4)
 
 
Що нам допомогло розв'язати задачу? Звичайний аналіз і малюнок.
 
Успіхів!
 
 
 
 

При роботі з текстовими даними в python важливо пам’ятати про іммутабельність даного типу даних.  Тобто якщо зі списками ми можемо змінити елемент списку прямо у списку, то при роботі з текстовими даними, нам буде потрібно фактично створення нової змінної. Для прикладу замінимо третій елемент в списку:

>>> a = [11, 22, 33, 44, 55]

>>> a[3] = 99

>>>a

[11, 22, 33, 99, 55]

Все логічно. Для тих, хто здивувався, нагадаємо, що індекс відраховується від нуля.

Тепер поміняємо в слові третю букву.

>>>a = 'бабайка'

>>>print(a[3])

a

>>>a[3] = 'у'

Traceback (most recent call last):

  File "<pyshell#10>", line 1, in <module>

    a[3] = 'у'

TypeError: 'str' object does not support item assignment

 

Тобто ми отримали помилку python.  Задачу заміни букви можна легко розв'язати за допомогою зрізів і конкатенації:

>>>a = 'бабайка'

>>>a = a[:3] + 'у' +  a[4:]

>>>a

'бабуйка'

 

Якщо нам треба не третій символ в рядковій величині, а символи замінити на інши символи, то для цього у Python є готовий інструментарій і це простіше.

А тепер – дискотека. Тобто задача.

 


 

Перше та останнє входження (4726)

https://www.e-olymp.com/uk/problems/4726

 

Задано рядок літер. Якщо у ньому літера f зустрічається лише один раз, виведіть її індекс (нумерація індексів з 0).

Якщо вона зустрічається два або більше разів, виведіть індекс її першої та останньої появи.

Якщо літера f у заданому рядку не зустрічається, нічого не виводьте.

 

Вхідні дані

Один рядок з не більш ніж 255 літер.

Вихідні дані

Виведіть відповідь в залежності від того скільки разів у вхідному рядку зустрічається літера f.

 

Ліміт часу 1 секунда

Ліміт використання пам'яті 128 MiB

 

Вхідні дані #1

comfort

 

Вихідні дані #1

3

 

Вхідні дані #2

office

 

Вихідні дані #2

1 2

 


 

Дозволю собі пояснити більшу частину програми, тому що майже в кожному рядку можуть зустрітися цікаві речі.

s = input()

first = s.find('f')  

# функція s.find()  повертає індекс позиції, де зустрічається символ 'f',

# пошук відбувається від початку рядкової змінної.

#  Якщо символ не знайдено, функція повертає  -1

 

if first != -1:

    print (first, end = '')

    s = s.replace('f','z',1)

#  Функція s.replace(old, new, count) замінить count символів old на new, якщо не вказати count, то замінить всі.

 

Для закінчення написання програми вам, думаю, згодиться функція s.rfind()  що повертає індекс позиції, де зустрічається певний символ,  але пошук відбувається з кінця рядкової змінної. Якщо символ не знайдено, функція повертає  -1.

 

А є ще більш гарний розв'язок - використовувати find і rfind для знаходження крайнього лівого і крайнього правого індексу, потім порівнювати отримане. Якщо числа рівні між собою і не рівні -1 - виводити одне. Якщо не рівні і жодне з них не -1 - виводити обидва.

 

Успіхів!

 


 

Вхід для зареєстрованих відвідувачів