4

This question might seem very much like the linked one below but it differs in a very special way; Mr.Wizard suggested I start a new post:

Partition a set into subsets of size $k$

What I want is to generate all the partitions of a set into subsets of size $k$ but incrementally, that is, one by one, so that I can generate one, process the partition in some way, and then continue with the next one in some loop. Ideally, the program will give the partitions in some sorted way, and if started with some partition, it would go on to give the next one, and then the next one, and so on. One could think of this as the NextKSizePartition function which given a partition finds the next one, in some kind of order ... if the program were to return them in lexicographic order, then that would be even better. Of course, the set of elements has to be a multiple of $k$. Note that a function like this is not available in Combinatorica.

The rationale for wanting this is that if you want, say, the partitions of the set of numbers from 1 to 16 into sets of size 2, you already get over a million of them ... if you want 18, over 10 million, and son on. So very soon you get into numbers that don’t fit in memory, but can still be processed sequentially. In what I look for, the higher up I check for certain properties, the better, even if it is just an increment in the set size by 2 or 4. In the world of combinatorial and graph theoretical conjectures, there is a fine threshold of when something might fail and when it is likely to stand ...

I hope the request is clear

EGME
  • 647
  • 3
  • 7
  • Oh, I come to the impression that do a completely different thing: I generate all subsets of lenght k. That's is apparently not what you want. I delete my post. – Henrik Schumacher Nov 21 '19 at 15:35
  • @HenrikSchumacher Thank you nevertheless. – EGME Nov 21 '19 at 15:36
  • I got to this late today, but I have a working proof of concept that I should be able to complete into something reusable and post tomorrow. – Mr.Wizard Nov 22 '19 at 03:29
  • What's wrong with say something like Subsets[Range@100, {4}, {10^6}] (giving the 10^6th subset of size 4 of the range [1,100]) - it pulls a given subset in a few hundred-thousandths of a second on a laptop... – ciao Nov 22 '19 at 23:52
  • @ciao You want the partitions of a Range@100 into sets of size 4 ... so you need to have the 25 sets of size 4 that give you a certain partition in some order. – EGME Nov 23 '19 at 06:31

1 Answers1

5

Here is my approach, building on Rojo's original from the linked quesiton. I use a primary function partition2 and two auxiliary functions f1 and f2.

I believe this is working but I have not tested it extensively yet.

Usage

  • partitions2 spits out an object that includes continuation information.
  • f1 extracts the partition for use.
  • f2 produces the next partition object.

Example:

p = partitions2[Range@6, 2]

f1 @ p

f2 @ p

{{1, 2}, {3, 4}, {5, 6}, 〈{3, 4, 5, 6}, 2, 2, 3〉, 〈{1, 2, 3, 4, 5, 6}, 2, 2, 5〉}

{{1, 2}, {3, 4}, {5, 6}}

{{1, 2}, {3, 5}, {4, 6}, 〈{3, 4, 5, 6}, 2, 3, 3〉, 〈{1, 2, 3, 4, 5, 6}, 2, 2, 5〉}

check against the original partitions function:

{a, b} = {12, 3};
p1 = partitions[Range@a, b];
p2 = f1 /@ NestList[f2, partitions2[Range@a, b], Length@p1 - 1];
p1 === p2
True

Code

Update: replaced Inactive/Activate with AngleBracket and added argument tests.

ClearAll[partitions2, f1, f2]

partitions2[list : {__}, l_Integer?Positive] := With[{ln = Length @ list}, If[ln == l, {list}, partitions2[list, l, 1, Binomial[ln - 1, l - 1]] ] /; Divisible[ln, l] ]

partitions2[list_, l_, n_, m_] /; n <= m := {#, ## & @@ partitions2[list ~Complement~ #, l], 〈list, l, n + 1, m〉} & @@ Subsets[list, {l}, {n}]

f1[{Longest[p__List], c___}] := {p}

f2[{p___, List, _List, 〈__, n, m_〉 /; n > m, cr___}] := f2 @ {p, {}, cr}

f2[{p___List, _, _, 〈c__〉, cr___}] := {p, ##, cr} & @@ partitions2[c]


Partition ranking

Here is a rank function to generate the continuation information from a given partition. At its core this uses RankKSubset from the old Combinatorica package; since loading that may cause conflicts I reproduce it here.

rank[p : {_}] := p

rank[p : {x_, y___List}] := Module[{u, l, ln, r}, u = Union @@ p; {l, ln} = Length /@ {x, u}; r = RankKSubset @@ ArrayComponents[{x, u}]; Join[{x}, rank[{y}], {〈u, l, r + 2, Binomial[ln - 1, l - 1]〉}] ]

(* Combinatorica function *) RankKSubset[{}, s_List] := 0;

RankKSubset[ss_List, s_List] := 0 /; Length[ss] === Length[s];

RankKSubset[ss_List, s_List] := Position[s, ss[[1]]][[1, 1]] - 1 /; Length[ss] === 1;

RankKSubset[ss_List, s_List] := Block[{n = Length[s], k = Length[ss], x = Position[s, ss[[1]]][[1, 1]], $RecursionLimit = ∞}, Binomial[n, k] - Binomial[n - x + 1, k] + RankKSubset[Rest[ss], Drop[s, x]]];

Testing:

rank[{{1, 2, 7}, {3, 8, 11}, {4, 6, 10}, {5, 9, 12}}]

% === Nest[f2, partitions2[Range@12, 3], 1325]

 {{1, 2, 7}, {3, 8, 11}, {4, 6, 10}, {5, 9, 12},
  〈{4, 5, 6, 9, 10, 12}, 3, 7, 10〉,
  〈{3, 4, 5, 6, 8, 9, 10, 11, 12}, 3, 22, 28〉,
  〈{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}, 3, 6, 55〉}

True

Mr.Wizard
  • 271,378
  • 34
  • 587
  • 1,371
  • Thank you!! ... it is a bit late today here but I will test this during the weekend ... – EGME Nov 22 '19 at 20:25
  • @EGME Can we start with the initial set elements sorted? If so I don't need to keep a separate list in the continuation information. – Mr.Wizard Nov 23 '19 at 14:55
  • Yes, this is perfectly acceptable ... hmm, I haven’t written your code into the computer yet ... I hope you don’t delete it ... I am trying to figure out how it works ... I have a technical question ... will this be of the kind that it will have a lot of information stored before it actually gets to the first complete partition (like in Rojo’s code) ...?? – EGME Nov 23 '19 at 14:58
  • @EGME (1) I won't delete it, but even if I did you can always look at past revisions by clicking the "edited ..." link below-center of the post body. (2) I'm not sure I understand what you mean, but if I do: no, this really does generate these one at a time, so e.g. you can do partitions2[Range@1000, 100] and it won't just grind away for eternity. – Mr.Wizard Nov 23 '19 at 15:02
  • Ok, excellent, then it seems it gets you one at a time ... I will get around to it ... I might need to ask you questions ... I am doing serious multitasking here ... hmm, I dont see the “edited” link in my pages ... anyway ... thank you ... your simplified code will probably be good to see ... – EGME Nov 23 '19 at 15:06
  • @EGME (1) Ask whatever you'd like, but I might be slow to respond. (2) The edited link should be there somewhere, if the post has been edited. I did make one revision so try refreshing the page. (3) The code wouldn't be simplified, rather the continuation information would be shorter; instead of {{1, 2}, {3, 4}, {5, 6}, 〈{3, 4, 5, 6}, 2, 2, 3〉, 〈{1, 2, 3, 4, 5, 6}, 2, 2, 5〉} we could just keep {{1, 2}, {3, 4}, {5, 6}, 〈2, 2, 3〉, 〈2, 2, 5〉} because we can generate e.g. {3, 4, 5, 6} from the preceding elements. This will be perhaps ~10% slower in computation but use less memory. – Mr.Wizard Nov 23 '19 at 15:17
  • Excellent, this seems to be working fine ... I will do some benchmarking ... and then I will put it to the test with a big job ... which the original function cannot handle due to memory constraints ... but this should be able to handle it given sufficient time ... thanks ... I still need to understand in detail your code ... I understand Rojo’s well already ... I might contact you again. – EGME Nov 24 '19 at 15:27
  • @EGME If you understand Rojo's method partitions2 should be pretty easy; I'm just asking for a single subset from Subsets using {n}. Parameters n and m keep track of the current subset number and final subset number respectively. f2 drops last sub-partitions (e.g. {3, 4} and {5, 6} in {{1, 2}, {3, 4}, {5, 6}}) from the result, and asks for the next one by passing the continuation information back to partitions2[c]. When we get to the end, i.e. n exceeds m, I need to drop one level of the continuation and an extra sub-partition; this is done with the first f2 line. – Mr.Wizard Nov 24 '19 at 15:51
  • I think I know what it is, but to make sure I don’t make a mistake, could you tell me what is a terminating condition in your continuation object ... with large jobs I cannot use the nesting functions ... I will use this in a loop which does a lot of processing on each partition before going on to the next one ... thanks, no rush! – EGME Nov 24 '19 at 15:52
  • @EGME I don't think I provided a termination condition; that's a good point! When the last continuation set is dropped f2 will no longer match, and you'll get something like f2[f2[{{}}]]. I think it would be better to Throw a completed message in that case. – Mr.Wizard Nov 24 '19 at 15:56
  • Well, I can detect the end anyway ... and the storage for a continuation object doesn’t get bad at all ... so this is excellent, I say very well done, and I thank you much! – EGME Nov 24 '19 at 15:56
  • @EGME Great! You're welcome. :-) – Mr.Wizard Nov 24 '19 at 15:58
  • I have an additional condition which I would like to check in order to determine if a partition should be generated or not ... basically, it thins out my set of partitions substantially and cuts a lot of time, which is good for what I want them (generating graph) ... I am trying to see if I can modify your code to do this ... but if not, would you be willing to give it a try? Not sure that should go to a new post, it is really a specific variant of this problem ... well, for me, it is closing time today ... – EGME Nov 24 '19 at 21:51
  • @EGME Please describe the condition. If I can solve it I'll add that; if I can't or it doesn't perform well you can post a new question. – Mr.Wizard Nov 25 '19 at 00:27
  • Ok, imagine that you have a partition into subsets of size 4, so that your typical subset is {a,b,c,d}. The condition is that the differences of the consecutive elements in the partition should be greater than 1, and also, the difference d-1 should not be n-1, where n is the number of elements in your list. This is one condition, which basically ensures that my partitions generate chords of simple graphs without multiple edges. Then you want to get rid of rotations and reflections, but I will describe that a bit later, I need to think how to say that succinctly. Can this be done? – EGME Nov 25 '19 at 21:14
  • So basically, that reduces the number of partitions into triples of a list with 18 elements from over 100 million to about 10 million or so ... And the time is cut from some hours to less than an hour. Then, if I can do the rotations, I will reduce the number by another factor of 10 and have something I can check in a day. But let’s get to that later. Right now, your code is working beautifully, have generated over 100 million partitions, and counting ... I am doing the postprocessing to reduce it to about 10 M. But would be nice to do in a short amount of time, or bigger cases. – EGME Nov 25 '19 at 21:18
  • @EGME That's sounds rather complicated. I would suggest you post a new question. I cannot think of a way to approach that that would not amount to just generating and checking. – Mr.Wizard Nov 25 '19 at 21:38
  • Ok, thanks ... right, I can’t think of another way either ... but ok, I will see if I can figure it out, or post a question later ... but thanks a lot in any case ... what you did already works amazingly well, and it allows me to check properties of graphs I could not earlier ... – EGME Nov 25 '19 at 21:40
  • Hello, Mr.Wizzard, I am wondering if it would be possible to make an improvement to your code, which surely you understand a lot better than I. Right now, you have a function f2 which produces a continuation object, f1 is used to extract the partition. Would it be possible to have a function (call it f3) which produces the continuation object from a given partition, so that I could start this algorithm with any given partition, in the middle of the whole possible lexi-ordered set of partitions? It would be great if I could I had this flexibility. Is my question clear? Thanks! – EGME Dec 06 '19 at 11:00
  • @EGME I am working on this now. – Mr.Wizard Dec 08 '19 at 03:57
  • @EGME Okay, I think I got it. Please see the addendum. – Mr.Wizard Dec 08 '19 at 04:50
  • Thanks! I will test this and get back to you! – EGME Dec 08 '19 at 09:47