Mathematica Stack Exchange is a question and answer site for users of Wolfram Mathematica. Join them; it only takes a minute:

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

I gather from this question that there is no primitive to build unordered tuples. That is, I want to do the equivalent of the following:

Union[Sort /@ Tuples[Range[9], {3}]]

to construct tuples of length 3 from Range[9], but considering (for example) {1,2,3} and {2,3,1} to be the same. The construct above clearly will not work if we replace 3 by, say, 20. Here is another approach:

f[lst_] := Flatten[Map[Table[Append[#, i], {i, Last@#, 9}] &, lst], 1];
Nest[f, Table[{i}, {i, Range[9]}], 2]

This has much better performance. But still, if 2 is replaced by 20, it takes almost two minutes on my computer. Is there a better approach?

share|improve this question
1  
Not exactly the same thing, but are you aware of Subsets[Range[9], {3}]? – Szabolcs 18 hours ago
    
@Szabolcs I am. Thanks. And no, it's not the same thing - it doesn't allow repetitions, which I definitely want to do (even though the example in the OP didn't show it). – rogerl 18 hours ago
2  
Would the downvoter care to comment? – rogerl 15 hours ago
    
@Szabolcs I kept knocking on the door of Subsets and it finally let me in. See my (second) answer below. – Mr.Wizard 9 hours ago

I saw your problem as a partition problem, so I tried the following function:

unorderedTuples[len_] := Flatten[IntegerPartitions[#, {len}, Range[9]] & /@ Range[len, 9*len], 1]

In short, it looks for all the ways you can sum the numbers 1-9 from len to 9*len for a len-tuple.

There's some ordering difference between your method and this function, but

Sort@(Sort /@ unorderedTuples[3]) == Nest[f, Table[{i}, {i, Range[9]}], 2]
(* True *)
Sort@(Sort /@ unorderedTuples[10]) == Nest[f, Table[{i}, {i, Range[9]}], 9]
(* True *)

Lastly, timing:

unorderedTuples[10] // AbsoluteTiming
(* 0.0227264 *)
Nest[f, Table[{i}, {i, Range[9]}], 9] // AbsoluteTiming
(* 0.273851 *)
share|improve this answer
3  
+1 Clever. I was sure I was missing something obvious, but never thought of using partitions! I'll give it a while before accepting. – rogerl 18 hours ago

This solution is not as good and not as fast as the one by @Dubs, but perhaps it is of some interest.

Your example could be written as

Flatten[Table[{i, j, k}, {i, 1, 9}, {j, i, 9}, {k, j, 9}], 2]

We can generalize this to larger tuples of size n as follows:

n = 10;
result = With[
   {list = Table[Unique[it], {n}]},
   {iter = Sequence @@ Table[{list[[i]], If[i == 1, 1, list[[i - 1]]], 9}, {i, n}]},
   Flatten[Table[list, iter], n - 1]
   ];

For n=20, it takes 5.6 s on my machine vs 1.2 s using the method by @Dubs.

share|improve this answer
    
this throws a With called with 3 arguments Did they augment With in some recent version? – george2079 17 hours ago
    
@george, it's undocumented, and first showed up in 10.1. – J. M. 17 hours ago
2  
10.1 is what I have .. anyway for older versions this works if you do nested With's : With[{list=..} , With[{iter=..} , body ] ] – george2079 17 hours ago

I took a look at this a few hours later and I realized the solution was staring me in the face.

This is now competitive with Dubs' IntegerPartitions solution, especially if you consider that unlike his it produces a fully sorted list by default.

f2[n_, m_] := 
  Subsets[Range[m + n - 1], {n}] // 
    Subtract[#, ConstantArray[Range[0, n - 1], Length @ #]] &

Test:

f2[5, 9] === Sort[Sort /@ unorderedTuples[5]]
True

If one includes sorting my code is significantly faster than unorderedTuples; if one does not is it still not too shabby. Closer still after the last update.

Sort[Sort /@ unorderedTuples[20]]         // Length // RepeatedTiming

unorderedTuples[20]                       // Length // RepeatedTiming

f2[20, 9]                                 // Length // RepeatedTiming
{4.643, 3108105}

{1.15, 3108105}

{1.35, 3108105}
share|improve this answer
    
Now this is clever… :) – J. M. 4 hours ago
    
This very technique is used somewhere else here - searched a bit, could not find it, don't recall whose answer it was (nor the question) ... – ciao 4 hours ago

This is about an order of magnitude slower than Dubs' answer but recursion is quite direct, and I am working to make it faster.

Attributes[f] = Listable;
f[0, c___, _] := {{c}}
f[n_, m_, c___] := Catenate @ f[n - 1, Range @ m, m, c]

f[3, 4]
{{1, 1, 1}, {1, 1, 2}, {1, 2, 2}, {2, 2, 2}, {1, 1, 3},
 {1, 2, 3}, {2, 2, 3}, {1, 3, 3}, {2, 3, 3}, {3, 3, 3},
 {1, 1, 4}, {1, 2, 4}, {2, 2, 4}, {1, 3, 4}, {2, 3, 4},
 {3, 3, 4}, {1, 4, 4}, {2, 4, 4}, {3, 4, 4}, {4, 4, 4}}
f[5, 9]            // Length // RepeatedTiming
unorderedTuples[5] // Length // RepeatedTiming

{0.00279, 1287}

{0.000338, 1287}

share|improve this answer

only marginally different that @Szabolcs... (independent, really! ) a bit cleaner avoiding that If

unorderedtups[rngmax_, n_] := Module[{a},
  a[0] = 1;
  Flatten[Table[ Array[a, n] ,
    Evaluate[Sequence @@ (Table[{a[i], a[i - 1], rngmax}, {i, n}])]], 
   n-1]]
unorderedtups[9, 20] // AbsoluteTiming

Edit: Just realized using indexed symbols for the iterators makes this considerably slower!!

share|improve this answer
    
This is what I did first. It was slow so I switched to atomic symbols. – Szabolcs 16 hours ago

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.