Статистика и открытые данные

И. В. Щуров, НИУ ВШЭ

Основано на задачах отсюда и отсюда. Авторы задач в подборке: А. Зотов, И. Щуров.

На странице курса находятся другие материалы.

Домашнее задание №2

За разные задачи можно получить разное число баллов. Если не указано обратное, задача весит 1 балл. Максимум за ДЗ можно набрать 15 баллов. Вы можете решить больше задач, чем требуется, чтобы дополнительно потренироваться.

Для предварительной проверки задания нужно сделать следующее:

  1. Скачать данный ipynb-файл на свой компьютер, загрузить его в Jupyter (открыть Jupyter, нажать кнопку Upload, выбрать этот ipynb-файл).
  2. Активировать тесты (см. ниже).
  3. Вставить решение каждой задачи в ячейку для кода, следующую за его условием, там, где написан комментарий # YOUR CODE HERE. Отступ вашего кода должен составлять 4 пробела. Ничего не менять вокруг!
  4. Запустить ячейку, в которую вы вставили код с решением. Ввести какие-то входные данные, проверить визуально правильность вывода.
  5. Запустить следующую ячейку (в ней содержится тест). Если запуск ячейки с тестом не приводит к появлению ошибки (assertion), значит, всё в порядке, задача решена. Если приводит к появлению ошибки, значит, тест не пройден и нужно искать ошибку.

Внимание! Если в какой-то момент забыть ввести входные данные и перейти на следующую ячейку, есть риск, что Notebook перестанет откликаться. В этом случае надо перезапустить ядро: Kernel → Restart. При этом потеряются все значения переменных, но сам код останется.

Чтобы сдать ДЗ, его надо загрузить в nbgr-x в виде ipynb-файла. Получить ipynb-файл можно, выбрав в Jupyter пункт меню File → Download as... → IPython Notebook (.ipynb).

В задачах вам потребуется написать код, запрашивающий данные с клавиатуры с помощью функции input(). Обратите внимание: input() всегда возвращает строку, чтобы преобразовать результат к другому типу, нужно использовать соответствующие функции, см. конспект лекции.

Активировать тесты

Запустите следующие ячейку, чтобы иметь возможность запускать тесты. Каждый раз после перезапуска ядра нужно снова запускать эту ячейку.

In [ ]:
from collections import deque

class Tester(object):
    def __init__(self, inp):
        self.outputs = []
        self.inputs = deque(inp)
    def print(self, *args, sep = " ", end = "\n"):
        text = sep.join(map(str, args)) + end
        newlines = text.splitlines(keepends=True)
        if self.outputs and self.outputs[-1] and self.outputs[-1][-1] != "\n" and newlines:
            self.outputs[-1] += newlines[0]
            self.outputs.extend(newlines[1:])
        else:
            self.outputs.extend(newlines)

    def input(self, *args):
        assert self.inputs, "Вы пытаетесь считать больше элементов, чем предусмотрено условием"
        return self.inputs.popleft()
    def __enter__(self):
        global print
        global input
        print = self.print
        input = self.input
        return self.outputs
    def __exit__(self, *args):
        global print
        global input
        del print
        del input

Задача 1

В математике функция sign(x) (знак числа) определена так:

sign(x) = 1, если x > 0,

sign(x) = -1, если x < 0,

sign(x) = 0, если x = 0.

Для данного числа x выведите значение sign(x).

Примеры

Входные данные

10

Выходные данные

1

Входные данные

-99

Выходные данные

-1
In [ ]:
def sign():
    # YOUR CODE HERE
sign()
In [ ]:
test_data = [
    (10, 1), (20, 1), (30, 1), (1, 1), (0, 0), (-1, -1), (-2, -1), (-10, -1), (-12, -1), (-999, -1)
]

for inp, out in test_data:
    with Tester([inp]) as t:
        sign()
        assert len(t) == 1, "Вам нужно вывести ровно одну строку с ответом"
        assert int(t[0].strip()) == out, "Неверный ответ, все сломалось на числе " + str(inp)
print("Ура, всё верно!")

Задача 2

Напишите программу, которая принимает на вход год year и определяет, является ли он високосным. Программа должна печатать слово leap в случае високосного года и not leap иначе.

Подсказка: не любой год, делящийся на 4, является високосным.

Примеры

Входные данные

42

Выходные данные

not leap

Входные данные

2300

Выходные данные

not leap

Входные данные

2000

Выходные данные

leap
In [ ]:
def leap_year():
    # YOUR CODE HERE
leap_year()
In [ ]:
test_data = [
    (1600, 'leap'), (1700, 'not leap'), (1900, 'not leap'), (2000, 'leap'),
    (1, 'not leap'), (42, 'not leap'), (2018, 'not leap'), (2019, 'not leap'),
    (2020, 'leap'), (112, 'leap'), (114, 'not leap'), (999, 'not leap'),
    (3000, 'not leap')
]

for inp, out in test_data:
    with Tester([inp]) as t:
        leap_year()
        assert len(t) == 1, "Вам нужно вывести ровно одну строку с ответом"
        assert t[0].strip() == out, "Неверный ответ, все сломалось на числе " + str(inp)
print("Ура, всё верно!")

Задача 3 (2 балла)

Задача отозвана, попустите её

In [ ]:
# YOUR CODE HERE
In [ ]:
assert False

Задача 4

Напишите программу, которая запрашивает с клавиатуры целые числа и помещает их в список. Программа должна перестать запрашивать числа после того, как будет введено 10 чисел, либо после того, как пользователь введёт число 0. В последнем случае 0 не помещается в список (и служит только указанием на то, что числа больше не нужно вводить). После того, как список сформирован, необходимо вывести на экран все введённые числа (в том же порядке, в котором они вводились), кроме первого и последнего.

Подсказка. Для выхода из цикла можно использовать оператор break .

Пример.

Входные данные:

2
3
4
5
6
7
8
9
10
110


Выходные данные:

3
4
5
6
7
8
9
10

Входные данные:

5
9
8
-2
0

Входные данные:

9
8
In [ ]:
def skip_first_and_last():
    # YOUR CODE HERE
skip_first_and_last()
In [ ]:
test_data = [
    ("1 2 3 4 5 7 6 8 9 10", "2 3 4 5 7 6 8 9"),
    ("2 1 3 0", "1"),
    ("1 2 3 4 5 6 7 8 9 0", "2 3 4 5 6 7 8"),
    ("2 2 2 0", "2"),
    ("100 999 -2 999 2 3 5 10 9 10", "999 -2 999 2 3 5 10 9")
]

for inp, answer in test_data:
    ans = answer.split()
    with Tester(inp.split()) as t_out:
        skip_first_and_last()
        assert len(t_out) == len(ans), "Неверное количество строк в ответе"
        for t, a in zip(t_out, ans):
            assert t.strip() == a.strip(), "Что-то пошло не так"

print("Ура, всё верно!")

Задача 5 (2 балла)

Напишите программу, которая запрашивает целое число n с клавиатуры, затем n целых чисел, после чего выводит все введённые числа в столбик: сначала все положительные (в том порядке, в каком они вводились), потом все отрицательные (также в том порядке, в котором они вводились). Нули следует игнорировать (не выводить).

Пример.

Входные данные:

3
9
-2
5

Выходные данные:

9
5
-2

Входные данные:

5
1
-1
0
-3
1

Выходные данные:

1
1
-1
-3
In [ ]:
def positive_and_negative():
    # YOUR CODE HERE
positive_and_negative()
In [ ]:
test_data = [
    ("4 4 3 2 1", "4 3 2 1"),
    ("4 1 2 -3 -4", "1 2 -3 -4"),
    ("4 2 -1 3 -2", "2 3 -1 -2"),
    ("1 1", "1"),
    ("10 100 999 -2 999 2 3 5 10 9 10", "100 999 999 2 3 5 10 9 10 -2"),
    ("3 -3 -1 -2", "-3 -1 -2")
]

for inp, answer in test_data:
    ans = answer.split()
    with Tester(inp.split()) as t_out:
        positive_and_negative()
        assert len(t_out) == len(ans), "Неверное количество строк в ответе: " + inp
        for t, a in zip(t_out, ans):
            assert t.strip() == a.strip(), "Что-то пошло не так: " + inp

print("Ура, всё верно!")

Задача 6

Ввести с клавиатуры несколько чисел, разделённых пробелами. Создать список, элементами которого являются булевские значения (то есть True или False), отвечающие на вопрос «является ли соответствующее число чётным». То есть если первое введённое число чётное, то в списке первым элементом должно быть True, если нечётное, то Fasle. Если второе введённое число чётное, то вторым элементом в списке дожно быть True и т.д. Количество элементов в списке должно равняться количеству введенных чисел. Вывести этот список на экран.

Пример.

Входные данные

1 2 3 4 5

Выходные данные

[False, True, False, True, False]

Входные данные

12 15 17 19 20 22 5

Выходные данные

[True, False, False, False, True, True, False]

Примечание. Элементами получающегося списка должны быть именно булевские значения, а не строки "True" и "False".

In [ ]:
def evenness():
    # YOUR CODE HERE
evenness()
In [ ]:
test_data = [
    ("1 2 3 4 5", 
     "[False, True, False, True, False]"),
    ("2 3 0", "[True, False, True]"),
    ("1 1 1 1", "[False, False, False, False]"),
    ("2", "[True]"),
    ("100 999 -2 999 2 3 5 10", 
     "[True, False, True, False, True, False, False, True]")
]

for inp, out in test_data:
    with Tester([inp]) as t:
        evenness()
        assert len(t) == 1, "Вам нужно вывести ровно одну строку с ответом"
        assert t[0].strip() == (out), ("Неверный ответ, были "
                                       "введены числа " + inp + 
                                       " выведено " + t[0] + " ожидалось "
                                       + str(out))
print("Ура, всё верно!")

Задача 7 (2 балла)

Требуется определить, можно ли от шоколадки размером $n × m$ долек отломить $k$ долек, если разрешается делать разломы только по прямой между дольками (то есть ломать шоколадку на два прямоугольника). Количество разломов при этом может быть любым. Программа принимает на вход три числа, разделенных пробелом: $n$, $m$ и $k$ - и должна вывести на экран True, если такое разделение возможно, и False иначе. Гарантируется, что $n \times m \leq 50000$.

Примеры

Входные данные

2 2 3

Выходные данные

False

Входные данные

5 5 3

Выходные данные

True

Входные данные

8 8 63

Выходные данные

False
In [ ]:
def chocolate():
    # YOUR CODE HERE
chocolate()
In [ ]:
test_data = [
    ("1 1 1", "True"),
    ("4 4 8", "True"),
    ("4 4 7", "False"),
    ("12 12 120", "True"),
    ("0 0 42", "False"),
    ("25 37 5", "True"),
    ("30 30 17", "True"),
    ("7 8 7", "True"),
    ("7 8 9", "True"),
    ("7 8 19", "False")
]

for inp, answer in test_data:
    ans = answer
    with Tester([inp]) as t_out:
        chocolate()
        assert len(t_out) == len([ans]), "Неверное количество строк в ответе"
        for t, a in zip(t_out, [ans]):
            assert t.strip() == a.strip(), "Что-то пошло не так"

print("Ура, всё верно!")

Задача 8 (3 балла)

Напишите программу, которая принимает на вход координаты коня на шахматной доске (два числа от 1 до 8 включительно) и выводит на экран построчно все клетки, куда конь может сходить следующим ходом.

Примеры

Входные данные

1 1

Выходные данные

3 2
2 3

Входные данные

5 5

Выходные данные

7 6
7 4
3 4
3 6
4 7
6 7
4 3
6 3
In [ ]:
def chess():
    # YOUR CODE HERE
chess()
In [ ]:
test_data = [
    ('1 1', [[2, 3], [3, 2]]),
    ('3 3', [[2, 1], [2, 5], [4, 1], [4, 5], [1, 2], [1, 4], [5, 2], [5, 4]]),
    ('7 7', [[6, 5], [8, 5], [5, 6], [5, 8]]),
    ('1 2', [[2, 4], [3, 1], [3, 3]]),
    ('4 4', [[3, 2], [3, 6], [5, 2], [5, 6], [2, 3], [2, 5], [6, 3], [6, 5]])
]
for inp, answer in test_data:
    with Tester([inp]) as t_out:
        chess()
        assert sorted([list(k) for k in [map(int, y) for y in [m.split() for m in t_out]]]) == sorted(answer), "Что-то пошло не так"
print("Ура, всё верно!")

Задача 9 (3 балла)

Разработчик пошаговой стратегии Losers-V постоянно расширяет рынки сбыта и создаёт локализации своей игры даже на самые малоизвестные языки. В том числе, их заинтересовал язык племени австралийских аборигенов аниндилъяква. Но в языке аниндилъяква нет числительных. Как же, например, перевести на него фразу "у вас семь чёрных драконов, а у вашего врага — сорок"? Локализаторы решили перевести её так: "у вас немного чёрных драконов, а у вашего врага — толпа". Они составили таблицу, в которой указали правила замены чисел, обозначающих количество монстров, на существительные и местоимения:

Количество Обозначение на русском языке Обозначение на языке аниндилъяква
от 1 до 4 несколько few
от 5 до 9 немного several
от 10 до 19 отряд pack
от 20 до 49 толпа lots
от 50 до 99 орда horde
от 100 множество throng

Помогите локализаторам автоматизировать процесс — напишите программу, которая принимает на вход с клавиатуры количество монстров и печатает соответствующее этому количеству слово на языке аниндилъяква.

Внимание! В решении этой задачи запрещено использовать условные операторы if, elif и else суммарно больше двух раз (т.е. использование if, else разрешено, а if, if, else, else — нет). Также запрещено использовать любые библиотечные функции (даже один раз).

Примеры

Входные данные

42

Выходные данные

lots

Входные данные

3

Выходные данные

few

Входные данные

9000

Выходные данные

throng
In [ ]:
def monsters():
    # YOUR CODE HERE
monsters()
In [ ]:
test_data = [
    (1, 'few'), (4, 'few'), (5, 'several'), (12, 'pack'), (48, 'lots'), (49, 'lots'), (50, 'horde'),
    (70, 'horde'), (99, 'horde'), (100, 'throng'), (101, 'throng'), (1000, 'throng'), (99999, 'throng')
]

for inp, out in test_data:
    with Tester([inp]) as t:
        monsters()
        assert len(t) == 1, "Вам нужно вывести ровно одну строку с ответом"
        assert t[0].strip() == out, "Неверный ответ, все сломалось на числе " + str(inp)
print("Ура, всё верно!")

Задача 10 (4 балла)

В игре «палочки» исходно на столе лежит 15 палочек. Играют двое, ходят по очереди. За один ход разрешается взять одну, две или три палочки. Выигрывает тот, кто возьмёт последнюю палочку.

Реализовать игру «палочки» для двух игроков. В начале игры на экран выводится число палочек, лежащих на столе. Затем происходит ход первого игрока: с клавиатуры запрашивается, сколько палочек он хочет взять. Если он указывает число 1, 2 или 3, количество оставшихся палочек уменьшается на это число, и ход переходит к другому игроку. Если он ошибается и указывает что-то, отличное от чисел 1, 2 или 3, игра сразу прекращается и победа засчитывается другому игроку. То же самое («техническое поражение») происходит в случае, если в результате хода игрока на столе остаётся отрицательное число палочек. Если в результате хода остаётся ноль палочек, победа засчитывается тому игроку, кто взял последние палочки. После каждого хода на экран выводится число оставшихся палочек. По завершении игры выводится надпись Player X is the winner!, где X — номер игрока (1 или 2). Игрок 1 — это игрок, который ходит первым.

Примеры

Символами << обозначается ввод, >> — вывод. Например, >>12 означает, что было выведено число 12 (то есть выполнена команда, эквивалентная print(12)).

Пример 1

>>15
<<5
>>Player 2 is the winner!

Произошло техническое поражение первого игрока из-за ввода неверного числа палочек.

Пример 2

>>15
<<3
>>12
<<3
>>9
<<2
>>7
<<3
>>4
<<1
>>3
<<3
>>Player 2 is the winner!

Игра доиграна до конца, игрок 2 выиграл.

Пример 3

>>15
<<3
>>12
<<3
>>9
<<2
>>7
<<3
>>4
<<2
>>2
<<3
>>Player 1 is the winner!

Произошло техническое поражение второго игрока из-за того, что он пытался взять больше палочек, чем есть на столе.

Пример 4

>>15
<<3
>>12
<<Help
>>Player 1 is the winner!

Произошло техническое поражение второго игрока из-за того, что он ввёл Help вместо числа палочек.

In [ ]:
def sticks():
    # YOUR CODE HERE
sticks()
In [ ]:
from textwrap import dedent

test_data = [
    """
    >>15
    <<5
    >>Player 2 is the winner!
    """,
    """
    >>15
    <<3
    >>12
    <<0
    >>Player 1 is the winner!
    """,
    """
    >>15
    <<3
    >>12
    <<3
    >>9
    <<2
    >>7
    <<3
    >>4
    <<1
    >>3
    <<3
    >>Player 2 is the winner!    
    """,
    """
    >>15
    <<3
    >>12
    <<3
    >>9
    <<2
    >>7
    <<3
    >>4
    <<2
    >>2
    <<3
    >>Player 1 is the winner!
    """,
    """
    >>15
    <<3
    >>12
    <<Help
    >>Player 1 is the winner!
    """,
    """
    >>15
    <<3
    >>12
    <<3
    >>9
    <<1
    >>8
    <<2
    >>6
    <<3
    >>3
    <<1
    >>2
    <<3
    >>Player 2 is the winner!
    """,
    """
    >>15
    <<3
    >>12
    <<3
    >>9
    <<1
    >>8
    <<2
    >>6
    <<3
    >>3
    <<1
    >>2
    <<2
    >>Player 1 is the winner!
    """
]
for data in test_data:
    lines = dedent(data).splitlines()
    inputs, outputs = [[line[2:] for line in lines 
                       if line.startswith(prefix)] 
                       for prefix in ["<<", ">>"]]
    with Tester(inputs) as t:
        sticks()
        assert len(t) == len(outputs), (t, outputs)
        assert all(q.strip().lstrip(">>") == w.strip() 
                   for q, w in zip(t, outputs)), (t, outputs)

Задача 11 (4 балла)

Напишите программу, которая будет складывать, вычитать, умножать или делить два целых неотрицательных числа. Числа и знак операции вводятся пользователем с клавиатуры в одну строку без пробелов. После выполнения вычисления программа не должна завершаться, а должна запрашивать новые данные для вычислений. Завершение программы должно выполняться при вводе символа 0 в качестве выражения. Если пользователь вводит неверный знак (не 0, +, -, *, /), то программа должна вывести ERROR и снова запросить выражение. Также она должна выводить ERROR, если пользователь ввел 0 в качестве делителя. При делении округляйте результат до второго знака после запятой.

Подсказка: для округления числа можно воспользоваться функцией round().

Примеры

Входные данные

10+2
12*3
12/0
18-1
0

Выходные данные

12
36
ERROR
17
In [ ]:
def calc():
    # YOUR CODE HERE
calc()
In [ ]:
from textwrap import dedent

test_data = [
    """
    <<12+1
    >>13
    <<18-2
    >>16
    <<0""",
    """
    <<4-8
    >>-4
    <<8*2
    >>16
    <<9/3
    >>3.0
    <<9/2
    >>4.5
    <<9/0
    >>ERROR
    <<9&0
    >>ERROR
    <<144/12
    >>12.0
    <<18+81
    >>99
    <<0"""
]
for data in test_data:
    lines = dedent(data).splitlines()
    inputs, outputs = [[line[2:] for line in lines 
                       if line.startswith(prefix)] 
                       for prefix in ["<<", ">>"]]
    with Tester(inputs) as t:
        calc()
        assert len(t) == len(outputs), (t, outputs)
        assert all(q.strip().lstrip(">>") == w.strip() 
                   for q, w in zip(t, outputs)), (t, outputs)