1

I have problem, because I want to generate permutations of a list (in prolog), which contains n zeros and 24 - n ones without repetitions. I've tried:findall(L, permutation(L,P), Bag) and then sort it to remove repetitions, but it causes stack overflow. Anyone has an efficient way to do this?

lolos
  • 37
  • 1
  • 5
  • 1
    There's a mathematical formula to compute the number of permutations... it may help understand the stack overflow... – Paulo Moura Apr 07 '20 at 19:09
  • 1
    I know there is 24! possible permutations and thats why it causes stack overflow, but 90% of them are duplicated for only two different values in a list. So I'm hoping there is a way to do this – lolos Apr 07 '20 at 19:12
  • Select n random numbers between 0 and 23 in ascending order. These give you the indexes of the zeroes and all the configurations are different. The key is generating these list of indexes. – David Tonhofer Apr 07 '20 at 20:24
  • Is there any chance you could show us your code in your question? – Enigmativity Apr 08 '20 at 12:50

3 Answers3

1

Instead of thinking about lists, think about binary numbers. The list will have a length of 24 elements. If all those elements are 1's we have:

?- X is 0b111111111111111111111111.
X = 16777215.

The de fact standard predicate between/3 can be used to generate numbers in the interval [0, 16777215]:

?- between(0, 16777215, N).
N = 0 ;
N = 1 ;
N = 2 ;
...

Only some of these numbers satisfy your condition. Thus, you will need to filter/test them and then convert the numbers that pass into a list representation of its binary equivalent.

Paulo Moura
  • 18,373
  • 3
  • 23
  • 33
0

Select n random numbers between 0 and 23 in ascending order. These integers give you the indexes of the zeroes and all the configurations are different. The key is generating these list of indexes.

%
% We need N monotonically increasing integer numbers (to be used
% as indexes) from [From,To].
%

need_indexes(N,From,To,Sol) :-
   N>0,
   !,
   Delta is To-From+1,
   N=<Delta,               % Still have a chance to generate them all
   N_less is N-1,
   From_plus is From+1,
   (
      % Case 1: "From" is selected into the collection of index values
      (need_indexes(N_less,From_plus,To,SubSol),Sol=[From|SubSol])
      ;
      % Case 2: "From" is not selected, which is only possible if N<Delta
      (N<Delta -> need_indexes(N,From_plus,To,Sol))      
   ).

need_indexes(0,_,_,[]). 

Now we can get list of indexes picked from the available possible indexes.

For example:

Give me 5 indexes from 0 to 23 (inclusive):

?- need_indexes(5,0,23,Collected).
Collected = [0, 1, 2, 3, 4] ;
Collected = [0, 1, 2, 3, 5] ;
Collected = [0, 1, 2, 3, 6] ;
Collected = [0, 1, 2, 3, 7] ;
...

Give them all:

?- findall(Collected,need_indexes(5,0,23,Collected),L),length(L,LL).
L = [[0, 1, 2, 3, 4], [0, 1, 2, 3, 5], [0, 1, 2, 3, 6], [0, 1, 2, 3, 7], [0, 1, 2, 3|...], [0, 1, 2|...], [0, 1|...], [0|...], [...|...]|...],
LL = 42504.

We are expecting: (24! / ((24-5)! * 5!)) solutions.

Indeed:

?- L is 20*21*22*23*24 / (1*2*3*4*5).
L = 42504.

Now the only problem is transforming every solution like [0, 1, 2, 3, 4] into a string of 0 and 1. This is left as an exercise!

David Tonhofer
  • 14,559
  • 5
  • 55
  • 51
0

Here is an even simpler answer to generate strings directly. Very direct.

need_list(ZeroCount,OneCount,Sol) :- 
   length(Zs,ZeroCount),maplist([X]>>(X='0'),Zs),
   length(Os,OneCount),maplist([X]>>(X='1'),Os),
   compose(Zs,Os,Sol).

compose([Z|Zs],[O|Os],[Z|More]) :- compose(Zs,[O|Os],More).
compose([Z|Zs],[O|Os],[O|More]) :- compose([Z|Zs],Os,More).
compose([],[O|Os],[O|More])     :- !,compose([],Os,More).
compose([Z|Zs],[],[Z|More])     :- !,compose(Zs,[],More).
compose([],[],[]).

rt(ZeroCount,Sol) :- 
   ZeroCount >= 0,
   ZeroCount =< 24,
   OneCount is 24-ZeroCount,   
   need_list(ZeroCount,OneCount,SolList),
   atom_chars(Sol,SolList).
?- rt(20,Sol).
Sol = '000000000000000000001111' ;
Sol = '000000000000000000010111' ;
Sol = '000000000000000000011011' ;
Sol = '000000000000000000011101' ;
Sol = '000000000000000000011110' ;
Sol = '000000000000000000100111' ;
Sol = '000000000000000000101011' ;
Sol = '000000000000000000101101' ;
Sol = '000000000000000000101110' ;
Sol = '000000000000000000110011' ;
Sol = '000000000000000000110101' ;
....
?- findall(Collected,rt(5,Collected),L),length(L,LL).   
L = ['000001111111111111111111', '000010111111111111111111', '000011011111111111111111', '000011101111111111111111', '000011110111111111111111', '000011111011111111111111', '000011111101111111111111', '000011111110111111111111', '000011111111011111111111'|...],
LL = 42504.
David Tonhofer
  • 14,559
  • 5
  • 55
  • 51