Вычислить сумму ряда с заданной точностью e(epsilon)>0
Есть решение, нет уверенности, что правильно:
PROGRAM PRP6; var e, res, a, b : REAL;
FUNCTION comp(x, y, e: REAL):real;{функция нахождения суммы} var w, z, sum: real; BEGIN w:=x; z:=y; sum:=1/(w*z); IF 1/(w*z) >= e then BEGIN sum:=comp(x, y, e); w:=w+4; z:=z+4; END else comp:=sum;
Просьба проверить и поправить, если что неправильно
TarasBer
3.02.2010 2:11
Так ты сначала у себя запусти, проверь. Ты ж не проверял. Если 1/(w*z) >= e, то, во-первых, делается рекурсивный вызов с теми же параметрами (что приведёт к переполнению стека - рекурсии неоткуда выйти), а во-вторых, в результат ничего не записывается. И, в третьих, с математической точки зрения то, что очередной член ряда стал меньше епсилон, ещё отнюдь не означает, что мы просуммировали ряд с достаточной точностью (я так могу гармонический ряд "просуммировать", ага). Вообще принято сначала рассчитать оценку погрешности через очередной член ряда (для каждого ряда оценка своя), а потом через эту оценку и определять, достигли мы точности, или нет.
dog
3.02.2010 3:34
Она при некоторых значениях епсилон выдает всегда один и тот же ответ, а при некоторых действительно происходит переполнение стека.
А если так?
FUNCTION comp(x, y, e: REAL):real;{функция нахождения суммы} var w, z, sum: real; BEGIN w:=x; z:=y; sum:=1/(w*z); IF 1/(w*z) >= e then BEGIN sum:=comp(w, z, e);{исправлено} w:=w+4; z:=z+4; END else comp:=sum;
END;
Ну на самом деле сложная задача, ряды да еще и рекурсия.
TarasBer
3.02.2010 3:53
Дык проверь сначала, чего меня спрашивать. От замены двух букв на равные им ничего не изменится, очевидно.
volvo
3.02.2010 4:52
Цитата
Ну на самом деле сложная задача, ряды да еще и рекурсия.
На самом деле рекурсия - проще, чем итерация. Если понимаешь сам принцип написания рекурсивных функций. Не веришь? Смотри:
1. Для начала определимся с тем, что наша рекурсия будет принимать, и что - возвращать... Ну, возвращать она будет Real, это очевидно. А принимать - 2 числа которые стоят в знаменателе, так? Не совсем. Зачем передавать их оба, если можно передать одно, только X, а зная X вычислять Y когда нужно?. Итак, с заголовком определились:
function f(X: integer; eps: real): real;
2. Теперь делаем то, без чего работающей рекурсивной функции не может существовать - опишем условие выхода из функции. Когда надо выходить? Правильно, если 1/(x * (x+2)) станет меньше eps... Так и запишем... Хм. А что запишем? Что должно вернуться, когда пришло время выйти из функции? Функция у нас считает сумму ряда, значит надо вернуть какое-то значение, которое эту сумму не испортит. Такое значение у нас только одно: ноль...
Вот, значит, и пишем условие выхода:
if 1/(x * (x+2)) < eps then f := 0
3. Ну хорошо, если условие выполнилось, понятно что будет. А если НЕ выполнилось - что делать? Словами описываем алгоритм: сложить значение текущего члена с суммой последующих. Так? Так... А на Паскаль перевести?
else { Не выполнилось условие } f := (1/(x * (x+2))) + f(x + 4, eps); { сложим текущий с последующими }
Итого - собираем все вместе:
function f(X: integer; eps: real): real; begin if 1/(x * (x+2)) < eps then f := 0 else f := (1/(x * (x+2))) + f(x + 4, eps); end;
Вот и все... Сложно? По-моему, нет...
НО. (опять это НО, я всегда ловлю себя на мысли, что слишком часто получаются какие-то оговорки, ничего не бывает просто и без проблем). Эта функция будет работать, но до определенного предела. Где предел, и почему функция не работает дальше (в настройках компилятора выставить все Run-time проверки !!!) - вот вопрос...
Первый, кто ответит на этот вопрос, правильно объяснит причину, и приведет способ исправления моей функции - получает +1 к рейтингу ...
Unconnected
3.02.2010 11:43
Наверное, проблема с максимальной точностью значения, которое может принять real?)
Client
3.02.2010 19:11
ошибка в этом месте
if 1/(x * (x+2)) < eps then f := 0
проиходит тогда, когда знаменатель становится 33489 и х=183 (видимо операцию, значение которой больше 32767, в рекурсии нельзя выполнить).Поэтому, делаю проверку на вхождение в дипазон типа integer.
if (sqrt(maxint)<x ) then f:=0 else if 1/(x*(x+2)) < eps then f:=0 else f:=(1/(x * (x+2))) + f(x+4,eps);
Добавлено через 14 мин. это я так думаю
volvo
3.02.2010 22:46
Оба предыдущих ответа не содержат правильной идеи... Во-первых, Real-а вполне себе хватает. А во-вторых,
Цитата
видимо операцию, значение которой больше 32767, в рекурсии нельзя выполнить
- в рекурсии можно вычислять и гораздо бОльшие результаты. Рекурсия в этом смысле ничем от итерации не отличается.
Думаем дальше
TarasBer
3.02.2010 22:46
> else f := (1/(x * (x+2))) + f(x + 4, eps);
Тут какая-то неприятность со стеком сопроцессора должна произойти. Надо
else begin tmp_f := (1/(x * (x+2))) + f(x + 4, eps); f := tmp_f; end;
volvo
3.02.2010 22:52
Цитата
Тут какая-то неприятность со стеком сопроцессора должна произойти.
Ничего не должно произойти, без разбиения все прекрасно вычисляется. Можно, конечно сделать то, что ты написал, но это не имеет решающего значения, можно обойтись и без этого.
Еще идеи есть? Client, ведь ты показал скриншот, который должен был заставить тебя задуматься кое над чем... Чего ты не задумался?
P.S. А где, собственно, топикстартер? Ей не интересно? Или ждет, когда всё решат, все ошибки исправят, а потом втихую заберет, и все, добилась своего?
TarasBer
3.02.2010 22:55
Тут про переполнение типа говорили. Я бы написал дробь как (1/x)*(1/(x+2))
volvo
3.02.2010 23:03
Цитата
Я бы написал дробь как
Хм...(Показать/Скрыть)
Вот от тебя (с твоей тягой к оптимизации) я это ожидал услышать меньше всего. Нам, конечно, мало одного деления, Добавим еще одно, чтоб еще дольше вычислять значение, и еще больше загрузить стек сопроцессора. Может, тогда он переполнится, и твое предположение из поста №9 окажется истинным?
Client
3.02.2010 23:06
x * (x+2)
ошибка именно здесь, даже без деления. ты про 1 картинку на 1 скрине?
TarasBer
3.02.2010 23:15
[offtop] > Вот от тебя (с твоей тягой к оптимизации) я это ожидал услышать меньше всего.
Моя тяга к оптимизации упала в обморок при виде вычисления ряда через рекурсию и валяется без сознания, поэтому молчит. [/offtop]
Проверил у себя. Вываливается именно с ~8 итераций.
P.S. А где, собственно, топикстартер? Ей не интересно? Или ждет, когда всё решат, все ошибки исправят, а потом втихую заберет, и все, добилась своего?
volvo, спасибо за простое и понятное объяснение рекурсии!
Тут тут я просто неожиданно как-то даже, что рекурсия так просто. Просто в легком шоке даже немножко от простоты рекурсии. Над "подводным камнем" думаю тоже.
dog
5.02.2010 5:54
Цитата(TarasBer @ 3.02.2010 19:15)
f := tmp_f + 1/(longint(x)*(x+2))
То есть как я понимаю при определенной величине происходит обыкновенное деление на ноль? Поэтому программа и вылетает? А ноль образуется в результате переполнения переменной x?
Вот замена входного параметра eps в функции f на double по моему немножко отодвинула порог вылета, но кардинально вопрос не решила. Будем думать дальше.
TarasBer
6.02.2010 18:29
> Поэтому программа и вылетает?
Нет, у меня программа вылетала из-за переполнения стека сопроцессора. Классический пример - рекурсивное вычисление факториала. Тут где-то на форуме есть пример с объяснением, поищите.
Client
7.02.2010 23:18
volvo, кажется больше идей нету... тут приведение типов как в С может и помогло бы... хотелось бы узнать, в чем кроется секрет.
volvo
8.02.2010 3:03
Приведение типов не нужно... Явное по крайней мере. Приведи неявно.
+ к этому - пока никто не показал окончательный код, который не будет вылетать при eps = 1E-8независимо от настроек компилятора Турбо-Паскаля. То есть, независимо от того, выбран у меня режим эмуляции сопроцессора или режим 8087/80287... Окончательную версию в студию можно? А то выцеживаете по одной строке... Я что, одну строку привел?
Krjuger
8.02.2010 3:16
Я думаю,что для начала надо разобраться почему вылетело RT215(Arithmetic overflow),а не RT205(Floating point overflow). Arithmetic overflow - целочисленное переполнение - возникает при выполнении арифметической операции над целыми числами, когда результат операции выходит за границы соответствующего типа.
Как говорил Client, при 33к с копейками в знаменателе уже происходит вылет, при этом есть тип Short, который может содержать целые числа о -32,768 до 32,767.Принципи вот из-за этого и происходит переполнение,но для мен лично небольшая загадка почему именно тип short береться для выполнения операций,а не сам integer.
Это лиш мои догадки,скорее всего я не прав.
TarasBer
9.02.2010 0:42
> пока никто не показал окончательный код, который не будет вылетать при eps = 1E-8
А там слишком много итераций уже надо. Стека не хватит. А "код, который не будет вылетать" я показать могу, но он не будет соответствовать требованию делать через [s]жо[s/]рекурсию.
volvo
11.02.2010 16:12
Цитата
А там слишком много итераций уже надо. Стека не хватит.
Ответ неверный... Для 1E-8 - вычисляется рекурсивно, ничего не вылетает. Если твой код, написанный через жорекурсию этого сделать не может - это чьи проблемы?
В общем, все ясно... Причину не ищем, пытаемся побороть следствие... Успехов.
Client
15.02.2010 20:12
uses crt; var x1:real; i:integer; eps:real;
function f(x:integer):real; begin inc(i); x1:=1.0/(x*(x+2.0)); //это и есть неявное приведение типов? if x1 < eps then f:=0 else f:=x1 + f(x+4); end;
begin clrscr; i:=0; eps:=0.0000001; writeln(f(3):0:7); writeln(i); readkey end.
Получилось так. Терерь проблема со стеком. Убрал описание eps из функции в надажде что будет экономиться место (ведь с каждым вхождением для нее выделяется память в стеке?) и добавил счетчик (для тестирования ) Для 1E-8 происходит переполнение стека. Для 1E-7 работает, вычисляет за 791 итерацию
Добавлено через 7 мин. хм, 16кб за 1321 итерацию... 1321 раз выделяется память для самой функции (real - 6 байт) и лок. переменной x (integer - 2 байта). 1321*(6+2)=10568. А еще где ~5кб? уходят на вычисление
1.0/(x*(x+2.0))
?
Krjuger
17.02.2010 20:39
Volvo,а разве я не высказал причину,ты бы хоть прокоментировал,верно ли, или не совсем,или же совсем не верно.
Lapp
18.02.2010 18:35
Цитата(Client @ 15.02.2010 16:12)
Получилось так. Терерь проблема со стеком.
Client, извини, но у тебя проблема не со стеком... У тебя явная проблема с рекурсией. Твоя программа не будет считать правильно даже при гигабайтном стеке и с прекрасным приведением типов. В ней есть одна очень серьезная ошибка. Давай ты сначала отладишь прогу на FPC или Delphi, а потом уже будешь решать проблемы со стеком на ТР. Хорошо?
Добавлено через 7 мин.
Цитата(Krjuger @ 7.02.2010 23:16)
при этом есть тип Short, который может содержать целые числа о -32,768 до 32,767.Принципи вот из-за этого и происходит переполнение,но для мен лично небольшая загадка почему именно тип short береться для выполнения операций,а не сам integer.
Krjuger, ты извини, но ты так туманно выразился, что причину в твоих словах отыскать я так и не смог. Типа short в TP нет. Есть тип shortint размером в 1 байт. А тип integer как раз и есть 2 байта, от -32К до +32К. Для того, чтоб не было вылетов по целым и по флоат, нужно правильно приводить типы. А причина останова в правильно реализованном алгоритме - это все-таки переполнение стека. И происходит оно в ТР при де... ой, чуть не проболтался! ))
Client
18.02.2010 21:54
убрал свои модификации, оставил как сделал volvo с подсчетом итераций, и eps сделал глобальной. Вот как выглядит функция
function f(x:integer):real; begin inc(i); if 1/(1.0* x * (x+2)) < eps then f := 0 else f := (1/(x * (x+2))) + f(x + 4); end;
и обработчик кнопки
procedure TForm2.Button1Click(Sender: TObject); var r:real; k:byte; begin eps:=1E-1; Memo1.Clear; for k := 1 to 10 do begin i:=0; r:=f(3); Memo1.Lines.Add(inttostr(k)+ ') ' +FloatToStr®); Memo1.Lines.Add('Кол-во итераций: ' + inttostr(i)+ ' eps= '+FloatToStr(eps)); Memo1.Lines.Add(' '); eps:=eps/10; end; end;
Моя ошибка была в добалении переменной и результат был не правильным. Но хотя если заменить выражение переменной, измений быть не должно. Видимо при возвращении функции результата вместо бывшей x1 подставлялось самое последнее значение вместо текущего (т.е. на каждой итерации свое значение)? (в этой стоке)
else f:=x1 + f(x+4);
Хм, на Delphi вроде работает. Теперь паскаль
uses crt; var x1:real; i,k:integer; eps:real;
function f(x:integer):real;
begin inc(i); if 1/(1.0 * x * (x+2)) < eps then f := 0 else f := (1/(1.0 * x * (x+2))) + f(x + 4); end;
begin clrscr; i:=0; eps:=1E-1; for k:=1 to 7 do begin i:=0; writeln(f(3):0:7, ' ',i); eps:=eps/10; end; readkey end.
Выдает такие же значения (до 1Е-7, потом стек переполняется).
пока никто не показал окончательный код, который не будет вылетать при eps = 1E-8независимо от настроек компилятора Турбо-Паскаля. То есть, независимо от того, выбран у меня режим эмуляции сопроцессора или режим 8087/80287...
Нечисто играешь, я тебе не разрешал настраивать компилятор, а ты это сделал... По умолчанию (и у меня именно такие установки) размер стека = 16384, ты его изменил, и хочешь мне доказать, что это сделалось подключением модуля Memory? А ты хотя бы его исходники-то видел, умник? Что такого изменилось, что после простого подключения модуля стек вдруг стал больше? Что происходит при подключении этого модуля, ты вообще знаешь?
Повторяю еще раз: я не хочу лазить в настройки и менять там что-то. Я хочу увидеть программу, которую я могу у себя откомпилировать (возможно даже консольным компилятором), запустить и посмотреть результат. Это понятно? А то смеяться над чужими программами-то ты умеешь, видели. А вот свое написать (да еще какое - простейшее, работа с вещественными числами) тебе, как выяснилось, не по силам?
ну говорилось про процессор ... а про память ничего ну а про memory че б голову не поморочить
Client
19.02.2010 18:04
{$S-} uses crt; var x1:real; i,k:integer; eps:real;
function f(x:integer):real;
begin inc(i); if 1/(1.0 * x * (x+2)) < eps then f := 0 else f := (1/(1.0* x * (x+2))) + f(x + 4); end;
begin clrscr; i:=0; eps:=1E-1; for k:=1 to 8 do begin i:=0; writeln(f(3):0:7,' ',eps, ' ',i); eps:=eps/10; end; readkey end.
Фишка в директиве {$S-} ? 1Е-8 работает с обычными настройками
Rian
19.02.2010 19:17
uses crt; var x1,s:real; i,k:integer; eps:real; z:boolean;
procedure f(x:word); begin inc(i);
if 1/(1.0*x * (x+2)) < eps then
else begin s :=s+1/(1.0*x * (x+2)); f(x+4); end; end;
begin clrscr; eps:=1E-1; for k:=1 to 8 do begin i:=0; s:=0; f(3); writeln(s:0:7, ' ',i,' ',eps); eps:=eps/10; end; readkey end.
TarasBer
19.02.2010 20:37
Костыль.
function f(X: longint; eps: real): real; var r, rf: real; begin r := 1/(longint(x+0)*(x+2)) + 1/(longint(x+4)*(x+6)) + 1/(longint(x+8)*(x+10)) + 1/(longint(x+12)*(x+14)); if (r - 4*eps < 0) then r := 0 else begin rf := f(x + 16, eps); r := r + rf; end; f := r; end;
Для стека 65536 и епсилон 1е-8 это работает, да. Для стека 16К надо ещё костылей повбивать.
Rian
19.02.2010 21:38
так что ответ можно считать правильным?
volvo
25.02.2010 18:44
Цитата
так что ответ можно считать правильным?
Твой что-ли? Размечтался: Нажмите для просмотра прикрепленного файла (хотя, в принципе, можешь считать как хочешь; программа которая вылетает с переполнением стека тобой тоже может считаться правильной - это на твое усмотрение)
Цитата
Костыль.
Вот именно, что костыль...
В общем, ребята, не напрягайтесь, сортируйте массивы и находите в них максимумы дальше, у вас это лучше получается...
Всем привет, от темы отписался. Стандартно НЕРАБОТАЮЩИЕ ответы меня больше не интересуют...
TarasBer
27.02.2010 0:24
> В общем, ребята, не напрягайтесь, сортируйте массивы и находите в них максимумы дальше, у вас это лучше получается...
Понты... Какое чудо заставит программу, использующую стек размером 16К, которая делает кучу итераций в рекурсии, не проедать стек полностью? Компилятор ТП7 не умеет распознавать подобные рекурсии и разворачивать их в цикл. Можно чисто итераций уменьшить (что я и сделал). А ещё можно руками править регистр стека, сохраняя его содержимое в отдельный файл. Чудес не бывает, вот и всё.
Rian
1.03.2010 21:07
Цитата(volvo @ 25.02.2010 13:44)
Твой что-ли? Размечтался: Всем привет, от темы отписался. Стандартно НЕРАБОТАЮЩИЕ ответы меня больше не интересуют...
блин... это кто еще не чисто играет? чтобы прога показала переполнение, как это было у тебя на 6 знаках (251) мне пришлось уменьшить стек до 1024 (меньше не делается)
ты што на АРИФМОМЕТРЕ компилил???
PS. правильный ответ в студию!
Гость
5.03.2010 2:42
-
Jova
5.03.2010 3:15
Цитата(volvo @ 25.02.2010 13:44)
Цитата так что ответ можно считать правильным?
Твой что-ли? Размечтался:
А не поторопился ? Решение Rian у меня работает. Нет переполнения стека