Задание 5
Задание 5 (демо-2023). На вход алгоритма подаётся натуральное число N. Алгоритм строит по нему новое число R следующим образом.
1. Строится двоичная запись числа N.
2. Далее эта запись обрабатывается по следующему правилу:
а) если сумма цифр в двоичной записи числа чётная, то к этой записи справа дописывается 0, а затем два левых разряда заменяются на 10;
б) если сумма цифр в двоичной записи числа нечётная, то к этой записи справа дописывается 1, а затем два левых разряда заменяются на 11.
Полученная таким образом запись является двоичной записью искомого числа R.
Укажите минимальное число N, после обработки которого с помощью этого алгоритма получается число R, большее 40. В ответе запишите это число в десятичной системе счисления.
Рассмотрим решения на языках PascalABC.NET и Python, представленные на рисунке 2.2, и выделим сходства и различия в подходах к решению.
PascalABC.NET |
Python |
uses School; begin foreach var n in Range(1,40) do begin var n2:=Bin(n); if n2.CountOf('1') mod 2 = 0 then n2:='10'+n2?[3:]+'0' else n2:='11'+n2?[3:]+'1'; if Dec(n2,2)>40 then println(n); end; end. |
for n in range (1,40): n2= bin(n)[2:] print(n2) if n2.count('1')%2==0: n2='10'+n2[2:]+'0' else: n2='11'+n2[2:]+'1' r=int(n2,2) if r>40: print(n) break |
Функциональный стиль PascalABC.NET |
|
## uses School; (1..40).Where(x->begin var s := Bin(x); var r := s.CountOf('1').Divs(2) ? '10'+s?[3:]+'0' : '11'+s?[3:]+'1'; Result := Dec(r,2)>40 ? True : False; end).PrintLines; |
Подключаем в PascalABC.NET модуль School для функций Bin(a), Dec(a, k) – функции для систем счисления (в Python функции bin(a) и int(a, k) доступны без подключения модулей).
Используем новый цикл foreach, чтобы проходить по каждому элементу последовательности от 1 до 40, генерируемой функцией Range(a, b) (в Python просто цикл for и range(a,b)).
В условии используем точечный метод CountOf('1') (в Python метод count('1')) для подсчёта количества вхождений цифры 1 в строку n2. В условии нужно проверять сумму цифр числа, но так как двоичная запись содержит только нули и единицы, то нули можно не считать и узнать количество единиц в числе.
Далее, в зависимости от суммы цифр в числе, происходит изменение строки с помощью конкатенации (соединения) строк и срезов. В обоих языках конструкция аналогичная, за исключением того, что в PascalABC.NET индексация в строках начинается с единицы, а не с нуля, как в Python, поэтому в срезе стоят разные значения. Также можно заметить, что между строкой и срезом находится вопросительный знак, Срезы вида a? Называются безопасными срезами. Они работают следующим образом: сначала контейнер считается бесконечным в обе стороны и срез выбирает в нём определённые элементы. После этого в срезе оставляются только те элементы, которые принадлежат исходному контейнеру. Если не указать вопросительный знак и произойдёт выход индекса среза за границы, то вернётся исключение.
В конце проверяем, если десятичная запись полученного числа больше чем сорок, то выводим исходное число на экран. Если в ответе чисел много, то берём первое из них.
Если сравнивать понятность и компактность кодов на двух языках, видно, что PascalABC.NET ничем не уступает языку Python, даже с условием подключения библиотеки и наличия блоков begin-end.
На рисунке выше также приведено решение на PascalABC.NET с помощью функционального стиля: точечной нотации, запросов LINQ, тернарных операторов и лямбда-выражений. Рассмотрим решение подробнее.
Создадим последовательность чисел от 1 до 40 конструкцией (1..40), её не обязательно присваивать переменной, так как мы будем работать целиком с последовательностью и где-то ещё её использовать не будем.
Далее, сразу применим запрос фильтрации Where(x->«условие»), который отберёт те элементы последовательности, которые удовлетворяют условию внутри лямбда-выражения. Так как условие содержит несколько операторов, то внутри нужен блок begin-end.
Сначала получим двоичную запись текущего числа x функцией Bin(x), запишем результат в переменную s. Дальше мы совмещаем в однострочном условии подсчёт единиц в числе и определение чётности их суммы с присваиванием переменной выражений, в зависимости от результата условия:
var r:= s.CountOf('1').Divs(2) ? '10'+s?[3:]+'0' : '11'+s?[3:]+'1';
Данная запись полностью аналогичная следующей записи:
If s.CountOf('1') mod 2 then
var r:= '10'+s?[3:]+'0'
else
var r:= '11'+s?[3:]+'1';
Такая короткая запись условного оператора называется тернарным оператором, она имеет следующий синтаксис:
var [переменная]:= [условие] ? [присваивается переменной, если True] : [присваивается переменной, если False]
Получается, если сумма цифр чётная, то переменной r присваивается строка '10'+s?[3:]+'0', иначе '11'+s?[3:]+'1'.
В переменную Result с помощью тернарного оператора будем передавать True, десятичная запись числа r больше 40, иначе передаём False. Согласно этим значениям True-False для каждого числа, запрос Where сформирует подходящую последовательность, которую выведем на экран методом PrintLines, чтобы элементы выводились в столбик.
Стоит отметить, что такое решение подходит для тех, кто уже хорошо ориентируется в функциональном стиле и понимает, как работают основные запросы, лямбда-выражения. Для проверки можно написать несколько решений и сравнить результаты.