【问题标题】:Rule to calculate power of a number when the exponent is Negative in Prolog?Prolog中指数为负时计算数字幂的规则?
【发布时间】:2012-01-04 16:07:43
【问题描述】:

我有一个幂函数pow,它试图计算B 的值的E 的幂。到目前为止,我处理的案件-
1.指数为0
2. 指数非零

pow(B,0,1).
pow(B,E,Result):-   E2 is E - 1,
                    pow(B,E2,Result2),
                    Result is B*Result2.

如何添加幂函数可以处理负指数的另一种情况?

【问题讨论】:

    标签: prolog exponentiation


    【解决方案1】:

    首先,应该考虑如何定义00。正式地说它是不确定的。它可以是 0 也可以是 1。正如 Wolfram 的 Mathworld 在其 article on powersarticle on zero 中所说:

    00(零的零次方)本身是未定义的。由于 a0 始终为 1,因此 00 应等于相互矛盾的事实,因此该数量缺乏明确定义的含义1,但 0a 始终为 0(对于 a > 0),所以 0a 应该等于 0。 00 的定义选择通常被定义为不确定的,尽管定义 00 = 1 允许简单地表达一些公式(Knuth 1992;Knuth 1997,第 57 页)。

    所以你应该首先选择如何定义0的特殊情况0:是0吗?是1吗?是未定义的吗?

    我选择将其视为未定义。

    话虽如此,您可以将正指数视为重复乘法(例如 103 是 10*10*10 或 1,000),您可以将负指数视为表明重复除法(例如,10-3 为 (((1/10)/10)/10),或 0.001)。我的倾向,部分是因为我喜欢这种方法的对称性,部分是为了避免削减(因为削减通常是你没有正确定义解决方案的信号),会是这样的:

    % -----------------------------
    % The external/public predicate
    % -----------------------------
    pow( 0 , 0 , _ ) :- ! , fail .
    pow( X , N , R ) :-
      pow( X , N , 1 , R )
      .
    
    % -----------------------------------
    % the tail-recursive worker predicate
    % -----------------------------------
    pow( _ , 0 , R , R  ).
    pow( X , N , T , R  ) :-
      N > 0 ,
      T1 is T * X ,
      N1 is N-1   ,
      pow( X , N1 , T1 , R )
      .
    pow( _ , 0 , R , R  ) :-
      N < 0 ,
      T1 is T / X ,
      N1 is N+1   ,
      pow( X , N1 , T1 , R )
      .
    

    正如其他人所指出的,另一种方法是将正指数定义为表示重复乘法,将负指数定义为表示正指数的倒数,因此 103 是 10*10 *10 或 1,000,10-3 是 1/(103),或 1/1,000 或 0.001。要使用这个定义,我会再次避免削减并做这样的事情:

    % -----------------------------
    % the external/public predicate
    % -----------------------------
    pow( 0 , 0 , _ ) :-  % 0^0 is indeterminate. Is it 1? Is it 0? Could be either.
      ! ,
      fail
      .
    pow( X , N , R ) :-
      N > 0 ,
      pow( X , N , 1 , R )
      .
    pow( X , N , R ) :-
      N < 0 ,
      N1 = - N ,
      pow( X , N1 , 1 , R1 ) ,
      R is 1 / R1
      .
    
    % -----------------------------------
    % The tail-recursive worker predicate
    % -----------------------------------
    pow( _ , 0 , R , R  ).
    pow( X , N , T , R  ) :-
      N > 0 ,
      T1 is T * X ,
      N1 is N-1   ,
      pow( X , N1 , T1 , R )
      .
    

    【讨论】:

    • 非常感谢您花时间和精力写下如此清晰而全面的答案。另外,我个人认为 0^0 是未定义的,尽管我知道有不同的方式来看待它。对于任何偶然读到这篇文章的人 - askamathematician.com/2010/12/…
    【解决方案2】:

    不要忘记a^(2b) = (a^b)^2x^2 = x*x。可以从顶级“UI”谓词以非尾方式调用带有累加器的尾递归工作谓词。这样您就不必为负幂实现工作谓词,而是将其重用于正幂,并在顶级谓词中更改其结果(我看到这已经被建议):

    pow(B, E, R):- E<0 -> ... ; E=:=0 -> ... ; E>0 -> ... .
    

    【讨论】:

      【解决方案3】:

      首先,您的第二个子句是非尾递归的(您可以阅读主题here)。这意味着最终,您将在运行时耗尽调用堆栈内存。 一件好事是使用累加器使其尾递归。您可以按如下方式实现:

      % we add an accumulator to poW/3, making it pow/4.
      pow(B, E, Result) :- pow(B, E, 1, Result).
      
      % when we hit 0, our accumulator holds B^E so we unify it with result.
      pow(_, 0, Accu, Accu) :- !.
      
      % at each step, we multiply our accumulator by B
      pow(B, E, Accu, Result) :-
          NewE is E - 1,
          NewAccu is Accu * B,
          pow(B, NewE, NewAccu, Result).
      

      然后,您可以通过将此子句添加到其他子句来简单地处理否定情况(它只是告诉 prolog 负幂是正幂的倒数):

      pow(B, E, Result) :-
          E < 0,
          PositiveE is - E,
          pow(B, PositiveE, 1, R),
          !,
          Result is 1 / R.
      

      请注意,您可以直接使用您的代码:

      pow(B, E, Result) :-
          E < 0,
          PositiveE is - E,
          pow(B, PositiveE, R),
          !,
          Result is 1 / R.
      

      另外,我们现在引入了一个非常红色的切割(如有必要,请参阅here 了解红色切割的含义)。所以最好通过这个修改变成绿色切割:

      pow(B, E, Result) :-
          E < 0,
          PositiveE is - E,
          pow(B, PositiveE, 1, R),
          !,
          Result is 1 / R.
      
      % we add an accumulator to poW/3, making it pow/4.
      pow(B, E, Result) :-
          E >= 0, %************* HERE *****************
          pow(B, E, 1, Result).
      
      % when we hit 0, our accumulator holds B^E so we unify it with result.
      pow(_, 0, Accu, Accu) :- !.
      
      % at each step, we multiply our accumulator by B
      pow(B, E, Accu, Result) :-
          NewE is E - 1,
          NewAccu is Accu * B,
          pow(B, NewE, NewAccu, Result).
      

      【讨论】:

      • 这非常有用。谢谢!! (特别是对于非尾递归和红切概念..)
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-09-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多