Сапунов Павел : другие произведения.

Мама, я больше не боюсь целых чисел

Самиздат: [Регистрация] [Найти] [Рейтинги] [Обсуждения] [Новинки] [Обзоры] [Помощь|Техвопросы]
Ссылки:
Школа кожевенного мастерства: сумки, ремни своими руками
 Ваша оценка:


   0x08 graphic
Потом я начал "причёсывать" его до вида приличного...
   0x08 graphic
Что получилось - судить Вам...

0x08 graphic

0x08 graphic
0x08 graphic
0x08 graphic
0x08 graphic

0x08 graphic

Мама,

я больше не боюсь

целых чисел...

(транкейт-математика)

  
   ї1 Георгий-искуситель
  
   В каждом отделе есть люди, которые любят работать, а есть другие, которым приходится...
   Жора не относился ни к тем, ни к другим. Своё предназначение он видел в том, чтобы "доставать" других. И этим приносил, как я считаю, огромную пользу обществу.
   Особенно ядовитым он был по понедельникам. Однажды, в один такой счастливый день он подошёл ко мне.
   - Я слышал, молодой человек,- Жора всех называл "молодыми", даже тех, кто старше его,- что Вы преуспеваете в деле программирования, особенно с использованием объектов из множества действительных чисел...
   - Да, я использую действительные числа. А как же иначе?
   - А может быть, Вам, молодой человек, лучше сначала целые числа досконально изучить?
   - Но целые я знаю...
   - Ой ли? Сколько будет два минус один?
   - Один,- улыбнулся я, ничего не подозревая.
   - А 3 минус 1?- Жора тоже улыбнулся, но ехидно.
   - Два...
   - А 4 минус 1?
   - Ну это слишком!- я возмутился.
   - Как говорят программисты - "истина остаётся истиной, даже если к ней прибавить ложь". Что в переводе на язык обречённых жить без программирования означает: "Истины много не бывает". Очень скоро ты это поймёшь. На тебе прогму одну. Набери её, а я потом подойду.
   Я взял "прогму". Вот что там было, в этой программе :
  
   uses crt ;
   var
   i :integer;
   x,z,a,b :real;
  
   begin clrscr;
  
   for i:=1 to 9 do
   begin
   a:=i+0.1;
   b:=1.1;
   z:= a - b;
   x:=trunc( z );
  
   writeln( 'trunc(',a:4:1,' - 1.1) = ',x:5:2);
   end;
  
   readln;end.
  
   А вот что выводила эта безобидная прогма на монитор:
  
   trunc( 1.1 - 1.1) = 0.00
   trunc( 2.1 - 1.1) = 0.00
   trunc( 3.1 - 1.1) = 1.00
   trunc( 4.1 - 1.1) = 3.00
   trunc( 5.1 - 1.1) = 4.00
   trunc( 6.1 - 1.1) = 5.00
   trunc( 7.1 - 1.1) = 6.00
   trunc( 8.1 - 1.1) = 7.00
   trunc( 9.1 - 1.1) = 8.00
  
   { Комментарий для тех, кто не знает языка программирования "Pascal": функция trunc(x) просто обрезает мантиссу действительного числа x. }
  
   Я поёжился, покряхтел, поохал, а через минуту ко мне подбежал не в меру счастливый Жорик:
   - Ну что, студент? Что скажешь?
   Я молчал. Наверно, я выглядел бледным, бедным и совсем больным. Дело в том, что в жизни я такого не встречал: я про "прогму".
   - Чего молчишь, как рыба, которая украла у рыбака кошелёк? Кстати, слово "кошелёк" теперь пишется через одно "ё" или через ё в квадрате? Надо отвечать так: в квадрате денег всегда больше... даже если их количество отрицательно... Это не я, это Стасик придумал! Как тебе?
   Мне сейчас было не до денег... Иногда бывает такое.
   - Я требую сатисфакции!- не унимался Жора,- скажи хоть что-нибудь, квазимодо души моей!
   Жора всегда "сыпал" смешными канделябрами непонятных выражений, когда у него было хорошее настроение. Я помолчал ещё с минуту, как того требовала дипломатия, и, наконец, сказал голосом поверженного оппонента:
   - Мама родная, я теперь боюсь целых чисел!
   Жора был счастлив как рыбак, которому какая-то совестливая рыба вернула слегка пожеванный "кошёлёк" с отрицательным червонцем в квадрате...
  
  
   ї2 В поисках...
  
   Честно говоря от чудачеств Жоры всегда была какая-либо польза. И в этот раз тоже: во-первых, я удивился, увидев то, что никогда раньше не видел. А это всегда заставляет думать. Во-вторых, я вообще зауважал транкейты - trunc(x). А когда кого-то или чего-то шибко уважаешь - от этого всегда огромнейшая польза. Потому что хуже всего, когда человек всех презирает и всех считает глупее себя - в этом случае человеку не у кого учиться и не к чему стремиться... Только не подумайте, что я понял это в ранней юности.
  
   Но обширно вспоминать юность я сейчас не собирался - лучше вспомнить, подумал я, всё, что я знаю о транкейтах ( о функциях trunc(x) ).
   Вспомнилась простая формула:
  
   round(x) = trunc(x+0.5)
   ( комментарий: функция round(х) округляет Х до ближайшего целого )
  
   Но при ближайщем рассмотрении оказалось, что она работает только при положительных Х. Испортив некоторое количество бумаги, я написал точную формулу:
  
   round(x) = trunc(x+0.5*sign(x))
  
   Она мне понравилась. Подумав, я написал ещё одну:
  
   round( -x) = trunc( -x -0.5*sign(x))
  
   поскольку я уже знал, что:
  
   trunc(-x) = -trunc(x) ( знак выносится )
   sign(-x) = -sign(x)
  
   ( По своему отношению к знакам обе эти функции похожи на синус. )
  
   А затем написал, почти не думая:
  
   trunc(a+b)=
   trunc(a)+trunc(b)+trunc(a+b-trunc(a)-trunc(b)) (1)
  
   И напрасно не думал, поскольку эта формула тоже жила только при положительных a и b.
   И тут я понял, почему я написал формулу (1) "не думая" - потому что в это время я на самом деле решал задачку Жоры. В его "прогму" надо просто добавить маленькое число "диди":
  
   uses crt ;
   const Dd=1E-10;
   var
   i :integer;
   x,z,a,b :real;
  
   begin clrscr;
  
   for i:=1 to 9 do
   begin
   a:=i+0.1;
   b:=1.1;
   z:= a - b;
   x:=trunc( z + Dd );
  
   writeln( 'trunc(',a:4:1,' - 1.1) = ',x:5:2);
   end;
  
   readln;end.
  
   Объяснение простое: компилятор, перед тем, как складывать, переводит числа в двоичную систему, и отсюда возникают ошибки - мизерные, но... Если бы решались дифференциальные ураврения, то программа такие крохотные ошибки и не заметила бы, но поскольку мы в программе используем функцию транкейт, то компилятор записав вместо числа 2.0 весьма похожее число 1.999999999, выдаёт ответ:
  
   Trunc(2.0) равен: trunc(1.999999999) = 1
  
   Теперь можно было думать о формуле (1). Случай, когда оба числа a и b отрицательны, не интересен, так как знак минус можно спокойно вынести за транкейт. Значит мне интересно знать транкейт разности. Подумав, написал формулу:
  
   trunc(b-a) = trunc(b-1) - trunc(a)
   +trunc(1+b -trunc(b) -a +trunc(a) ) (2)
  
   В формулу (2) тоже пришлось добавить "диди":
  
   trunc( b-a ) = trunc(b -1 +Dd) -trunc(a +Dd)
   +trunc( 1 +b-trunc(b) -a+trunc(a+Dd) +Dd )
  
   где Dd=1E-10
  
   Область определения параметров: b>0, b>|a|
   Если надо вычислить trunc(2.1-4.6) ,то это надо сделать так: trunc(2.1-4.6) = -trunc(4.6-2.1).
   Конечно, можно было сделать формулу на все случаи жизни. Но зачем? Она будет сложной... А ведь если сказать честно, то и для формулы (2) не так легко найти практического применения.
   Но тем не менее, после этой никому ненужной формулы я нашел я ещё одну - ещё более страшную и ещё более ненужную:
  
   Trunc(b*b) = trunc(b)*trunc(b)
   +trunc(2*trunc(b)*(b-trunc(b)) )
   +trunc(b*b-trunc(b)*trunc(b)-trunc(2*trunc(b)*(b-trunc(b))))
  
   Написал я этого крокодила... и сказал себе на полном сурьёзе:
   - Мама, я больше не боюсь целых чисел!
  
  
   ї3 О пользе
  
   Честно говоря, я такой же человек как и все: тоже люблю пользу, деньги и удовольствия. Удовольствия я уже немного получил, денег от этих формул не дождёшься, а пользу всегда можно создать самому.
   Из всех вышеприведённых формул самая полезная наверно одна:
  
   trunc(-x) = -trunc(x)
  
   И самая короткая. И я решил придумать ещё одну - такую же полезную, по возможности такую же короткую.
   Поначалу у меня получались крокодилы невероятных размеров, но со временем я научился оформлять свои мысли кратко...
   И наконец... Вот она:
  
   Trunc(K*y) = K*trunc(y) +trunc(K*(y-trunc(y))) (3)
  
   Чтобы увидеть пользу её надо записать так:
  
   Trunc(K*y)/K=trunc(y)+trunc(K*(y-trunc(y)))/K (4)
  
   Ну как - уже увидели? Это формула для округления числа до определенного знака после десятичной точки. Например, чтобы округлить 3.14159 до 0.01, надо сделать так, по логике левой части (4):
  
   Trunc(3.14159 * 100)*0.01 = 3.14
  
   По логике правой части (4) делаем так:
  
   Trunc(3.14159) + trunc(100*(3.14159 -trunc(3.14159))*0.01 = 3 + trunc(100*0.14159)*0.01 = 3.14
  
   Теперь, я думаю, и Вы получили нужную долю удовольствия. Но честно я скажу: я верю в то, что все в мире формулы полезные - просто не всё мы умеем правильно использовать...
  
  
   ї4 О духовном
  
   Но польза от математики далеко не всегда бывает чисто практической. Иногда она бывает теоретической, или даже моральной, можно сказать, духовной. Например, когда простая вещь заставит Вас поверить во что-то более большее.
   Давно у меня был такой случай. Я не верил, что функция sign(x) является математической. Действительно, что математического в строках:
  
   Function sign(x:real):real;
   Begin
   if x<0 then sign:=-1;
   if x=0 then sign:=0;
   if x>0 then sign:=1;
   End;
  
   Я мог поверить во что угодно - в то, что эта функция "химическая", "патрульно-дорожная", "приказательно-заставлятельная" - не верил только в то, что она математическая... До тех пор, пока сам не написал такие строки:
   function sign(z:real):integer;
   const r=1E-38;
   BEGIN
   sign:= round( z/(abs(z)+r) );
   END;
   Придумав, найдя, создав то, во что ранее сам не верил, я удивился, поверил и принял истину: мир гораздо сложнее того, что мы о нем можем вообразить... Да... Мир прекрасен, и главное - гениально продуман.
   Но поскольку ума с течением времени у каждого человека приблавляется не слишлом много, то история опять повторилась. Теперь в отношении функции trunc(x) . До сих пор я не верил, что она математическая. Сидит внутри компилятора некий "червячок" и откусывает мантиссы у действительных чисел - ну что тут математического? Или обрезать кусачками провод, пусть даже по которому течёт с помощью интернета великая теорема Ферма - ну что тут математического? И вот настал день...
   Помните формулу (3)? Я напомню:
  
   Trunc(K*y) = K*trunc(y) +trunc(K*(y-trunc(y))) (3)
  
   Я подумал: а что если доказать, что у функции y-trunc(y) имеется обратная функция? Тогда бы я поверил, что функция y-trunc(y) является математической. Не знал я тогда, что дух математики и на сей раз решил меня удивить.
   Вот график функции z=y-trunc(y) :
   0x08 graphic
   z
  
   1
  
  
   1 y
   Область определения функции Z : 0 =< z <1
   ( то есть, зет не меньше нуля и меньше единицы )
  
   Я подумал и нарисовал обратную функцию:
  
   0x08 graphic
y
  
  
  
  
  
  
  
  
  
  
  
   z
  
   И записал её:
   y = z + C (5)
  
   здесь z - аргумент, но его область определения та же: 0 =< z <1
   а С - это переменная константа, она принимает целые положительные значения:
  
   С = 0, 1, 2, 3, ...
   Полученную обратную функцию (5) я подставил в формулу (3), и получил новую формулу:
  
   trunc(K*(z+C))= K*trunc(z+C)+trunc(K*z) (6)
  
   и не забывая, что:
   0=< z < 1
   C = 0, 1, 2, 3, ...
   K - тоже целое, положительное
  
   я подставлял различные допустимые значения в формулу (6). И пришел к выводу, что всё правильно.
   Функция y = z + C
   есть обратная для функции z = y - trunc(y)
  
   Несколько непривычно. Но всё верно. Есть такое свойство. Если F(t) - прямая функция, а W(s) - обратная к ней, то :
  
   F(W(s)) = s
   W(F(t)) = t
  
   Например, задана функция s= t2 или, по иному, t=s0.5
   Тогда прямая функция - F(t)=t2 а обратная к ней функция равна W(s)= s0.5
  
   Получаем:
  
   F(W(s))=( s0.5 )2 = s
   W(F(t))=( t2 )0.5 = t
  
   В нашем случае прямая функция: F(y) = y - trunc(y)
   А обратная: W(z) = z+C
   Поэтому пишем:
  
   F(W(z)) = z+C - trunc(z+C) = z+C-C = z
  
   Правда, непонятно, как записать зависимость обратной от прямой, то есть W(F(y))
   А всё потому, что мы получили удивительное сочетание: усечённая по области определения переменная z и переменная-константа С.
  
   В душе я понимаю как записать W(F(y)), но разум стесняется записать это на бумаге. Может быть, в следующий раз...
  
  
   ї5 И снова о пользе
  
   Разгоровы о духовном как правило кончаются, когда начинает работать конвейер прагматизма. Вспомните формулу:
   round(x) = trunc(x+0.5*sign(x))
  
   Интересна функция
  
   0x08 graphic
y = x+0.5*sign(x)
  
   Её график выглядит так:
  
  
  
  
  
  
  
  
   Понятно, что график обратной функции рисуется так:
   0x08 graphic
  
  
  
  
  
  
  
  
  
   Значит, обратная функция равна:
  
   x = y - 0.5*sign(y)
   при |y| > 0.5
  
   Я не могу сдержаться от восхищения:
   - Блин! Это действительно очень красиво!
   Впрочем, Вы тоже начнёте ругаться такими красивыми ругательствами, когда прочтёте статью "Многоугольник с одной стороной"
   В одном я уверен точно: "зачёт" по целым числам я, наверное, сдал... Георгию.
  

Павел Сапунов

27 августа 2008 года

  
 Ваша оценка:

Связаться с программистом сайта.

Новые книги авторов СИ, вышедшие из печати:
О.Болдырева "Крадуш. Чужие души" М.Николаев "Вторжение на Землю"

Как попасть в этoт список

Кожевенное мастерство | Сайт "Художники" | Доска об'явлений "Книги"