PROLOG:总结系列程序

时间:2015-06-18 05:24:58

标签: prolog

我一直在努力解决我在书中发现的这个问题,但我无法理解它在脑海中。问题要求我使用series(N, Total)进行此程序; 1 + 1/2 + 1/3 + 1/4 + ... + 1 /(N-1)。任何帮助让我明白这个问题将不胜感激!非常感谢!

3 个答案:

答案 0 :(得分:2)

我不明白你遇到了什么问题。

解决此问题的一种简单方法是定义递归谓词。

您可以使用is/2rdiv/2处理小数。

这将从1/1到1 /(B-1)总和:

series(B, B, 0).
series(A, B, Total) :-
    A < B,
    succ(A, A1),
    series(A1, B, Total1),
    Total is rdiv(1, A) + Total1.

如何阅读:

  • 从B到B(不包括)的总和(1 / i)是0
  • 从A到B的总和(1 / i),其中A <1。 B,是1 / A加从A + 1到B 的总和(递归)。请注意谓词的顺序,它们会稍微重新排列,因为is/2需要将所有参数都接地。

示例:我们使用它计算总和1 / i为i = 1,...,3 = 1/1 + 1/2 + 1/3 = 11/6:

?- series(1,4,X).
X = 11 rdiv 6 .

答案 1 :(得分:1)

使用 foldl/4可以模仿到目前为止提供的答案。

@mescalinum did in previous answer一样,我不会使用浮点数,而是arbitrary-precision rational numbers。 与浮点数不同,有理数的加法是可交换和关联的。因此,在总结有理数时,任何添加顺序都会得到相同的结果。

我建议将lambdas与两个元谓词init1/3reduce/3一起使用,如下所示:

seriesR(N,Val) :-
   init1(\I^E^(E is rdiv(1,I)),N-1,Rs),
   reduce(\X^Y^XY^(XY is X+Y),Rs,Val).

示例查询:

?- seriesR(4,Val).
Val = 11 rdiv 6.

要表明reduce/3可以帮助加快总结 - 即使它有一些内部开销 - 让我们运行seriesR/2和{{1}某些 BIG series/3

的头对头
?- time((series(10000,_),false)).
% 40,001 inferences, 2.805 CPU in 2.804 seconds (100% CPU, 14259 Lips)
false.

?- time((seriesR(10000,_),false)).
% 180,019 inferences, 0.060 CPU in 0.060 seconds (100% CPU, 3014245 Lips)
false.

修改

让我们深入挖掘一下!

我们使用的implementation lambda expressions会产生可衡量的效果成本。一旦进行适当的优化,这些成本将来可能会消失。

目前,让我们量化这些成本......

N

我们得到num_num_sum(X,Y,Sum) :- Sum is X+Y. r_reciprocal(X,Y) :- Y is rdiv(1,X). seriesR2(N,Val) :- init1(r_reciprocal,N-1,Rs), reduce(num_num_sum,Rs,Val). seriesR/2,我们得到:

seriesR2/2

在这个例子中,使用lambda表达式会产生大约3倍的最坏情况开销。重要的是?也许......但是比使用?- time((between(1,10000,_),seriesR(10,V),false)). % 1,750,001 inferences, 0.389 CPU in 0.389 seconds (100% CPU, 4500790 Lips) false. ?- time((between(1,10000,_),seriesR2(10,V),false)). % 820,001 inferences, 0.137 CPU in 0.137 seconds (100% CPU, 5999216 Lips) false. 代替foldl少了几个数量级!

答案 2 :(得分:0)

据我了解,你需要一个评估这个函数的谓词:

sum 1/x over x = 1..n

在过程语言中,你会写出类似迭代解决方案的东西:

static double iterative_harmonic_number( int n )
{
  if ( n < 1 ) throw new ArgumentOutOfRangeException("n");

  double r = 0.0 ;

  while ( n > 0 )
  {
    r += 1.0 / n-- ;
  }

  return r ;
}

或者这个递归解决方案:

static double recursive_harmonic_number( int n )
{
  if ( n < 1 ) throw new ArgumentOutOfRangeException("n");

  double h = (1.0 / n) ;
  if ( n > 1 ) { h += recursive_harmonic_number(n-1) ; } ;

  return h ;

}

两者在1..10中产生相同的结果:

         iterative       recursive
         --------------- ---
f(  1 ): 1                1
f(  2 ): 1.5              1.5
f(  3 ): 1.83333333333333 1.83333333333333
f(  4 ): 2.08333333333333 2.08333333333333
f(  5 ): 2.28333333333333 2.28333333333333
f(  6 ): 2.45             2.45
f(  7 ): 2.59285714285714 2.59285714285714
f(  8 ): 2.71785714285714 2.71785714285714
f(  9 ): 2.82896825396825 2.82896825396825
f( 10 ): 2.92896825396825 2.92896825396825

Prolog非常适合递归解决方案,所以让我们来看看。

  • 1个终止递归(n = 1)和
  • 的特殊情况
  • 一般情况(n> 1)。

我们可以在Prolog中直接表达:

f( 1 , 1.0 ) .     % the terminating special case
f( N , R   ) :-    % the general case:
  N > 1 ,          % - N must be positive (and greater than 1),
  N1 is N-1 ,      % - compute N-1
  f(N1,T) ,        % - recursively evaluate it
  R is T + 1.0 / N % - add that result to the current N's reciprocal
  .                % Easy!