Задание 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, чтобы элементы выводились в столбик.

    Стоит отметить, что такое решение подходит для тех, кто уже хорошо ориентируется в функциональном стиле и понимает, как работают основные запросы, лямбда-выражения. Для проверки можно написать несколько решений и сравнить результаты.


Последнее изменение: Понедельник, 22 мая 2023, 19:36