Простое любопытство и некоторая необходимость заставили меня проверить работу операций mod и div в различных языках программирования. И должен сказать, что данные эксперименты привели совершенно к неожиданным результатам.
Итак, все началось с моей статьи "Задание 17", в которой я обосновывал результат операций mod и div в языке Python. Напомню основные положения.
Теорема: для любых целых чисел n и q существуют единственным образом определенные целые числа m и r, удовлетворяющие условиям:
\[ \left \{ {n=m \cdot q+r\\0 \leq |r| <|q|\\Sign(q)=Sign(r)} \right.\] , где Sign(x) — функция определения знака числа x.
Таким образом, будем называть число r остатком от деления n на q, а число m — неполным частным (целой частью) от деления n на q.
Сознаюсь, я добавил некоторые ограничения в данную теорему, но справедливости своей она не потеряла и так же, доказуема. Далее, я рассмотрел, как данная операция работает в языке Python. Думаю, пришло время продемонстрировать код, чтобы мои утверждения могли быть проверены в различных условиях.
Итак, код на языке Python (совсем не изящный, но задача совсем не в этом):
print('n\tq\tn = mq+r\t\tm\tr')
n=147
q=10
print(n,'\t',q,'\t',n ,'=',n//q,'*',q,'+',n%q,'\t',n//q,'\t',n%q)
n=-147
q=10
print(n,'\t',q,'\t',n ,'=',n//q,'*',q,'+',n%q,'\t',n//q,'\t',n%q)
n=147
q=-10
print(n,'\t',q,'\t',n ,'=',n//q,'*',q,'+',n%q,'\t',n//q,'\t',n%q)
n=-147
q=-10
print(n,'\t',q,'\t',n ,'=',n//q,'*',q,'+',n%q,'\t',n//q,'\t',n%q)
Получился следующий результат:
|
n |
q |
\[ n=m \cdot q + r\] |
m |
r |
|
147 |
10 |
\[ 147=14 \cdot 10+7\] |
14 |
7 |
|
-147 |
10 |
\[ -147=-15 \cdot 10 +3\] |
-15 |
3 |
|
147 |
-10 |
\[ 147 = -15 \cdot (-10)-3\] |
-15 |
-3 |
|
-147 |
-10 |
\[-147 = 14 \cdot(-10) - 7\] |
14 |
-7 |
И вот, я решил проверить подобный код на языке C++.
Собственно, код:
#include <iostream>
using namespace std;
int main()
{
int n=147;
int q=10;
cout << "n\tq\tm*q+r\t\tm\tr" << endl;
cout << n<<"\t"<<q<<"\t"<<n/q<<"*"<<q<<"+"<<n%q<<"\t\t"<<n/q<<"\t"<<n%q<<endl;
n=-147;
cout << n<<"\t"<<q<<"\t"<<n/q<<"*"<<q<<"+"<<n%q<<"\t"<<n/q<<"\t"<<n%q<<endl;
n=147;
q=-10;
cout << n<<"\t"<<q<<"\t"<<n/q<<"*"<<q<<"+"<<n%q<<"\t"<<n/q<<"\t"<<n%q<<endl;
n=-147;
cout << n<<"\t"<<q<<"\t"<<n/q<<"*"<<q<<"+"<<n%q<<"\t"<<n/q<<"\t"<<n%q<<endl;
return 0;
}
Данный код был скомпилирован с помощью компилятора LLVM clang-7 под операционной системой Debian 10.
Результат был неожиданным:
|
n |
q |
\[ n=m \cdot q + r\] |
m |
r |
|
147 |
10 |
\[ 147=14 \cdot 10+7\] |
14 |
7 |
|
-147 |
10 |
\[ -147=-14 \cdot 10 -7\] |
-14 |
-7 |
|
147 |
-10 |
\[ 147 = -14 \cdot(-10)+7\] |
-14 |
7 |
|
-147 |
-10 |
\[-147 = 14 \cdot(-10) - 7\] |
14 |
-7 |
Итак, что мы видим? Равенство знаков q и r не соблюдается, но знаки m и r подбираются таким образом, чтобы с одной стороны, выражение \[n=m\cdot q+r\] сохраняло свою справедливость, с другой стороны сохранялось следующее равенство: \[ |r|=|n|\mod |q| \]то есть, последняя цифра записи числа равнялась абсолютному значению остатка от деления на основание системы счисления.
Компиляция с помощью g++ 8.3.0 дала точно такой же результат.
Далее, рассмотрим код на Pascal:
var
n,q : integer;
begin
writeln('n q n=m*q+r m r');
n:=147;
q:=10;
writeln(n,' ',q,' ',n,'=',n div q,'*',q,'+',n mod q,' ',n div q,' ', n mod q);
n:=-147;
q:=10;
writeln(n,' ',q,' ',n,'=',n div q,'*',q,'+',n mod q,' ',n div q,' ', n mod q);
n:=147;
q:=-10;
writeln(n,' ',q,' ',n,'=',n div q,'*',q,'+',n mod q,' ',n div q,' ', n mod q);
n:=-147;
q:=-10;
writeln(n,' ',q,' ',n,'=',n div q,'*',q,'+',n mod q,' ',n div q,' ', n mod q);
end.
Компилятор fpc 3.0.4
Результат аналогичен результату на C++.
Я пока не стал рассматривать такие языки, как C, C#, Java и прочие, так как в школьном образовании они, скорее, экзоты, нежели обыденность. Но, проверка данного аспекта была бы интересна. Особенно, на разных операционных системах, компиляторах и так далее. В ОС Linux, думаю, результаты будут сопоставимы, так как для всех компилируемых языков все сводится к вызовам библиотеки libc. А вот под ОС Windows результаты могут быть неожиданные, так как разные системы могут использовать свои собственные библиотеки.
В рамках данного исследования, результаты, полученные на языке Python уже кажутся мне более неожиданными и менее логичными, нежели на других языках. Но в любом случае, мы видим, что базовая теорема теории чисел сохраняет свою справедливость, а языки программирования следуют этой теореме, хотя и несколько различными способами.
В целом, данный материал предназначен для учащихся высокого уровня мотивации, которым важен каждый балл ЕГЭ и участвующим в олимпиадах, где малейшая деталь может сыграть роковую роль в итоговом результате.
Учите математику, исследуйте системы программирования, подвергайте все сомнению и тщательной проверке - и вы станете классным специалистом...