Domanda

I want to write sql query that will calculate iterated function sequence of some function until it reaches fixed point, and then return that fixed point:

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

E.g. let f(x) = (256/x + x)/2:

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

Here is my attempt to write the query:

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;

Now it is possible to get iterated sequence that converges to some fixed point:

=# 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)

(Actually it converges to √256 because this is Babylonian method of calculating square roots.)

Now I need one additional query to get only the last row from the sequence:

=# 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)

The question is: how to write this more concise?
In particular, I don't like that separate query to get the last value (and that I need to accumulate all intermediate values in array).

È stato utile?

Soluzione

Leave the definition of f as it is.

In your sequence generation, retain the value of f(x) in the output.

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;

Then limit your result to just the fixed value.

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

Edit: Add a procedural version.

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;
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top