Автор решений - Волков Максим
Реализовать функцию, вычисляющую площадь треугольника по координатам его вершин в декартовой системе координат. Результат вывести на экран.
Для задания координат можно определить вспомогательный класс Point:
public class Main {
static class Point {
double x;
double y;
}
public static void main(String[] args) {
Point a = new Point();
a.x = 0.0;
a.y = 0.0;
Point b = new Point();
b.x = 2.0;
b.y = 0.0;
Point c = new Point();
c.x = 0.0;
c.y = 2.0;
// ...
}
}
Для вычисления площади можно использовать формулу Герона или применить геометрические свойства векторного произведения. Функция извлечения квадратного корня в Java: Math.sqrt(x).
Входные данные
Программа запрашивает входные данные у пользователя, каждую координату отдельно для каждой вершины.
Тестовые примеры
| Вершина1 | Вершина2 | Вершина3 | Результат |
|---|---|---|---|
| (0,0) | (2,0) | (0,2) | 2 |
| (0,0) | (1,0) | (0,1) | 0.5 |
| (-5,3) | (4,-7) | (3,14) | 89.5 |
| (-5,-15) | (-20,-8) | (-110,-213) | 1852.5 |
Пример вызова программы
% java ru/croc/task1/Main
% Введите координату х вершины №1: 0
% Введите координату y вершины №1: 0
% Введите координату х вершины №2: 2
% Введите координату y вершины №2: 0
% Введите координату х вершины №3: 0
% Введите координату y вершины №3: 2
% Площадь треугольника: 2
Если командная строка не видит JAVA - скорее всего необходимо корректно установить переменные окружения (environment variables). См. (пример для java 10, но аналогично для любой другой версии)
Написать метод, форматирующий и выводящий на экран заданный размер в байтах в человекочитаемом виде.
Человекочитаемый вид:
{целая часть < 1024}.{дробная часть макс. 1 знак} {единица измерения}
Например:
printBytes(23) -> "23.0 B"
printBytes(1024) -> "1.0 KB"
printBytes(53692044905543) -> "48.8 TB"
Для вывода только одного знака дробной части вещественного числа можно воспользоваться методом:
String.format("%.1f", 1.23456);
Входные данные
Число байт.
Тестовые примеры
| Число на вход | Строка в результате |
|---|---|
| 23 | 23.0 B |
| 1024 | 1.0 KB |
| 53692044905543 | 48.8 TB |
| 5428952 | 5.2 MB |
Пример вызова программы
java ru/croc/task2/Main 23
Задан массив целых чисел. Необходимо переставить наименьшее из этих чисел в начало массива, а наибольшее - в конец.
Массивы в Java определяются с помощью указания их размера в квадратных скобках:
int[] v = new int[77];
Размер можно не указывать, но тогда нужно определить сами элементы в фигурных скобках:
int[] v = new int[] {6, 28, 1};
Обращаться к элементам можно по индексу:
System.out.println(v[0]); // выводим первый по счету элемент
v[1] = 16; // изменяем значение элемента с индексом 1
Размер массива:
v.length;
Входные данные
Элементы массива, по очереди через пробел. Без экранирующих символов и обозначений начала и конца массива.
Тестовые примеры
| Исходный массив | Результирующий массив |
|---|---|
| [6, 28, 1, 17] | [1, 17, 6, 28] |
| [6, 28, 17, 1] | [1, 6, 17, 28] |
| [1, 17, 6, 28] | [1, 17, 6, 28] |
| [28, 6, 17, 1] | [1, 6, 17, 28] |
| [28, 5, 11, 1, 44, 17, 8] | [1, 5, 11, 28, 8, 17, 44] |
Пример вызова программы
java ru/croc/task3/Main 6 28 1 17
В текстах программ на Java могут использоваться многострочные (/* ... */) и однострочные (// ...) комментарии. Реализовать метод, принимающий на вход строковую переменную - исходный текст программы на Java, вырезающий из этой строки все комментарии и возвращающий результат в виде строки.
public static void main(String[] args) {
String source = “...”; // test data
String noComments = removeJavaComments(source);
System.out.println(noComments);
}
Пример
/*
* My first ever program in Java!
*/
class Hello { // class body starts here
/* main method */
public static void main(String[] args/* we put command line arguments here*/) {
// this line prints my first greeting to the screen
System.out.println("Hi!"); // :)
}
} // the end
// to be continued...
Результат
class Hello {
public static void main(String[] args) {
System.out.println("Hi!");
}
}
Вы разрабатываете небольшое приложение по аннотированию (разметке) изображений с целью последующего использования этой разметки для обучения моделей computer vision. В этом приложении пользователь может выделять области на изображении с помощью прямоугольников и окружностей и подписывать их произвольным текстом.
Прямоугольники определяются координатами левого нижнего и правого верхнего углов, а окружности - координатами центра и радиусом. Вся разметка для изображения представляется массивом Annotation[].
В приложении определен класс аннотированного изображения:
class AnnotatedImage {
private final String imagePath;
private final Annotation[] annotations;
public AnnotatedImage(String imagePath, Annotation... annotations) {
this.imagePath = imagePath;
this.annotations = annotations;
}
public String getImagePath() {
return this.imagePath;
}
public Annotation[] getAnnotations() {
return this.annotations;
}
}
Определите класс Annotation для представления данных разметки (подпись + фигура) и классы Figure, Rectangle, Circle для задания размеченных областей.
Переопределите метод toString класса Annotation так, чтобы в результат выводилась информация о полях и вложенных объектах. Формата вывода:
Окружность:
“C (<X0>, <Y0>), <R>: <Подпись>”
Прямоугольник:
“R (<X1>, <Y1>), (<X2>, <Y2>): <Подпись>”
Например:
C (100, 100), 10: Tree
R (100, 100), (150, 200): Car
От пользователей приложения (см. задачу 5) пришел запрос на возможность перемещать уже размеченные области. Для поддержки новой функциональности вам требуется внести несколько изменений:
- выбор аннотации по координатам точки (x, y);
В массиве аннотаций требуется найти первую, фигура которой содержит точку с заданными координатами.
Annotation findByPoint(int x, int y) {
// ...
}
- выбор аннотации по шаблону подписи;
В массиве аннотаций требуется найти первую, подпись которой содержит заданную подстроку.
Annotation findByLabel(String label) {
// ...
}
Определить, содержит ли строка заданную подстроку, можно с помощью метода contains(CharSequence s) класса String.
- перемещение фигуры выбранной аннотации на смещение (dx, dy);
В рамках этого изменения вы решили доработать классы фигур таким образом, чтобы они реализовывали интерфейс
public interface Movable {
void move(int dx, int dy);
}
Доработайте классы и реализуйте соответствующие методы.
Определить класс, описывающий позицию на шахматной доске 8x8. Данные класса: компоненты x и y, отсчитываемые от левого нижнего угла (x = 0, y = 0 - левая нижняя клетка).
Все методы, позволяющие установить координаты, в том числе и конструкторы, должны проверять корректность аргументов и генерировать IllegalPositionException (необходимо определить это исключение самостоятельно) в случае ошибочных значений.
Переопределить метод toString(), выводящий координаты позиции в формате <номер колонки в виде буквы от 'a' до 'h'><номер строки, начиная с 1>. Например, позиция с координатами (1, 1) имеет строковое представление "b2".
Реализовать "фабричный метод" конструирования объекта позиции из строкового представления ("b2" -> объект):
static ChessPosition parse(String position) {
// ...
}
В виде массива строк задана некоторая последовательность позиций на шахматной доске 8x8. Например, "b1", "a3", "c4", "d6". Реализовать метод, проверяющий, что последовательность может быть пройдена фигурой конь в соответствии с правилами хода этой фигуры (буквой "Г"). На вход метод принимает массив объектов класса, определенных в текущей задаче.
Определить новый класс IllegalMoveException обрабатываемого исключения, которое генерируется методом проверки в случае ошибки. Класс должен содержать информацию о неправильном ходе: из какой в какую позиции ход запрещен. При вызове метода проверки это исключение должно обрабатываться, а неправильный ход выводиться на экран. Последовательность ходов для проверки задается в аргументах командной строки программы.
public class IllegalMoveException extends Exception {
// ...
}
Тестовые примеры
[in] "g8", "e7", "e6"
[out] "конь так не ходит: e7 -> e6"
[in] "g8", "e7", "c8"
[out] "OK"
В текстовом файле слова могут быть разделены одним или несколькими пробелами, или символами перевода строки. Необходимо реализовать программу, считающую количество слов в файле и выводящую результат на экран. Путь к файлу задается первым аргументом командной строки (args[0]).
Пример
[in] Забыл Панкрат Кондратьевич домкрат, А без домкрату ну не поднять на тракте трактор.
[out] 13
В виде строки задан относительный путь в файловой системе, в котором:
"." означает текущую директорию; ".." означает родительскую директорию по отношению к текущей; "/" используется в качестве разделителя директорий.
Реализовать функцию, выполняющую "нормализацию" заданного пути, то есть, удаляющую из него лишние директории с учетом переходов "." и "..".
Пример:
[in]
"КРОК/работа/src/./../../универ/../../../мемы/котики"
[out] "../мемы/котики"
Вы “ответственно” подходите к информационной безопасности и храните свои пароли в файлах в виде MD5-хешей (https://ru.wikipedia.org/wiki/MD5, https://www.baeldung.com/java-md5).
Но, к сожалению, вы забыли и никак не можете вспомнить свой пароль от учетной записи в почте. Хотя у вас остался его хеш 40682260CC011947FC2D0B1A927138C5. Вы точно помните, что пароль состоял из 7 букв латинского алфавита, и что все они были строчными.
Для генерации хеша вы используете функцию hashPassword
private static final char[] HEX_DIGITS = "0123456789ABCDEF".toCharArray();
private static String toHexString(byte[] bytes) {
StringBuilder hex = new StringBuilder(bytes.length * 2);
for (byte b : bytes) {
hex.append(HEX_DIGITS[(b & 0xff) >> 4]);
hex.append(HEX_DIGITS[b & 0x0f]);
}
return hex.toString();
}
private static String hashPassword(String password) {
MessageDigest digest;
try {
digest = MessageDigest.getInstance("MD5");
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
digest.update(password.getBytes());
byte[] bytes = digest.digest();
return toHexString(bytes);
}
Напишите программу, которая методом полного перебора напомнит вам пароль. Причем за наиболее короткий срок - пароль вам нужен как можно быстрее. Для ускорения процесса вы решили перебор выполнять в несколько потоков.
Количество потоков - входные данные для программы, задается первым аргументом командной строки. Хеш пароля - вторым аргументом. Найденный пароль печатается в стандартный поток вывода.
Вы работаете над приложением для проведения аукциона. Начать разработку решили с самого важного - класса лота аукциона. В ходе анализа предметной области вы пришли к выводу, что каждый лот должен иметь 3 параметра: текущую стоимость, имя пользователя, предложившего ее, и время окончания торгов по этому лоту.
Также класс должен предоставлять два метода - метод “ставки”, который обновляет текущую стоимость лота и сохраняет предложившего ее пользователя, если торги по лоту еще ведутся по времени и предложенная цена выше текущей. Второй метод - метод получения имени пользователя победителя (итогового, не текущего лидера).
Делать ставки на лот одновременно могут сразу несколько пользователей.
Реализуйте класс по описанию так, чтобы одновременное участие в ставках большого количества пользователей не приводило к ошибкам в проведении аукциона (то есть, потокобезопасно).
Небольшой стартап разрабатывает социальный сервис, в котором пользователи могут оставлять комментарии. Со временем в комментариях появился спам и разработчики решили бороться с ним с помощью "черных списков" запрещенных слов. Они составили такие списки и поручили вам написать функцию, удаляющую из всех накопленных комментариев нежелательные.
Они предоставили вам интерфейс, который внедрили в свой продукт, и попросили написать его реализацию:
import java.util.List;
import java.util.Set;
public interface BlackListFilter {
/**
* From the given list of comments removes ones
* that contain words from the black list.
*
* @param comments list of comments; every comment
* is a sequence of words, separated
* by spaces, punctuation or line breaks
* @param blackList list of words that should not
* be present in a comment
*/
void filterComments(List<String> comments, Set<String> blackList);
}
Реализуйте интерфейс BlackListFilter.
Кинотеатр "Места для поцелуев" открыл стриминговый сервис для онлайн-просмотра фильмов. За несколько месяцев работы сервиса накопилась история просмотров разными пользователями и владельцы решили внедрить в него систему рекомендаций, которая предлагала бы пользователям интересный фильм на основе их истории просмотров.
У вас есть два файла:
- Список доступных фильмов. Каждая строка содержит числовой идентификатор фильма и его название, разделенные запятой. Например:
1,Мстители: Финал
2,Хатико
3,Дюна
4,Унесенные призраками
- История просмотров по всем пользователям сервиса. Каждая строка файла содержит список идентификаторов фильмов, просмотренных одним человеком за все время пользования сервисом. Идентификаторы разделены запятыми. Например:
2,1,3
1,4,3
2,2,2,2,2,3
На основе этих данных реализуйте алгоритм рекомендаций, который бы для списка просмотров конкретного пользователя рекомендовал следующий фильм.
Алгоритм выбора рекомендации:
-
Для просмотров пользователя из историй по всем пользователям выбираются те, у которых хотя бы половина фильмов совпадает с заданной. (То есть, выбираются все пользователи, которые посмотрели минимум половину фильмов пользователя, для которого формируется рекомендация).
-
Из отобранных списков исключаются все, которые пользователь уже посмотрел.
-
Для оставшегося списка фильмов подсчитывается суммарное количество просмотров среди всех пользователей сервиса и фильм с максимальным числом просмотров выбирается как рекомендация (если таких фильмов оказалось несколько, выбирается любой из них).
Список просмотров текущего пользователя задается через пользовательский ввод, рекомендация выдается в виде названия фильма в System.out. Пути к файлам с названиями фильмов и истории просмотров пользователей сервиса могут быть определены в виде констант в приложении.
Пример
[in]
2,4
[out]
Дюна
Вы реализовали фильтрацию комментариев по “черному списку” слов (задача 13) и с чистой совестью ушли в отпуск на 3 недели.
По возвращении из отпуска вы обнаружили, что логика фильтрации усложнилась. Также выяснилось, что нет единого способа представления комментариев среди сервисов: где-то они представлены строками, где-то объектами специализированных классов. Вдобавок некоторым сервисам оказалось неудобным передавать набор комментариев на фильтрацию в виде списка и предпочтительнее коллекция другого типа.
В результате разработчики разных сервисов реализовали копии методов для применения правил “черного списка”, учитывающие специфику их сервисов. А так как правила фильтрации тоже менялись в процессе, эти методы правились не совместно, что привело к плохо читаемому и поддерживаемому коду: логика с одной стороны дублируется, но с другой - работает в каждом методе по-особенному.
Вы берете дело в свои руки и решаете реализовать единый метод фильтрации, учитывающий все обнаруженные особенности. В ходе анализа кода вы отмечаете для себя следующее:
-
Условия фильтрации в разных сервисах различаются и нет возможности обобщить их в рамках одного метода. Поэтому способ фильтрации вы решаете представлять в виде предиката и позволять определять его вызывающим сервисам.
-
Набор используемых сервисами классов-представлений для комментариев обширен (String, Comment, CommentDto, …) и, возможно, будет и в дальнейшем расширяться.
-
Сервисы работают с наборами комментариев в виде коллекций разных типов (HashSet, ArrayList, ArrayDeque, …), но все они имплементируют интерфейс Iterable.
-
Результат фильтрации удобнее представлять в виде отдельной коллекции-возвращаемого значения метода, а исходную коллекцию при этом не модифицировать. Некоторым сервисам важен порядок следования комментариев, поэтому он должен быть сохранен в отфильтрованном результате.
Обновите интерфейс BlackListFilter и реализуйте новый механизм фильтрации в виде default-метода.
Государственная организация принимает заявки, на которые необходимо формировать ответ. В формировании ответа участвуют разные отделы организации по следующему принципу:
- сначала заявка рассматривается корневым отделом;
- затем она переводится во все его дочернии отделы, в которых рассматривается параллельно;
- и т.д., пока заявка не пройдет через все отделы.
Для каждого отдела известно количество часов, необходимых для рассмотрения заявки. У каждого отдела может быть только один родительский, после которого начинается работа над заявкой.
Реализовать метод, вычисляющий время, необходимое для обработки заявки в заданной конфигурации (при условии, что все отделы начинают обработку заявки без задержек сразу после предшествующих).
Конфигурация отделов задается файлом со следующим форматом строк <код отдела>, <код родительского отдела или “-”, если отдел корневой>, <время обработки заявки в часах>. Например:
A,-,1
B1,A,3
C11,B1,1
C12,B1,1
B2,A,1
C21,B2,4
B3,A,2
C31,B3,1
D311,C31,1
Время обработки заявки для этой конфигурации: 6 часов
Вы разрабатываете систему в микросервисной архитектуре, в которой сервисы в процессе работы записывают логи в отдельные файлы. Каждая строка файла лога имеет вид:
time message\n,
где time - время в формате POSIX time (количество миллисекунд с 1 января 1970 года), message - произвольный текст.
Строки в логах всегда отсортированы по времени в порядке возрастания.
Сервисов в системе много, и вы обнаружили, что при анализе ошибок тратите слишком много времени, так как приходится восстанавливать общий порядок действий в системе по большому количеству разных логов.
Поэтому вы решили реализовать утилиту слияния нескольких логов в один общий. Утилита в качестве аргумента командной строки принимает путь к директории, в которой находятся файлы логов (возможно, разбитые по вложенным директориям). Результат слияния утилита записывает в стандартный поток вывода в виде общей последовательности строк из всех логов в порядке возрастания времени. Логами считаются файлы с расширениями .log или .trace, все файлы с другими расширениями игнорируются.
Реализуйте эту утилиту, имея в виду, что логи бывают большими и не всегда могут поместиться в оперативной памяти целиком.
ЗАМЕТКА: Эта бонусная задача исследовательского типа. Её целью является не просто получение нужного результата, но и самостоятельная реализация подходящих условий для этого. Мы понимаем, что задача немного “грибная” (=с изюминкой), но все-таки и такие задачи бывают в реальной работе.
Закон Амдала определяет ускорение алгоритма, которого можно добиться при его многопоточной реализации.
ɑ - доля вычислений, которая не может быть распараллелена (выполняется внутри критической секции),
p - количество потоков,
Sp - во сколько раз скорость выполнения в p потоков превышает скорость выполнения в однопоточном режиме.
Вы реализовали алгоритм, ¾ вычислений которого могут быть выполнены параллельно. То есть, ɑ = 0.25. Придумайте и реализуйте приложение, которое в режиме симуляции определит оптимальное количество потоков для этой степени параллелизации (позволяющее получить 90% прироста от возможного теоретического максимума):
То есть, min p, для которого Sp >= 0.9 * 4 >= 3.6
Для этого реализуйте симуляцию алгоритма, которая четверть вычислений будет выполнять в режиме синхронизации, а все остальные параллельно и найдите p, соответствующее условиям выше.
Вы разрабатываете бэкенд интернет-магазина. Магазин уже функционирует и принимает заказы. Информация о заказах регистрируется в Excel-таблице вручную. В нее внесены данные за все время существования магазина. Заказчик выгрузил и передал вам эту таблицу в виде CSV-файла.
Формат файла
<номер_заказа:integer>,<логин_пользователя:string>,<артикул_товара:string>,<название_товара:string>,<цена_в_рублях:integer>
Содержимое
1,vasya,Т1,Монитор,500
1,vasya,Т2,Мышь,50
2,petya,Т2,Мышь,50
2,petya,Т2,Мышь,50
2,petya,Т3,Клавиатура,150
1,vasya,Т4,Блок питания, 200
3,nikita,Т4,Блок питания, 200
4,olga,Т4,Блок питания, 200
3,nikita,Т5,Видеокарта,15000
3,nikita,Т5,Видеокарта,15000
4,olga,Т5,Видеокарта,15000
4,olga,Т5,Видеокарта,15000
Дополнительно заказчик сообщил, что товары с одинаковым артикулом всегда продавались с одними и теми же названием и ценой.
Спроектируйте структуру таблиц для представления информации о заказах в базе данных и загрузите в них данные из файла. Для этого разработайте приложение, которое:
- Создаст в базе данных все необходимые таблицы.
- Импортирует в базу данных все записи исходного файла.
Путь к файлу с данными передается программе в качестве аргумента командной строки при запуске.
Вы продолжаете разработку бэкенда интернет-магазина. Уже спроектирована схема данных и есть первичное наполнение, следующим шагом вы решаете разработать DAO-класс для работы с базой данных заказов. Для этого вы добавляете в приложение классы сущностей заказа (Order) и товара (Product) и определяетесь с составом необходимых методов DAO-класса:
Product findProduct(String productCode);
Поиск в базе данных товара с указанным артикулом. Если соответствующего товара в базе данных нет, метод возвращает null.
Product createProduct(Product product);
Создание нового товара. Если в базе данных существует товар с переданным артикулом, метод выбрасывает исключение.
Product updateProduct(Product product);
Изменение информации о товаре. Название и цена товара в базе данных заменяется на значения, указанные в полях параметра product.
Артикул товара, данные которого должны быть изменены, также задается полем объекта product.
void deleteProduct(String productCode);
Удаление товара и всех упоминаний о нем в заказах.
Вас смущает необходимость изменения уже выданных заказов, но заказчик настаивает.
Order createOrder(String userLogin, List products);
Создание заказа.
Для указанного пользователя в базе данных создается новый заказ с заданным списком товаров.
Не забудьте протестировать собственное решение (любым способом, можно просто вызывать требуемые методы из main-метода с произвольными параметрами. Реализуйте программу с указанными операциями.
Реализуйте программу, которая печатает/выводит/отправляет строку "Hello, world!" в любое место, кроме консоли. (Записывать символы в стандартный поток вывода запрещено.)

