2012-02-07 7 views
3

私は1000より下の3と5の正の倍数の合計を求めようとしています。5の倍数の合計から3の倍数を削除するはずの部分を追加した後、gprologは、クエリ?- sigma(1000,N).初心者 - 3と5の倍数を加算する

問題が明らかにsigma5にあるが、私はかなりそれを見つけることができないため、「いいえ」を吐き続けます:

sigma(Num, Result) :- sigma3(Num, 3, Result3), 
         sigma5(Num, 5, Result5), 
         Result is Result3 + Result5. 

sigma3(Num, A, Result) :- A < Num, 
          Ax is A+3, 
          sigma3(Num, Ax, ResultX), 
          Result is ResultX + A. 
sigma3(Num, A, Result) :- A >= Num, 
          Result is 0. 

sigma5(Num, A, Result) :- A < Num, 
          mod3 is A mod 3, 
          0 \= mod3, 
          Ax is A+5, 
          sigma5(Num, Ax, ResultX), 
          Result is ResultX + A. 
sigma5(Num, A, Result) :- A < Num, 
          mod3 is A mod 3, 
          0 == mod3, 
          Ax is A+5, 
          sigma5(Num, Ax, ResultX), 
          Result is ResultX. 
sigma5(Num, A, Result) :- A >= Num, 
          Result is 0. 

私のコードの問題は何ですか?

答えて

2

Prologはこれまで決して算術機能として普及していません。

これは、不適切な評価をすることなくシンボリック処理のために '用語構成子'を表現する必要があるためです。実際の算術が必要な場合は、結果として 'スペース'(変数)を明示的に割り当てる必要があります式を「落とす」。これはむしろ冗長で不快なコードにつながります。

しかし、GPLogやSWI-Prologで利用可能なCLP(FD)のような一般的な拡張を使用すると、他の言語ではすぐに利用できない、より良い結果が得られます。通常の整数領域のクロージャ算術演算。例えば、SWI-PrologのCLP(FD)ライブラリーから、「双方向」はとにかく

n_factorial(0, 1). 
n_factorial(N, F) :- N #> 0, N1 #= N - 1, F #= N * F1, n_factorial(N1, F1). 

?- n_factorial(X, 3628800). 
X = 10 . 

を階乗、ここにあなたが試みたものと同様の元の問題への単純な志向のソリューションは、ですが、アキュムレータを使用してを使用して結果を計算します。このシンプルなトリックでは、テール再帰プロシージャを記述することができます。

sigma(Num, Result) :- 
    sigma(1, Num, 0, Result). 

sigma(N, M, Acc, Tot) :- 
    ( N < M, !, 
     ( (0 is N mod 3 ; 0 is N mod 5) 
     -> Sum is Acc + N 
     ; Sum is Acc 
     ), 
     N1 is N + 1, 
     sigma(N1, M, Sum, Tot) 
    ; Tot is Acc 
    ). 

テスト:

?- sigma(1000, X). 
X = 233168 . 
+1

+1:SWIの 'library(clpfd)'とYAPは、上記のようなプログラムをコンパイルします。伝統的な(is)/ 2-modesは、naive(is)/ 2に匹敵する効率を持っています! – false

1
mod3 is A mod 3, 

(他のすべてのmod3も)は変数であるため、Mod3である必要があります。その修正と、プログラムが正しく実行さ (少なくともN用= 1000)

ところで、ここでは、(高階述語を使用して)私のソリューションです:

sum(S):- 
    findall(X,between(1,999,X),L),  % create a list with all numbers between 1 and 999 
    include(div(3),L,L3),    % get the numbers of list L which are divisible by 3 
    include(div(5),L,L5),    % get the numbers of list L which are divisible by 5 
    append(L3,L5,LF),     % merge the two lists 
    list_to_set(LF,SF),     % eliminate double elements 
    sumlist(SF,S).      % find the sum of the members of the list 

div(N,M):- 
    0 is M mod N. 

それは当然の少ない効率的なのですが、入力されています目立つ差を作るには小さすぎる

+0

痛い、これがなければなりません私は小文字で変数名を書いたので、今日は10回目に問題があります(私はそれに慣れています...)。ありがとう。 gprologはfindallを知っていますか? – Cubic

+0

'findall/3'は標準で定義されています。 SWI-Prologを使用してみてください。ただし、より完全である場合は、SWI-Prologを使用してみてください。 –

+0

@Cubicそれは普通だと思うxd私はgpr​​ologでfindall/3を試してみたが、Daniel Lyonsが言ったようにかなり基本的だと思う。とにかく、この場合は本当に必須ではありません。 [1,2,3、... 999]の記述を避けるために使用されています(これは単純な再帰でも可能です)。それにもかかわらず、私はfindall/3の仕組みを学ぶことを提案します。 –

4

整数が含まれているので、finite domain constraintsを使用することを検討してください。たとえば、SWI-Prologの場合:

?- use_module(library(clpfd)). 
true. 

?- findall(N, (N mod 3 #= 0 #\/ N mod 5 #= 0, N in 0..999, indomain(N)), Ns), 
    sum(Ns, #=, Sum). 
Ns = [0, 3, 5, 6, 9, 10, 12, 15, 18|...], 
Sum = 233168. 
+0

私はSWI Prologを使用していません。 – Cubic

+1

あなたの損失;-)。しかし、すべての深刻さで:すべての深刻なPrologシステムはCLP(FD)を提供しますが、私はSWIを特定の例として使用していました。 – mat

+1

@Cubic:制約なしでは、最初の0.999のindomain(N)を '(0,999、N)'の間で置き換えます。 'findall(N、(0,999、Nの間)、\ – false

0

このすべては、私にとって非常に複雑なようです。

sum_of(L , S) :- 
    L > 0 , 
    sum_of(0 , L , 0 , S) 
    . 

sum_of(X , X , S , S) . % if we hit the upper bound, we're done. 
sum_of(X , L , T , S) :- % if not, look at it. 
    X < L ,     % - backtracking once we succeeded. 
    add_mult35(X , T , T1) , % - add any multiple of 3 or 5 to the accumulator 
    X1 is X + 1 ,    % - next X 
    sum_of(X1 , L , T1 , S) % - recurse 
    . 

add_mult35(X , T , T) :- % no-op if X is 
    X mod 3 =\= 0 ,   % - not a multiple of 3, and 
    X mod 5 =\= 0 ,   % - not a multiple of 5 
    !.      % 
add_mult35(X , T , T1) :- % otherwise, 
    T1 is T + X    % increment the accumulator by X 
    . 

これは、それよりもさらに簡潔になる可能性があります。

別に私のコードから、おそらく(それはもはやそれ自身にかなりの偉業である私のC液、より実際のです)非常に恐ろしいこと、

ANSI C:

int sum_multiples_of_three_and_five(int lower_bound , int upper_bound) 
{ 
    int sum = 0 ; 

    for (int i = lower_bound ; i <= upper_bound ; ++i) 
    { 
    if (0 == i % 3 || 0 == i % 5) 
    { 
     sum += i ; 
    } 
    } 

    return sum ; 
}