2014-01-06 2 views
1

Я хочу написать SQL запрос, который рассчитает iterated function sequence некоторой функции до тех пор, пока не достигнет фиксированной точки, а затем вернуть эту стационарную точку:Вычислять фиксированную точку функции в sql?

f(f(f(f ..(x)))) = x0 = f(x0) 

Э.Г. пусть f(x) = (256/x + x)/2:

create function f(x float) returns float as $$ 
    select (256/x + x)/2 
$$ language sql; 

Вот моя попытка написать запрос:

create function f_sequence(x float) returns table(x0 float) as $$ 
    with recursive 
    t(a,b) as 
     (select x, f(x) 
     union all 
     select b, f(b) from t where a <> b) 
    select a from t; 
$$ language sql; 

Теперь можно получить итерированную последовательность, сходящуюся к некоторой фиксированной точке:

=# select f_sequence(333); 
    f_sequence 
------------------ 
       333 
166.884384384384 
84.2091902577822 
43.6246192451207 
24.7464326525125 
17.5456790321891 
16.0680829640781 
16.0001442390486 
16.0000000006501 
       16 
(10 rows) 

(на самом деле он сходится к √256, потому что это расчета квадратных корней.)

Теперь мне нужно еще один запрос, чтобы получить только последнюю строку из последовательности:

=# with 
    res as (select array_agg(f_sequence) as res from f_sequence(333)) 
    select res[array_length(res,1)] from res; 
res 
----- 
    16 
(1 row) 

Вопрос заключается в: как написать это более кратким?
В частности, мне не нравится этот отдельный запрос, чтобы получить последнее значение (и мне нужно скопировать все промежуточные значения в массиве).

+0

В стороне: вы делаете это для развлечения или есть серьезная причина для использования pg здесь? –

+0

Это не похоже на использование SQL для меня. – duffymo

+0

Это часть некоторой реальной проблемы. Мне нужно рекурсивно построить подмножество объектов. На каждом шаге я выбираю лучший объект для добавления к набору, пока невозможно добавить больше. –

ответ

2

Оставьте определение f так, как оно есть.

В генерации последовательности сохраняйте значение f (x) на выходе.

create function f_sequence(x float) returns table(x0 float, fx float) as $$ 
    with recursive 
    t(a,b) as 
     (select x, f(x) 
     union all 
     select b, f(b) from t where a <> b) 
    select a, b from t; 
$$ language sql; 

Затем ограничьте свой результат только фиксированным значением.

select x0 from f_sequence(256) where x0 = fx; 

Редактировать: Добавить процедурные версии.

create function iterf(x float) returns float as $$ 
declare fx float := f(x); 
begin 
    while fx != x loop 
    x := fx; 
    fx := f(x); 
    end loop; 
    return fx; 
end; 
$$ language plpgsql; 
+0

Ничего себе, спасибо. Очень хорошо. Но по-прежнему требуется сохранить целую последовательность промежуточных значений до тех пор, пока не будет дан результат. В моем случае я ожидаю ~ 10k итераций, так что это не большая проблема, а просто из любопытства, возможно ли это сделать в постоянной памяти? –

+0

Единственный способ, о котором я мог думать, это перейти к процедурной sql и loop. Но если вы идете по этому подходу, вы также можете сделать это на прикладном уровне, как предложили другие. Я добавил пример того, как вы могли бы сделать это в постоянной памяти для ответа. – yieldsfalsehood

+0

Да, спасибо. Это было очевидно, но я немного перехожу к рекурсии. –

Смежные вопросы