- •Министерство образования и науки Российской Федерации
- •Разработка программы для решения задачи «игра умножения»
- •Задание
- •Содержание
- •Введение
- •1 Постановка задачи
- •2 Анализ поставленной задачи
- •2.1 Основные объекты задачи и их взаимодействие
- •2.2 Решение задачи – вариант 1
- •2.3 Решение задачи – вариант 2
- •3 Описание результатов разработки
- •4 Руководство пользователя
- •Заключение
- •Список использованных источников
- •Приложение а (обязательное) Листинг программы a.1 Основной модуль MultiplyGame
- •A.2 Модуль тестирования MultiplyGameTest
- •A.3 Модуль MultiplyGameFile (ввод-вывод из файла)
2.3 Решение задачи – вариант 2
Рассмотрим другой вариант определения является ли некоторая позиция выигрышной или проигрышной. Из первичного определения выигрышной позиции следует, что позиции от ⌈N / 9⌉ до N – 1 являются выигрышными, где ⌈x⌉ – округление до ближайшего большего целого числа. В свою очередь позиции от ⌈⌈N / 9⌉ / 2⌉ до ⌈N / 9⌉ – 1 являются проигрышными: из них игрок вынужден будет перейти в позиции, которые являются для противника выигрышными. Следующий интервал лежит в пределах от ⌈⌈⌈N / 9⌉ / 2⌉ / 9⌉ до ⌈⌈N / 9⌉ / 2⌉ – 1, и эти позиции уже являются выигрышными для рассматриваемого игрока и т.д.
Суть решения состоит в том, чтобы получать нижние интервалы выигрышных и проигрышных позиций, до тех пор, пока нижний интервал не дойдет до начальной позиции игры – P = 1. Если последним делителем была 9, то побеждает игрок начинающий игру (Слава), если же 2 – то побеждает второй игрок (Оля).
Нетрудно заметить, что данный алгоритм решения является более наглядным, не требует дополнительных ресурсов на хранение громоздких массивов со всеми позициями. Решение сводится к всего одному циклу в котором находятся нижние пределы выигрышных и проигрышных интервалов для первого игрока (Славы). Это значительно ускоряет работу алгоритма.
3 Описание результатов разработки
Учитывая все приведенные достоинства и недостатки вариантов решений, целесообразно реализовать вариант 2, который является более быстрым и менее ресурсоемким.
В соответствии с требованиями к решаемой задаче, входными данными для программы должно быть число N. Подробно рассмотрим вариант программы, в которой значение N будет задаваться пользователем в консоли. Также возможна реализация варианта с чтением входных данных из файла и выводом результата в другой файл, исходный код такого приложения представлен в приложении A.3.
Так как среди целочисленных типов языка Java отсутствуют беззнаковые типы данных, то для хранения числа N и нижней границы выигрышных и проигрышных интервалов необходимо использовать переменную типа long.
Основной модуль программы реализуем в классе MultiplyGame. Прежде всего необходимо задать некоторые константные значения, используемые в программе:
границы допустимого диапазона числа N:
static final long rangeMin = 2, rangeMax = 4294967295L
массив имен игроков типа String:
static final String[] players = new String[] {"Stan", "Ollie"}
делители, используемые при нахождении нижних границ выигрышных и проигрышных интервалов, также целесообразно задать в виде массива:
static final byte[] dividers = new byte[] {9, 2}
Для того чтобы приложение было исполняемым, основной класс должен содержать метод main (String[] args). В данном методе реализуем ввод числа N и вывод победителя или сообщения об ошибке. Для обработки возможных исключительных ситуаций необходимо использовать блок try .. catch. Это позволит избежать краха приложения, если пользователь вводит не числовую константу или вводит число N, лежащее вне пределов допустимого диапазона. UML диаграмма класса представлена ниже (рис. 3.1). Приведем текст метода main.
Scanner in = new Scanner(System.in);
try {
System.out.print("Please enter N: ");
long N = Long.parseLong(in.nextLine()); // ввод числа N
System.out.print(players[getWinner(N)]);
// вызов метода getWinner() и вывод имени победителя
System.out.println(" wins");
}
catch (Exception e) {
System.out.print("Wrong number!"); // вывод сообщения об ошибке
}
Рисунок 3.1. UML диаграмма класса MultiplyGame
Как видно в приведенном тексте, основной алгоритм программы будет реализован в методе getWinner (long N). Данный метод определяет победителя и возвращает его индекс (0 или 1). Если число N лежит в недопустимом диапазоне этот метод возвращает значение -1, что также приводит к исключительной ситуации в основном методе класса и выводится сообщение об ошибке.
Рассмотрим более подробно метод getWinner и особенности его реализации. В первую очередь необходимо проверить число N на допустимые пределы:
if (N < rangeMin || N > rangeMax) return -1
Далее для реализации алгоритма понадобится индекс winner, принимающий значения 0 или 1 и меняющийся с каждым шагом. Таким образом этот индекс будет указывать на победителя по окончанию выполнения цикла. Кроме того будем использовать этот индекс для обращения к массиву dividers, то есть делить нижнюю границу выигрышного или проигрышного интервала попеременно на 9 или на 2. Что касается нижней границы (минимальной выигрышной позиции), то ее значение будет храниться во временной переменной minPos типа long. Первоначально эта переменная инициализируется со значением равным N.
Блок-схема метода getWinner приведена на рис. 3.2. Основный цикл данного метода инвертирует значение переменной winner (если равняется 0, то присваивается значение 1 и наоборот) и находит следующую границу выигрышного интервала для одного из игроков. Округление до ближайшего большего целого числа производится с помощью метода Math.ceil. Цикл выполняется пока minPos > 1 – если это условие не выполняется, победитель игры определен и индекс победителя возвращается в основной метод класса MultiplyGame.
Рисунок 3.2. Блок-схема метода getWinner
Для проведения тестирования следует создать еще один модуль с классом MultiplyGameTest. Данный класс реализует тестирование с помощью технологии JUnit. UML диаграмма класса представлена ниже (рис. 3.3). Класс содержит всего один метод для тестирования реализованного алгоритма:
public void testGetWinner() {
assertEquals(MultiplyGame.getWinner(-15), -1);
// метод должен вернуть ошибку
assertEquals(MultiplyGame.getWinner(1), -1);
// метод должен вернуть ошибку
assertEquals(MultiplyGame.getWinner(4294967296L), -1);
// метод должен вернуть ошибку
assertEquals(MultiplyGame.getWinner(162), 0);
// должен победить игрок 0 (Слава)
assertEquals(MultiplyGame.getWinner(17), 1);
// должен победить игрок 1 (Оля)
assertEquals(MultiplyGame.getWinner(34012226), 0);
// должен победить игрок 0 (Слава)
assertEquals(MultiplyGame.getWinner(81934), 1);
// должен победить игрок 1 (Оля)
}
Рисунок 3.3. UML диаграмма класса MultiplyGameTest
Как видно из результатов тестирования (рис. 3.4), разработанный метод getWinner, работает верно и не вызывает «завалов» теста.
Рисунок 3.4. Результаты теста JUnit
Общая диаграмма классов представлена на рис. 3.5.
Рисунок 3.5. Общая диаграмма классов
Примеры работы программы (скриншот) – приведены на рис. 3.6.
Рисунок 3.6. Примеры работы консольного приложения MultiplyGame
