Нарциссическое число в Prolog

#prolog

#пролог

Вопрос:

привет всем, я снова здесь, на этой неделе я получил это домашнее задание:

Я должен найти все числа между 10 и 10000 , которые обладают свойством, подобным следующему примеру:

 89 = 8^1   9^2
2427 = 2^1   4^2   2^3   7^4 = 2   16   8   2401
  

Я реализовал это в Haskell, и это работает просто отлично (я думаю) и возвращает список, подобный этому:

 [89,135,175,518,598,1306,1676,2427]
  

и затем я попытался записать его в Prolog (по мере необходимости тоже) следующим образом:

 num(0).
num(1).
num(2).
num(3).
num(4).
num(5).
num(6).
num(7).
num(8).
num(9).

allNarc(X):- num(A),num(B),num(C),num(D),
             X = A*1000 B*100 C*10 D,Y = A**1 B**2 C**3 D**4,
             X =:= Y,X>10.
allNarc(X):- num(B),num(C),num(D),
             X = B*100 C*10 D,Y = B**1 C**2 D**3,
             X =:= Y,X>10.
allNarc(X):- num(C),num(D),
             X = C*10 D,Y = C**1 D**2,
             X =:= Y,X>10.
  

результат получается примерно таким:

 ?- allNarc(X).
X = 1*1000 3*100 0*10 6 ;
X = 1*1000 6*100 7*10 6 ;
X = 2*1000 4*100 2*10 7 ;
X = 0*100 4*10 3 ;    <- 43
X = 0*100 6*10 3 ;    <- 63
X = 1*100 3*10 5 ;
X = 1*100 7*10 5 ;
X = 5*100 1*10 8 ;
X = 5*100 9*10 8 ;
X = 8*10 9 ;
false.
  

очевидно, что 43 и 64 не должны принадлежать к этой группе, и результат просто уродлив, кто-нибудь может помочь мне получить результат, подобный результату в моей реализации на Haskell?

Ответ №1:

Два очка.

  1. когда вы говорите, X = вы объединяете левую и правую части = . Если вы хотите, чтобы X было числом, равным вычислению выражения, используйте X is <expression here> .
  2. вы хотите предотвратить появление нуля в первой позиции: num(A), A = 0, num(B), ...

После внесения этих изменений я получаю:

 ?- allNarc(X).
X = 1306 ? ;
X = 1676 ? ;
X = 2427 ? ;
X = 135 ? ;
X = 175 ? ;
X = 518 ? ;
X = 598 ? ;
X = 89 ? ;
no
  

Вы также могли бы использовать bagof для сбора значений. например, bagof(X,allNarc(X),Narcs). Narcs тогда это список ваших значений.

Комментарии:

1. эй, большое спасибо, я только что понял это сам. Я бы предпочел использовать setof для получения точного списка, подобного моей программе на haskell.

Ответ №2:

Вам нужно:

  1. Не допускайте, чтобы первые цифры были равны 0.
  2. Убедитесь, что X вычисляется до нужного вам значения. На данный момент вы используете термин unification, который не подходит, вам нужно использовать is ключевое слово.

Смотрите:

 num(0).
num(1).
num(2).
num(3).
num(4).
num(5).
num(6).
num(7).
num(8).
num(9).
posnum(A) :- num(A), A = 0.

allNarc(X):- posnum(A),num(B),num(C),num(D),
             X is A*1000 B*100 C*10 D,Y is A**1 B**2 C**3 D**4,
             X =:= Y,X>10.
allNarc(X):- posnum(B),num(C),num(D),
             X is B*100 C*10 D,Y is B**1 C**2 D**3,
             X =:= Y,X>10.
allNarc(X):- posnum(C),num(D),
             X is C*10 D,Y is C**1 D**2,
             X =:= Y,X>10.
  

Обратите внимание, что затем вы можете найти все числа с помощью setof :

 ?- setof(X,allNarc(X),XL).
XL = [89, 135, 175, 518, 598, 1306, 1676, 2427].
  

Это гораздо удобнее, чем перечислять их по одному.

Комментарии:

1. привет, спасибо 🙂 Я обнаружил, что я должен предотвратить такие вещи, как 43 = 0^1 4^2 3^3 из моего результата, что привело к вашему объяснению, 1. кстати, я бы предпочел использовать setof B)

2. Исправлено! Прошло некоторое время с тех пор, как я делал какой-либо пролог, поэтому я забыл о setof :).