Как создать список только с двумя единицами и другими нулями заданной длины? - PullRequest
2 голосов
/ 16 февраля 2020

Я должен сгенерировать списки, состоящие из 2 '1 и других элементов' 0. Я попробовал следующий код, но он не работает:

count([], _, 0).
count([X|T], X, Y) :- count(T, X, Z), Y is 1+Z.
count([X1|T],X,Z):- X1\=X,count(T,X,Z).

two(X) :- count(X, 1, Counter), Counter =:= 2.

Запрос length(Vs, 4), Vs ins 0..1, two(Vs). ничего не дает.

Как правильно генерировать такие списки? Я ожидаю получить что-то вроде [1, 1, 0, 0], [1, 0, 1, 0] ... [0, 0, 1, 1].

Ответы [ 4 ]

4 голосов
/ 16 февраля 2020
twoones(Bs) :-
   Bs ins 0..1,
   sum(Bs, #=, 2).

| ?- length(Bs,4), twoones(Bs).
Bs = [_A,_B,_C,_D],
clpz:(_A+_B+_C+_D#=2),
clpz:(_A in 0..1),
clpz:(_B in 0..1),
clpz:(_C in 0..1),
clpz:(_D in 0..1) ? 
yes
| ?- length(Bs,4), twoones(Bs), labeling([], Bs).
Bs = [0,0,1,1] ? ;
Bs = [0,1,0,1] ? ;
Bs = [0,1,1,0] ? ...

Здесь я использую library(clpz), который является преемником library(clpfd). Для простых примеров, как это, нет большой разницы.

2 голосов
/ 17 февраля 2020

Грамматика - это вполне естественный способ задания шаблона списка:

two --> [1], one ; [0], two.
one --> [1], zeros ; [0], one.
zeros --> [] ; [0], zeros.

Пример вызова:

?- length(Xs, 3), phrase(two, Xs, []).
Xs = [1, 1, 0]
Yes (0.00s cpu, solution 1, maybe more)
Xs = [1, 0, 1]
Yes (0.00s cpu, solution 2, maybe more)
Xs = [0, 1, 1]
Yes (0.00s cpu, solution 3, maybe more)
No (0.00s cpu)
1 голос
/ 16 февраля 2020

Это отличные ответы.

Вот еще один. nth0 происходит от library(lists).

  • SWI Пролог nth0
  • SICStus nth0

  • Два 1 были заменены на a и b в целях иллюстрации.

  • findall(0, between(1, Zlen, _), Zlist) создает список 0 (Zlist) длины Zlen. См. between.
gimme(List,Len) :- Len >= 2,                    
                   Zlen is Len-2,
                   findall(0, between(1, Zlen, _), Zlist),
                   between(0, Zlen, Pos1),                 % we will insert 'a' at Pos1
                   Pos1n is Pos1+1,
                   between(Pos1n,Len,Pos2),                % we will insert 'b' at Pos2, always after 'a'
                   nth0(Pos1, Tlist, a, Zlist),            % Zlist -morph-> Tlist
                   nth0(Pos2, List, b, Tlist).             % Tlist -morph-> List                
?- gimme(L,2).
L = [a, b] ;
false.

?- gimme(L,3).
L = [a, b, 0] ;
L = [a, 0, b] ;
L = [0, a, b] ;
false.

?- gimme(L,4).
L = [a, b, 0, 0] ;
L = [a, 0, b, 0] ;
L = [a, 0, 0, b] ;
L = [0, a, b, 0] ;
L = [0, a, 0, b] ;
L = [0, 0, a, b] ;
false.

?- gimme(L,5).
L = [a, b, 0, 0, 0] ;
L = [a, 0, b, 0, 0] ;
L = [a, 0, 0, b, 0] ;
L = [a, 0, 0, 0, b] ;
L = [0, a, b, 0, 0] ;
L = [0, a, 0, b, 0] ;
L = [0, a, 0, 0, b] ;
L = [0, 0, a, b, 0] ;
L = [0, 0, a, 0, b] ;
L = [0, 0, 0, a, b] ;
1 голос
/ 16 февраля 2020

Я думаю, что вы делаете это слишком сложно здесь. Вместо использования clpfd здесь вы можете просто создать предикат, который генерирует такие списки.

Сначала мы можем создать предикат, объединяющий все списки, содержащие только 0 s:

all0([]).
all0([0|T]) :-
    all0(T).

затем мы можем создать предикат with1s(N, l), который будет "вставлять" N единицы в список, который он генерирует:

with1s(0, L) :-
    all0(L).
with1s(N, [H|T]) :-
    N > 0,
    ((H=1, N1 is N-1);
     (H=0, N1 = N)),
    with1s(N1, T).

Так, например, для списка из трех элементов мы получаем:

?- L = [_,_,_], with1s(2, L).
L = [1, 1, 0] ;
L = [1, 0, 1] ;
L = [0, 1, 1] ;
false.

Это, конечно, не работает двунаправленно, я оставляю это как упражнение для дальнейшего улучшения предиката.

...