Хабр Курсы для всех
РЕКЛАМА
Практикум, Хекслет, SkyPro, авторские курсы — собрали всех и попросили скидки. Осталось выбрать!
100 даже близко не является правильным ответом. На самом деле последовательность сходится к 5— что значит «на самом деле»? Последовательность сходится именно к 100, так вы ее задали, а к 5 она сходиться не может в принципе, это точка отталкивания а не притяжения.
def f(y,z)
108 - (815 - 1500 / z) / y
end
@x = { 0 => Rational(4), 1 => Rational(17,4) }
def x(n)
@x[n] ||= f(x(n-1), x(n-2))
end
(2..30).each do |r|
puts x(r).to_f
end
-- Пример на Lua
-- Исходная функция
function fib(x)
if x < 2 then return -x end;
return fib(x-1) + fib(x-2);
end
-- Итерация 1: явно выделяем аккумуляторную функцию (+(x,y))
function fib1(x)
if x < 2 then return -x end;
local x1 = fib1(x-1);
local x2 = fib1(x-2);
return x1 + x2;
end
-- Итерация 2: возвращаем N-1 предыдущих результатов из функции
function fib2(x)
if x < 2 then return -x, nil end;
local x1 = fib2(x-1);
local x2 = fib2(x-2);
return x1 + x2, x1;
end
-- Итерация 3: избавляемся от всех рекурсивных вызовов, кроме одного
function fib3(x)
local function f(x, stop)
if stop then return -x, nil end;
if x < 2 then return -x, f(x-1, true) end;
local x1, x2 = f(x-1, stop);
return x1 + x2, x1;
end
return f(x, false);
end
-- Общий вид рекурсивной функции с несколькими рекурсивными вызовами
-- @X Вектор аргументов функции
-- @check условие выхода из рекурсии, функция от вектора аргументов
-- @default действия, выполняемые при выходе из рекурсии, функция от вектора аргументов
-- @pre действия, выполняемые над аргументами при продолжении рекурсии, до вызова рекурсивной функции, функция от вектора аргументов
-- @post действия, выполняемые над результатом рекурсивного вызова при продолжении рекурсии, функция от вектора результатов
function genF(check, default, pre, post)
local function f(X)
if check(X) then return default(X) end;
return post(
f(pre[1](X)),
f(pre[2](X)),
f(pre[3](X)),
...
);
end
return f;
end
-- Преобразованная функция
function genG(check, default, pre, post)
local function f(X, stop)
if stop then return default(X), nil end;
if check(X) then return default(X), f(pre(X), true) end;
return post(f(pre(X), stop)), x1;
end
return funtion(X) return f(X, false); end
end
N-1 предыдущихГодится только для последовательностей вида Ak = f(Ak-1, ..., Ak-N), где f – какая-то «комбинирующая функция». Но последовательности могут быть и такие, что k-ый член определяется, например, с помощью k/2, k/3 и т.д.
function f(X)
if check(X) then return default(X) end;
return post(
X,
f(pre[1](X)),
f(pre[2](X)),
f(pre[3](X)),
...
);
end
function g(x)
if x % 2 == 0 return x / 2 end
return g(g(x-1))
end
При использование этого метода не появляется никаких преимуществ перед обычной рекурсией.
static int RecFunc(int k){
int s=1;
for(int i=0;i<k;i++){
s+=RecFunc(i);
s+=RecFunc(i/2);
}
return s;
}
static int ExecFunc(int k) {
Stack<IEnumerator<int[]>> stack=new Stack<IEnumerator<int[]>>();
stack.Push(RecFunc(k).GetEnumerator());
for(;;) {
stack.Peek().MoveNext();
int[] arr=stack.Peek().Current;
if(arr[0]==0) { // it was return
int lastres=arr[2];
stack.Pop().MoveNext(); // end iterator
if(stack.Count==0) return lastres;
stack.Peek().Current[2]=lastres;
} else { // call
stack.Push(RecFunc(arr[1]).GetEnumerator()); // recursive call
}
}
}
static IEnumerable<int[]> RecFunc(int k) {
int s=1;
int[] inf1=new int[3]{1,0,0}; // code, argument, placeholder for result
for(int i=0;i<k;i++) {
inf1[1]=i; // RecFunc(i)
yield return inf1;
s+=inf1[2];
inf1[1]=i/2; // RecFunc(i/2)
yield return inf1;
s+=inf1[2];
}
inf1[2]=s;
inf1[0]=0;
yield return inf1;
}
class Program{
public static void Main(string[] args){
RRFunc.Execute(RecFunc,4);
}
public static IEnumerable<RFunc> RecFunc(object[] arg) {
int s=1;
int k=(int)arg[0];
for(int i=0;i<k;i++) {
arg[0]=i; // RecFunc(i)
yield return RecFunc;
s+=(int)arg[0];
arg[0]=i/2; // RecFunc(i/2)
yield return RecFunc;
s+=(int)arg[0];
}
Console.WriteLine("func({0})={1}",k,s);
arg[0]=s;
yield return null;
}
}
public delegate IEnumerable<RFunc> RFunc(object[] argres);
class RRFunc {
public static object Execute(RFunc func,object obj) {
object[] arg=new object[1] { obj };
Stack<IEnumerator<RFunc>> stack=new Stack<IEnumerator<RFunc>>();
stack.Push(func(arg).GetEnumerator());
for(;;) {
stack.Peek().MoveNext();
RFunc next=stack.Peek().Current;
if(next==null) { // it was return
stack.Pop().MoveNext(); // end iterator
if(stack.Count==0) return arg[0];
} else { // call
stack.Push(next(arg).GetEnumerator()); // recursive call
}
}
}
}
public delegate IEnumerable<RFunc> RFunc(object[] argres);
class RRFunc {
public static object Execute(RFunc func,object obj) {
object[] arg=new object[1] { obj };
Stack<IEnumerator<RFunc>> stack=new Stack<IEnumerator<RFunc>>();
for(;;) {
if(func!=null) stack.Push(func(arg).GetEnumerator()); // recursive call
else if(stack.Count==1) return arg[0];
else stack.Pop();
stack.Peek().MoveNext();
func=stack.Peek().Current;
}
}
public static IEnumerable<RFunc> Fact(object[] arg) {
int k=(int)arg[0];
if(k>2) {
arg[0]=k-1;
yield return Fact;
arg[0]=(int)arg[0]*k;
}
yield return null;
}
}
abstract class Operation<T> {
public abstract void Process(Stack<IEnumerator<Operation<T>>> stack);
protected abstract void SetResult(T value);
public static void DisposeFrame(IEnumerator<Operation<T>> en) {
var d = en as IDisposable;
if (d != null) d.Dispose();
}
}
class Return<T> : Operation<T> {
public T Value { get; set; }
public override void Process(Stack<IEnumerator<Operation<T>>> stack) {
var top = stack.Pop();
stack.Peek().Current.SetResult(Value);
DisposeFrame(top);
}
protected override void SetResult(T value) {
throw new NotSupportedException();
}
}
class Call<T> : Operation<T> {
public IEnumerable<Operation<T>> Sequence { get; private set; }
public T Result { get; private set; }
public void Init(IEnumerable<Operation<T>> seq) {
Sequence = seq;
Result = default(T);
}
public override void Process(Stack<IEnumerator<Operation<T>>> stack) {
stack.Push(Sequence.GetEnumerator());
}
protected override void SetResult(T value) {
Result = value;
}
}
static T Exec<T> (this IEnumerable<Operation<T>> sequence) {
var stack = new Stack<IEnumerator<Operation<T>>>();
var $call = new Call<T> { Sequence = sequence };
stack.Push(new Operation<T>[]{ $call }).GetEnumerator());
try {
while (stack.Count > 0) {
if (stack.Peek().MoveNext())
stack.Peek().Current.Process(stack);
else if (stack.Count > 1)
throw new InvalidOperationException("Функция не вернула никакого значения");
}
return $call.Result;
} catch (Exception ex) {
Exception ex1 = null;
while (stack.Count > 0) {
try { Operation<T>.DisposeFrame(stack.Pop()); }
catch (Exception ex2) { ex1 = ex2; }
}
/* Используется такой странный способ - чтобы по возможности сохранить место появления исходного исключения.
К сожалению, без рекурсии это невозможно в общем случае - но сохранить хотя бы верхний уровень надо попытаться. */
if (ex1 != null)
throw ex1;
else
throw;
}
}
static IEnumerable<Operation<int>> RecFunc(int k) {
var $call = new Call<int>();
int s=1;
for(int i=0;i<k;i++) {
/* Здесь я нагло использую тот факт, что до первого вызова MoveNext() функция-генератор не получает управления */
$call.Init(RecFunc(i));
yield return $call;
s+= $call.Result;
$call.Init(RecFunc(i / 2));
yield return $call;
s+= $call.Result;
}
return new Result<T> { Value = s };
}
static async Task<int> RecFunc(int k) {
int s=1;
for(int i=0;i<k;i++) {
await Task.Yield();
s+= await RecFunc(i);
s+= await RecFunc(i / 2);
}
return s;
}
class SequencedSynchronizationContext : SynchronizationContext {
private SendOrPostCallback callback;
private object state;
public override void Post(SendOrPostCallback d, Object state) {
// Сюда мы будем попадать при каждом await Task.Yield()
if (this.callback != null) throw new NotSupportedException();
this.callback = callback;
this.state = state;
}
// Здесь надо бы перегрузить остальные методы, чтобы заставить их кидать NotSupportedException - но мне лень.
public void Run() {
var old = SynchronizationContext.Current;
SetSynchronizationContext(this);
try {
while (callback != null) {
var cb = callback;
callback = null;
cb(state);
}
} finally {
SetSynchronizationContext(old);
}
}
}
static int ExecRunFunc(int k) {
var ctx = new SequencedSynchronizationContext();
int result = 0;
ctx.Post(async () => result = await RecFunc(k));
ctx.Run();
return result;
}
static async void CallFunc() {
int a=await RecFunc(4);
Console.WriteLine(a);
}
static void Main(string[] args) {
Task t=Task.Run(new Action(CallFunc));
t.Wait();
}
Console.WriteLine(RecFunc(4).Result) — не пробовали?)Wait() и Return именно что ждут результатов задачи, которая, возможно, запущена в другом потоке
Проблема в другом. При использование этого метода не появляется никаких преимуществ перед обычной рекурсией.
А в новом C++/CLI обещают аж автоматические конечные автоматы для использования в любых целях.
void QuickSort(T array[],int from,int to){
if(to-from<=1) return;
int mid=Split(array,from,to);
QuickSort(array,from,mid);
QuickSort(array,mid+1,to);
}
-- Генерируем функцию QuickSort. Принимает вектор аргументов. Все
-- функции, из которых она состоит, также принимают вектор X = {array, from, to}.
-- В реальном коде его можно было распаковать в аргументы функции.
local QuickSort = getF({
--Условие остановки рекурсии и значение при остановке
check = function(X) return X[3] - X[2] <= 1 end,
default = function() end,
-- Преобразования аргументов для рекурсивных вызовов
pre = {
function(X) -- Для первого рекурсивного вызова
local mid = Split(X[1], X[2], X[3])
return {X[1], X[2], mid}
end,
function(X) -- Для второго рекурсивного вызова
local mid = Split(X[1], X[2], X[3])
return {X[1], mid+1, X[3]}
end,
},
-- Преобразование результата после рекурсивных вызовов
post = function() end,
});
-- Вызов
QuickSort({array, from, to});
void QuickSort(T array[],int from,int to){
var stack=null;
while(to-from>1 || stack!=null){
if(to-from>1){
int mid=Split(array,from,to);
stack=[mid+1,to,stack];
to=mid;
}else{
(from,to,stack)=stack;
}
}
}
-- Итерация 2: возвращаем N-1 предыдущих результатов из функции
function fib2(x)
if x < 2 then return -x, nil end;
local x1 = fib2(x-1);
local x2 = fib2(x-2);
return x1 + x2, x1;
end
-- Итерация 3: используем все результаты, возвращаемые предыдущим вызовом
function fib3(x)
if x < 2 then return -x, nil end;
local x1, p1 = fib3(x-1);-- p1 == x2 по определению
local x2, p2 = fib3(x-2);-- p2 == x3 по определению
return x1 + x2, x1;
end
-- Итерация 4: переименовываем результаты, чтобы увидеть истину
function fib4(x)
if x < 2 then return -x, nil end;
local x1, x2 = fib4(x-1);
local x2, x3 = fib4(x-2);
return x1 + x2, x1;
end
-- Итерация 5: избавляемся от всех рекурсивных вызовов, кроме одного
function fib5(x)
if x < 2 then return -x, nil end;
local x1, x2 = fib5(x-1);
return x1 + x2, x1;
end
-- Итерация 6: вставляем защиту в начале итераций, пока не вычислим
-- N (в данном случае, 2) значений, необходимых для рекурсии. Чтобы
-- сохранить внешнюю сигнатуру, оборачиваем преобразованную функцию
-- в функцию-хелпер
function fib6(x)
local function f(x, stop)
if stop then return -x, nil end;
if x < 2 then return -x, f(x-1, true) end;
local x1, x2 = f(x-1, stop);
return x1 + x2, x1;
end
return f(x, false);
end
local function genF(t)
local function none() end
local check = t.check or none
local default = t.default or none
local pre = t.pre or {}
local post = t.post or none
local function f(...)
-- Упаковываем все аргументы, принятые f в массив, иначе с
-- ними работать нельзя
local args = {...}
-- unpack делает то же самое, что сделало бы
-- return args[1], args[1], ..., args[#args]
-- но только для произвольного количества элементов.
-- Таким образом, во все функции передаются все значения,
-- которые приняла f
if check(unpack(args)) then return default(unpack(args)) end
local results = {}
for _, p in ipairs(pre) do
results[#results + 1] = f(p(unpack(args)))
end
return post(unpack(results))
end
return f;
end
и увеличившуюся на 6 разрядов точность представления числаТочность не может вырасти магически. Если эти разряды появились из ниоткуда — значит, в них мусор!
4.0000
4.2500
-7.5147
-130.7314
217.9809
114.1815
104.3214
100.9882
100.3300
100.0778
100.0262
100.0062
100.0021
100.0005
100.0002
100.0000
from fractions import Fraction
def f(y, z):
return 108 - (815 - 1500/z)/y
X = [Fraction(4), Fraction(4.25)]
for x in xrange(2, 31):
print float(X[0])
X = [
X[1],
f(X[1], X[0])
]
import Data.Ratio
main :: IO ()
main = print $ xs !! 30
where xs = x0 : x1 : zipWith f (tail xs) xs
x0 = 4
x1 = toRational 4.25
f y z = 108 - (815 - 1500 / z) / y> WITH RECURSIVE t(n, y, z) AS (
VALUES (1, 4.25::numeric(160,150), 4::numeric(160,150))
UNION ALL
SELECT n+1, (108 - (815.0 - 1500.0/z) / y)::numeric(160,150), y FROM t WHERE n < 100
)
SELECT n, trunc(z, 30) FROM t;
from fractions import Fraction
CACHE = {}
def func(y, z):
return 108 - Fraction(815 - Fraction(1500, z), y)
def x(i):
if not i in CACHE:
if i == 0:
result = 4
elif i == 1:
result = Fraction('4.25')
else:
result = func(x(i - 1), x(i - 2))
CACHE[i] = result
return CACHE[i]
def main():
for i in range(100):
print(i, float(x(i)))
if __name__ == '__main__':
main()
(0, 4.0)
(1, 4.25)
(2, 4.470588235294118)
(3, 4.644736842105263)
(4, 4.770538243626063)
(5, 4.855700712589074)
...
(94, 5.0)
(95, 5.0)
(96, 5.0)
(97, 5.0)
(98, 5.0)
(99, 5.0)
f = lambda y, z: (108 - (815 - 1500 / z) / y)
@functools.lru_cache()
def xer(i):
if i == 0: return 4.
if i == 1: return 4.25
return f(xer(i-1), xer(i-2))
0 4.0
1 4.25
2 4.470588235294116
3 4.6447368421052175
4 4.770538243625083
5 4.855700712568563
6 4.91084749866063
7 4.945537395530508
8 4.966962408040999
9 4.980042204293014
10 4.987909232795786
11 4.991362641314552
12 4.967455095552268
13 4.42969049830883
14 -7.817236578459315
@functools.lru_cache()
def xer(i):
if i == 0: return Decimal(4)
if i == 1: return Decimal('4.25')
return f(xer(i-1), xer(i-2))
0 4
1 4.25
2 4.4705882352941176470588235
3 4.6447368421052631578947362
4 4.7705382436260623229461618
5 4.8557007125890736342039857
6 4.9108474990827932004342938
7 4.9455374041239167246519529
8 4.9669625817627005962571288
9 4.9800457013556311118526582
10 4.9879794484783912679439415
11 4.9927702880620482067468253
12 4.9956558915062356478184985
13 4.9973912683733697540253088
14 4.9984339437852482376781601
15 4.9990600687785413938424188
16 4.9994358732880376990501184
17 4.9996602467866575821700634
18 4.9997713526716167817979714
19 4.9993671517118171375788238
20 4.9897059157620938291040004
21 4.7951151851630947311130380
22 0.7281074924258006736651754
23 -581.7081261405031229400219627
24 105.8595186892360167901632650
Рекуррентное соотношение Мюллера: проблемы с округлением чисел с плавающей точкой