14

For example, starting from {1,2,3,4}, I want to generate all permutations like {1,3,2,4},{1,3,4,2},{3,4,1,2} which preserve the order of e.g first two terms {1,2} and the order of e.g. last two terms {3,4}

I've tried to use the Permutation function, but it cannot preserve the order of {1,2} and {3,4}.

Kuba
  • 136,707
  • 13
  • 279
  • 740
user41614
  • 161
  • 4
  • Do you mean something like Tuples[{1, 2, 3}, 3] or Permutations[{1, 2, 3}]? – e.doroskevic Jul 14 '16 at 09:35
  • 3
    Please include the "last n-m terms" in the body of your question; it should not be necessary to read the title to answer the question. – Mr.Wizard Jul 14 '16 at 09:36
  • Sorry. For example, I want to permute {1,2,3,4,5},while preserving the order of first 3 terms(i.e.{1,2,3}) and the order of last 2 terms(i.e.{4,5}). For instance, {1,4,2,5,3} and {4,1,2,5,3} satisfies my requirement, because the order of {1,2,3} and the order of {4,5} are not changed. – user41614 Jul 14 '16 at 09:49
  • Somewhat related: (82522) – Mr.Wizard Jul 14 '16 at 12:37
  • Is speed important and are larger cases needed? If so, answers so far can be handily beaten... – ciao Jul 14 '16 at 21:15
  • @ciao You sure do like to dangle the carrot, don't you? :^) – Mr.Wizard Jul 15 '16 at 05:05
  • 1
    @Mr.Wizard - not at all - it's simply a case of prompting to see if the effort to code up an idea is warranted - I see problem, play code in head, guesstimate performance benefit, usually close... in this case, I was thinking of the extended interpretation (which can be done more quickly than what's here), but realized after re-reading question the OP is after simple shuffle-product as noted elsewhere. If OP says they need other interpretation and speed matters, I'll happily code up idea. – ciao Jul 15 '16 at 05:19
  • @ciao As you can see I have closed this as a duplicate. Since you have a method in mind that will beat oP2 would you be willing to post a self-Q&A to share it? – Mr.Wizard Jul 15 '16 at 05:35
  • @Mr.Wizard - sure, I'll take the time to code it up and compare, if correct in performance estimate, I'll post - but that's 2 things I have in the queue at your request (the other is the matrix generators - I hope you didn't think I meant they're in one handy place when I used the word "library" - I meant it in the sense of a collection, which in my case is as organized as a lazy teenager's bedroom), so don't hold your breath, and sorry ahead of time for my guaranteed tardiness... – ciao Jul 15 '16 at 05:59
  • @ciao I am the reigning king of procrastination and tardiness around here; you've got a long way to go to dethrone me. ;o) I fully understand if the library thing never happens as I've got half-finished answers scattered all across this site with things like "I'll continue this tomorrow" at the bottom. It's really quite embarrassing but nobody has particularly called me out on it so I hope I don't annoy too many with it. – Mr.Wizard Jul 15 '16 at 06:04
  • 1
    I cannot seem to post this as an answer: Pick[#, OrderedQ /@ (# /. (1 | 2 | 3) -> Nothing)] &@ Pick[#, OrderedQ /@ (# /. (4 | 5 | 6 | 7) -> Nothing)] &@ Permutations[Range[7]] – user1066 Jul 15 '16 at 09:23
  • @Mr.Wizard. [1-2] I'm not sure that I agree that this question is a duplicate, or that any of the answers you link to add anything other than one particular interpretation of the OP question. In general terms, the OP may want to generate permutations while preserving the order of certain terms. For example, to preserve the order of the first and last terms, and to preserve the order of all 'middle' terms: Pick[#, OrderedQ /@ (# /. (1 | 7) -> Nothing)] &@ Pick[#, OrderedQ /@ (# /. (2 | 3 | 4 | 5 | 6) -> Nothing)] &@ Permutations[Range[7]]. – user1066 Jul 15 '16 at 11:35
  • [2-2] Or, to preserve the order of the first and last terms only: Pick[#, OrderedQ /@ (# /. (2 | 3 | 4) -> Nothing)] &@ Permutations[Range[5]] – user1066 Jul 15 '16 at 11:37
  • @TomD If we assume than the "n" in "{1,2,3,...,n}" and "n-m terms" is the same, then this question became the duplicate as it is presently marked. (Or so I believe.) I guess we must await clarification from user41614. Kuba's code and my adaptation of it does allow preservation of order of arbitrary non-intersecting disjoint selections from the input list, without the inefficiency of total enumeration followed by filtering. – Mr.Wizard Jul 15 '16 at 12:10
  • 1
    @Mr. Wizard. I accept that your code is more efficient. But is seems to me it is also quite complex and 'non-obvious' for a relatively simple task? I do not like the way you closed this question only after a number of users (including yourself) had posted informative answers, and where now no-one else can contribute. It is such behaviour that has made me (and others) become very disillusioned with SO. It used to be that anyone could ask a question, and that anyone could answer a question. – user1066 Jul 15 '16 at 16:48
  • @TomD If you read the comments above you will see that I was hoping for a new question to separate the two interpretations of this post. It was never my intent to limit answers, but instead to give them the best platform possible. I certainly do not wish to contribute to your disillusionment and I hope that I am always responsive to such concerns. I shall reopen this question right now. Since you area also taking the more interesting interpretation it would probably be best to edit the question to explicitly match or at least include that. – Mr.Wizard Jul 15 '16 at 17:16
  • @TomD Separately I am troubled by your comment "It is such behaviour that has made me (and others) become very disillusioned with SO." I would like to learn of your concerns and address them as I am able. Would you please consider opening a [Meta] question on the topic? – Mr.Wizard Jul 15 '16 at 17:21
  • @ciao Please note the development above. I am doing my best to please everyone and hopefully not making a hash of it. – Mr.Wizard Jul 15 '16 at 17:23
  • 1
    @Mr.Wizard - hey, keep me out of the cat herding. .. – ciao Jul 15 '16 at 20:05
  • @user106 I have been hoping to hear from you and I see that you have not posted on Meta. I am genuinely concerned about your disillusionment and I am still hoping you will let me do my best to address it if you will engage me in a dialog. – Mr.Wizard Jul 23 '16 at 12:00

6 Answers6

9
pos = {{1, 2}, {4, 5}};
list = {a, b, c, d, e};

This answer is more general, OP wants to split the list on two parts while I'm allowing not covered elements to be permuted freely, thus unnecessarily complicated.

  • we replace elements in the same group with the same unique symbol e.g {x, x, c, y, y}
  • we take advantage of the fact that Permutations considers repeated elements identical,
  • we replace unique symbol ocurrences with consecutive elements from old groups

Ugly implementation

Module[{
  temp = list,      
  uni = Unique[] & /@ pos,      
  elements = list[[#]] & /@ pos,
  i
  },

 MapThread[(temp[[#]] = #2 ) &, {pos, uni}];

 Fold[
   (i = 1; # /. #2[[1]] :> #2[[2, i++]]) &,
   #, 
   Transpose[{uni, elements}]
 ] & /@ Permutations[temp]  


 ]

enter image description here

Kuba
  • 136,707
  • 13
  • 279
  • 740
  • 1
    I don't think it's ugly. – Szabolcs Jul 14 '16 at 10:32
  • This method seems familiar. I have a suspicion that this question has been asked before. Nevertheless +1 for some nice coding! – Mr.Wizard Jul 14 '16 at 12:13
  • @Mr.Wizard Thanks, and I think so. p.s. I overdid it since I allowed to specify parts which don't contain whole list, while OP only needs to split the list on two parts. As Coolwater shows. – Kuba Jul 14 '16 at 12:16
  • Generality is always nice however! – Mr.Wizard Jul 14 '16 at 12:17
  • This may have been the problem I was remembering and it is certainly not a duplicate: (32404) – Mr.Wizard Jul 14 '16 at 12:53
  • I think I either misunderstand your intent or something is wrong here. If I convert your shorter code to a function oP and then run oP[{"w", "i", "z", "a"}, {{1, 2, 4}}] I get {{"w", "i", "a", "z"}, {"w", "i", "z", "a"}, {"w", "a", "i", "z"}, {"a", "w", "i", "z"}} -- I was expecting "w", "i", and "a" to remain in that order, but instead I get things like {"a", "w", "i", "z"}. Comment? – Mr.Wizard Jul 14 '16 at 13:57
  • @Mr.Wizard - as stated in the OP, this is just the shuffle-product, asked and answered before... – ciao Jul 15 '16 at 00:24
  • @ciao I now see the close relation to (41614) (thanks!) but unless I am missing something this is not "just" the shuffle-product as a subset of the elements (those not specified as ordered) may appear in any order. Doesn't that complicate this? – Mr.Wizard Jul 15 '16 at 01:41
  • @Mr.Wizard- since the first N are carved out, and then the remaining are carved out, it's exactly a shuffle-product. If OP said "first x and last y" where y is not length-x, it's a bit different (or for that matter using arbitrary groups), it's still just a shuffle-product with non-group members permuted... – ciao Jul 15 '16 at 01:47
  • @ciao Dare to create a generic topic about that?, I too recognize it is relatively common topic yet usually I don't know what to look for. – Kuba Jul 15 '16 at 07:45
  • @Mr.Wizard since your answer is a fixed version of my naive approach I will just leave here the old one. – Kuba Jul 15 '16 at 07:46
9

I think one should avoid Permutations, because it imposes unnecessarily high complexity. E.g:

go[L_, m_] := Normal[SparseArray[Flatten[With[{R = Range[Length[L]]},
 MapIndexed[Thread[Thread[{First[#2],
  Join[#, Complement[R, #]]}] -> L] &, Subsets[R, {m}]]], 1]]]

go[{1, 2, 3, 4, 5}, 2]
Coolwater
  • 20,257
  • 3
  • 35
  • 64
  • @Kuba I put the first m elements at whichever ordered positions are possible (from Subset). I put the rest n-m elements (in order because of Complement) on remaining positions, so i wouldn't expect that. – Coolwater Jul 14 '16 at 12:04
  • 1
    @Kuba The title contradicts that interpretation, even though the special case in the question text coincide with it – Coolwater Jul 14 '16 at 12:10
  • You are right, I have misinterpreted the question – Kuba Jul 14 '16 at 12:11
6

Working with Kuba's redacted method (which had problems) I came up with this:

oP2[list_, groups_] :=
  Module[{idx, ele},
    idx = ArrayComponents[
      Range @ Length @ list, 1,
      MapIndexed[Alternatives @@ # -> #2[[1]] &, groups]
    ];
    ele = list[[Ordering @ idx]];
    ele[[#]] & /@ Ordering /@ Ordering /@ Permutations @ idx
  ]

It appears to work correctly:

oP2[{"w", "i", "z", "a", "r", "d"}, {{3, 6}, {1, 2, 4}}] // Shallow
{{w,i,z,a,r,d},{w,i,z,a,d,r},{w,i,z,r,a,d},{w,i,z,r,d,a},{w,i,z,d,a,r},
 {w,i,z,d,r,a},{w,i,a,z,r,d},{w,i,a,z,d,r},{w,i,a,r,z,d},{w,i,r,z,a,d},<<50>>}
Mr.Wizard
  • 271,378
  • 34
  • 587
  • 1,371
5

Several interpretations that seem to do other than what the OP is asking for resulting in unnecessary code complexity..

This is simply a shuffle-product as stated in OP (the first N of length M shuffled with the remaining M-N of the list.)

This uses some code for the SP I did long ago, with a TakeDrop tacked on to provide specification of N per OP. Quite good performance, and if OP needs functionality of other interpretations (e.g., first N and last N of list with rest fully permuted, etc.), easily adapted to such cases.

op = With[{j = Join @@ {##}, sp = Permutations[Join @@ ConstantArray @@@Transpose[{Range@Length@{##}, Length /@ {##}}]]},
      Partition[j[[Flatten[Ordering[Ordering[#]] & /@ sp]]], Length[j]]] & @@ TakeDrop@## &;

Use, (e.g. list of length 7 with first 3 and last 4 properly ordered):

op[{1,2,3,4,5,6,7},3]
ciao
  • 25,774
  • 2
  • 58
  • 139
  • Ah, now I finally see the interpretation you were referring to. I somehow was blind to "n" being the same value. However by this interpretation the question is a duplicate of (41614) and should be closed as such. Should we edit it to instead be the generalization that Kuba addressed, or close it and post a new question with that description? – Mr.Wizard Jul 15 '16 at 05:10
  • @Mr.Wizard - Is that "we" a nosism? ;=}

    As the OP is written, I'd consider it a duplicate, but the generalization is interesting, so.... your call, I had no plan to close-vote as dupe.

    – ciao Jul 15 '16 at 05:15
  • By "we" I expressly meant you and me. – Mr.Wizard Jul 15 '16 at 05:28
  • @Mr.Wizard - I know - it was a joke, hope not taken the wrong way. Court jester fail... – ciao Jul 15 '16 at 05:32
  • I did not take it the wrong way, you made your mood clear with an emoji; I however did not, so "fail" on my end. Seriously, little bits of humor from you, belisarius, Daniel, Simon, and others really add to the experience here; please don't stop. – Mr.Wizard Jul 15 '16 at 05:40
  • Interpreting the question as you have done, and using the 'brute force' approach: Pick[#, OrderedQ /@ (# /. (1 | 2 | 3) -> Nothing)] &@ Pick[#, OrderedQ /@ (# /. (4 | 5 | 6 | 7) -> Nothing)] &@ Permutations[Range[7]] == op[{1, 2, 3, 4, 5, 6, 7}, 3] – user1066 Jul 15 '16 at 09:34
3
n = 5; m = 3;
a = Range[n];
left = Subsets[a, {m}];
right = Complement[a, #] & /@ left;
perm = MapThread[Join, {left, right}]
Permute[a, #] & /@ perm//TableForm

Explanation: We prepare first a list of all possible permutations using Subsets and Complement. Subsequently we Join permutations for the left $m$ and right $n-m$ objects and apply permutations by using Permute. As a result we obtain:

$\left( \begin{array}{ccccc} \mathbf{1} & \mathbf{2} & \mathbf{3} & 4 & 5 \\ \mathbf{1} & \mathbf{2} & 4 & \mathbf{3} & 5 \\ \mathbf{1} & \mathbf{2} & 4 & 5 & \mathbf{3} \\ \mathbf{1} & 4 & \mathbf{2} & \mathbf{2} & 5 \\ \mathbf{1} & 4 & \mathbf{2} & 5 & \mathbf{3} \\ \mathbf{1} & 4 & 5 & \mathbf{2} & \mathbf{3} \\ 4 & \mathbf{1} & \mathbf{2} & \mathbf{3} & 5 \\ 4 & \mathbf{1} & \mathbf{2} & 5 & \mathbf{3} \\ 4 & \mathbf{1} & 5 & \mathbf{2} & \mathbf{3} \\ 4 & 5 & \mathbf{1} & \mathbf{2} & \mathbf{3} \\ \end{array} \right)$

Notice, this example is slightly different from the one in OP. Here $n=5$, $m=3$.

yarchik
  • 18,202
  • 2
  • 28
  • 66
  • This seems to miss some cases, e.g. 2 is never the last element. – N.J.Evans Jul 14 '16 at 21:27
  • 2
    @N.J.Evans Please, notice that in my example m=3and therefore 2 cannot be the last element. – yarchik Jul 14 '16 at 21:57
  • noted! I was expecting the same output provided by OP. I should have read more closely. I can't change my vote w/o an edit now :( If you edit to point out that you've given a different case from OP's example I'll upvote it. – N.J.Evans Jul 15 '16 at 13:02
  • 1
    @N.J.Evans Now a note is added and additionally an illustration is provided – yarchik Jul 15 '16 at 14:13
1

You can use Fold to apply patterns repeatedly to cull the list of permutations:

list = {1, 2, 3, 4};
patterns = {___, #1, ___, #2, ___} & @@@ Partition[list, 2];

This generates the patterns {{___, 1, ___, 2, ___}, {___, 3, ___, 4, ___}} which can be applied using Fold:

Fold[
 Cases[#1, #2] &,
 Permutations[list],
 patterns
 ]

The meat of the answer is the application of Fold and Cases to a list of patterns that you want to apply. The first application selects all cases where 1,2 are ordered, and the second takes only the cases of that subset in which 3,4 are ordered.

N.J.Evans
  • 5,093
  • 19
  • 25