GNU Octave 11.1.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
 
Loading...
Searching...
No Matches
perms.cc
Go to the documentation of this file.
1////////////////////////////////////////////////////////////////////////
2//
3// Copyright (C) 2023-2026 The Octave Project Developers
4//
5// See the file COPYRIGHT.md in the top-level directory of this
6// distribution or <https://octave.org/copyright/>.
7//
8// This file is part of Octave.
9//
10// Octave is free software: you can redistribute it and/or modify it
11// under the terms of the GNU General Public License as published by
12// the Free Software Foundation, either version 3 of the License, or
13// (at your option) any later version.
14//
15// Octave is distributed in the hope that it will be useful, but
16// WITHOUT ANY WARRANTY; without even the implied warranty of
17// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18// GNU General Public License for more details.
19//
20// You should have received a copy of the GNU General Public License
21// along with Octave; see the file COPYING. If not, see
22// <https://www.gnu.org/licenses/>.
23//
24////////////////////////////////////////////////////////////////////////
25
26#if defined (HAVE_CONFIG_H)
27# include "config.h"
28#endif
29
30#include <algorithm>
31#include <numeric>
32
33#include "defun.h"
34#include "error.h"
35#include "errwarn.h"
36#include "ovl.h"
37
39
40static inline double
41Factorial (octave_idx_type n)
42{
43 double ret = 1;
44 for (octave_idx_type i = 2; i <= n; i++)
45 ret *= i;
46 return ret;
47}
48
49//
50// Use C++ template to cater for the different octave array classes.
51//
52
53template <typename T>
54static inline Array<T>
55GetPerms (const Array<T>& ar_in, bool uniq_v = false)
56{
57 octave_idx_type m = ar_in.numel ();
58 double nr = Factorial (m); // number of rows of resulting array
59
60 // Setup index vector filled from 0..m-1
62 std::iota (&myvidx[0], &myvidx[m], 0);
63
64 const T *Ar = ar_in.data ();
65
66 if (uniq_v) // need to reduce the size of the resulting array
67 {
68 // Mutual Comparison is used to detect duplicated values.
69 // Using sort would be possible for numerical values and be of
70 // O(n*log (n)) complexity instead of O(n*(n -1) / 2). But sort
71 // is not supported for the octave_value container (structs/cells).
72 // Moreover, sort requires overhead for creating, filling, and sorting
73 // the intermediate array which would need to be post-processed.
74 // In practice, and because n must be very small, mutual comparison is
75 // typically faster and consumes less memory.
76
77 // Number of unique permutations is n! / (n_el1! * n_el2! * ...)
78 // where n_elX is the number of myvidx elements with value X.
79 for (octave_idx_type i = 0; i < m - 1; i++)
80 {
81 octave_idx_type count = 1;
82 for (octave_idx_type j = i + 1; j < m; j++)
83 {
84 bool isequal;
85 if constexpr (std::is_same<T, octave_value>::value)
86 // operator '==' is not supported for octave_value objects
87 isequal = Ar[i].is_equal (Ar[j]);
88 else
89 isequal = (Ar[i] == Ar[j]);
90
91 if (myvidx[j] > myvidx[i] && isequal)
92 {
93 myvidx[j] = myvidx[i]; // not yet processed...
94 ++count;
95 }
96 }
97 nr /= Factorial (count);
98 }
99
100 // At this point, myvidx serves as a unique id of the elements.
101 }
102
103 // Sort vector indices for inverse lexicographic order later.
104 std::sort (myvidx, myvidx + m, std::greater<octave_idx_type> ());
105
106 // Set up result array
107 octave_idx_type n = static_cast<octave_idx_type> (nr);
108 Array<T> res (dim_vector (n, m));
109 T *Res = res.rwdata ();
110
111 // Do the actual job
112 octave_idx_type i = 0;
113 do
114 {
115 for (octave_idx_type j = 0; j < m; j++)
116 Res[i + j * n] = Ar[myvidx[j]];
117 i++;
118 }
119 while (std::next_permutation (myvidx, myvidx + m, std::greater<int> ()));
120
121 return res;
122}
123
124DEFUN (perms, args, ,
125 doc: /* -*- texinfo -*-
126@deftypefn {} {@var{P} =} perms (@var{v})
127@deftypefnx {} {@var{P} =} perms (@var{v}, "unique")
128Generate all permutations of vector @var{v} with one row per permutation.
129
130Results are returned in reverse lexicographic order if @var{v} is in ascending
131order. If @var{v} is in a different permutation, then the result is permuted
132that way too. Consequently, an input in descending order yields a result in
133normal lexicographic order. The result has size
134@code{factorial (@var{n}) * @var{n}}, where @var{n} is the length of @var{v}.
135Any repeated elements are included in the output.
136
137If the optional argument @qcode{"unique"} is given then only unique
138permutations are returned, using less memory and taking less time than calling
139@code{unique (perms (@var{v}), "rows")}.
140
141Example 1
142
143@example
144@group
145perms ([1, 2, 3])
146@xresult{}
1473 2 1
1483 1 2
1492 3 1
1502 1 3
1511 3 2
1521 2 3
153@end group
154@end example
155
156Example 2
157
158@example
159@group
160perms ([1, 1, 2, 2], "unique")
161@xresult{}
1622 2 1 1
1632 1 2 1
1642 1 1 2
1651 2 2 1
1661 2 1 2
1671 1 2 2
168@end group
169@end example
170
171Programming Note: If the @qcode{"unique"} option is not used, the length of
172@var{v} should be no more than 10-12 to limit memory consumption. Even with
173@qcode{"unique"}, there should be no more than 10-12 unique elements in
174@var{v}.
175@seealso{permute, randperm, nchoosek}
176
177@end deftypefn */)
178{
179 int nargin = args.length ();
180
181 if (nargin < 1 || nargin > 2)
182 print_usage ();
183
184 octave_value retval;
185
186 // Parameter check "unique"
187 bool uniq_v = false;
188 if (nargin == 2)
189 {
190 const charMatrix opt = args (1).char_matrix_value ();
191 const char *str = opt.data ();
192 if (std::string (str, opt.cols ()) != "unique")
193 {
194 error ("perms: option must be the string \"unique\".");
195 }
196 uniq_v = true;
197 }
198
199 if (! (args (0).is_matrix_type () || args (0).is_range ()
200 || args (0).iscell () || args (0).is_scalar_type ()
201 || args (0).isstruct ()))
202 {
203 error ("perms: V must be a matrix, range, cell array, "
204 "struct, or a scalar.");
205 }
206
207 std::string clname = args (0).class_name ();
208
209 // Execute main permutation code for the different classes
210 if (clname == "double")
211 retval = GetPerms<double> (args (0).array_value (), uniq_v);
212 else if (clname == "single")
213 retval = GetPerms<float> (args (0).float_array_value (), uniq_v);
214 else if (clname == "logical")
215 retval = GetPerms<bool> (args (0).bool_array_value (), uniq_v);
216 else if (clname == "char")
217 retval = GetPerms<char> (args (0).char_array_value (), uniq_v);
218 else if (clname == "int8")
219 retval = GetPerms<octave_int8> (args (0).int8_array_value (), uniq_v);
220 else if (clname == "int16")
221 retval = GetPerms<octave_int16> (args (0).int16_array_value (), uniq_v);
222 else if (clname == "int32")
223 retval = GetPerms<octave_int32> (args (0).int32_array_value (), uniq_v);
224 else if (clname == "int64")
225 retval = GetPerms<octave_int64> (args (0).int64_array_value (), uniq_v);
226 else if (clname == "uint8")
227 retval = GetPerms<octave_uint8> (args (0).uint8_array_value (), uniq_v);
228 else if (clname == "uint16")
229 retval = GetPerms<octave_uint16> (args (0).uint16_array_value (), uniq_v);
230 else if (clname == "uint32")
231 retval = GetPerms<octave_uint32> (args (0).uint32_array_value (), uniq_v);
232 else if (clname == "uint64")
233 retval = GetPerms<octave_uint64> (args (0).uint64_array_value (), uniq_v);
234 else if (clname == "cell")
235 retval = Cell (GetPerms<octave_value> (args (0).cell_value (), uniq_v));
236 else if (clname == "struct")
237 {
238 const octave_map map_in (args (0).map_value ());
239 string_vector fn = map_in.fieldnames ();
240 if (fn.numel () == 0 && map_in.numel () != 0)
241 {
243 retval = out;
244 }
245 else
246 {
247 octave_map out;
248 if (fn.numel () == 0)
249 {
250 out = octave_map (dim_vector (1, 0));
251 }
252 else
253 {
254 for (octave_idx_type i = 0; i < fn.numel (); i++)
255 {
256 out.assign (fn (i), GetPerms<octave_value>
257 (map_in.contents (fn (i)), uniq_v));
258 }
259 }
260 retval = out;
261 }
262 }
263 else // none of the above class criteria were met
264 {
265 warning ("perms: unable to permute for class %s", clname.c_str ());
266 // retval stays empty
267 }
268 return ovl (retval);
269}
270
271/*
272
273%!assert (rows (perms (1:6)), factorial (6))
274%!assert (perms (pi), pi)
275%!assert (perms ([e, pi]), [pi, e; e, pi])
276%!assert (perms ([pi, e]), [e, pi; pi, e])
277%!assert (perms ([1 2 3]), [3 2 1; 3 1 2; 2 3 1; 2 1 3; 1 3 2; 1 2 3])
278%!assert (sortrows (perms (1:5)), sortrows (perms ([2 5 4 1 3]')))
279%!assert (perms ("abc"), char ("cba", "cab", "bca", "bac", "acb", "abc"))
280%!assert (sortrows (perms ("fobar")), unique (perms ("fobar"), "rows"))
281%!assert (unique (perms (1:5)(:))', 1:5)
282%!assert (perms (int8 (1:4)), int8 (perms (1:4)))
283
284%!assert (sortrows (perms ("abb", "unique")), ["abb"; "bab"; "bba"])
285%!assert (size (perms ([1 1 1 1 2 2 2 3 3], "unique")), [1260 9])
286%!assert (size (perms (int8([1 1 1 1 1 2 2 2 2 3 3 3]), "unique")), [27720 12])
287
288## Test "unique" with different input orders.
289%!assert <*67115> (sortrows (perms ([1 1 2 2], "unique")), [1 1 2 2; 1 2 1 2; 1 2 2 1; 2 1 1 2; 2 1 2 1; 2 2 1 1])
290%!assert <*67115> (sortrows (perms ([1 2 1 2], "unique")), [1 1 2 2; 1 2 1 2; 1 2 2 1; 2 1 1 2; 2 1 2 1; 2 2 1 1])
291%!assert <*67115> (sortrows (perms ([1 2 2 1], "unique")), [1 1 2 2; 1 2 1 2; 1 2 2 1; 2 1 1 2; 2 1 2 1; 2 2 1 1])
292%!assert <*67115> (sortrows (perms ([2 1 1 2], "unique")), [1 1 2 2; 1 2 1 2; 1 2 2 1; 2 1 1 2; 2 1 2 1; 2 2 1 1])
293%!assert <*67115> (sortrows (perms ([2 1 2 1], "unique")), [1 1 2 2; 1 2 1 2; 1 2 2 1; 2 1 1 2; 2 1 2 1; 2 2 1 1])
294%!assert <*67115> (sortrows (perms ([2 2 1 1], "unique")), [1 1 2 2; 1 2 1 2; 1 2 2 1; 2 1 1 2; 2 1 2 1; 2 2 1 1])
295
296## Should work for any array type, such as cells and structs,
297## and not only for numeric data.
298
299%!assert <*52431> (perms ({1}), {1})
300%!assert <*52431> (perms ({0.1, "foo"}), {"foo", 0.1; 0.1, "foo"})
301%!assert <*52431> (perms ({"foo", 0.1}), {0.1, "foo"; "foo", 0.1})
302%!assert <*52431> (perms ({"foo"; 0.1}), {0.1, "foo"; "foo", 0.1})
303%!assert <*52431> (perms ({0.1; "foo"}), {"foo", 0.1; 0.1, "foo"})
304%!assert <*52431> (perms ({"foo", "bar"}), {"bar", "foo"; "foo", "bar"})
305%!assert <*52431> (perms ({"bar", "foo"}), {"foo", "bar"; "bar", "foo"})
306%!
307%!assert <*52431> (perms (struct ()), struct ())
308%!assert <*52431> (perms (struct ("foo", {1, 2})),
309%! struct ("foo", {2, 1; 1, 2}))
310%!assert <*52431> (perms (struct ("foo", {1, 2}, "bar", {3, 4})),
311%! struct ("foo", {2, 1; 1, 2}, "bar", {4, 3; 3, 4}))
312
313## Also sort logical input with order dependent on the input order and
314## not their values.
315
316%!assert <*52431> (perms (logical ([1 0])), logical ([0 1; 1 0]))
317%!assert <*52431> (perms (logical ([0 1])), logical ([1 0; 0 1]))
318%!assert <*52431> (perms (logical ([0 1 0])),
319%! logical ([0 1 0; 0 0 1; 1 0 0; 1 0 0; 0 0 1; 0 1 0]))
320%!assert <*52431> (perms (logical ([0 1 1])),
321%! logical ([1 1 0; 1 0 1; 1 1 0; 1 0 1; 0 1 1; 0 1 1]))
322
323%!assert <*52432> (perms ([]), reshape ([], 1, 0))
324%!assert <*52432> (perms (single ([])), reshape (single ([]), 1, 0))
325%!assert <*52432> (perms (int8 ([])), reshape (int8 ([]), 1, 0))
326%!assert <*52432> (perms ({}), cell (1, 0))
327
328%!test <*52432>
329%! s = struct ();
330%! s(1) = [];
331%! assert (perms (reshape (s, 0, 0)), reshape (s, 1, 0));
332%! assert (perms (reshape (s, 0, 1)), reshape (s, 1, 0));
333
334## test if "unique" works also for cell arrays
335%!assert <*63965> (perms ({"foo"; "foo"}, "unique"), {"foo", "foo"})
336%!assert <*63965> (perms ({"foo", "foo", "bar"}, "unique"), ...
337%! {"bar", "foo", "foo";
338%! "foo", "bar", "foo";
339%! "foo", "foo", "bar"})
340
341## Test input validation
342%!error <Invalid call> perms ()
343%!error <option must be the string "unique"> perms (1:5, "foobar")
344%!error <option must be the string "unique"> perms (1:5, {"foo"})
345
346*/
347
348OCTAVE_END_NAMESPACE (octave)
N Dimensional Array with copy-on-write semantics.
Definition Array-base.h:130
octave_idx_type cols() const
Definition Array-base.h:495
const T * data() const
Size of the specified dimension.
Definition Array-base.h:687
octave_idx_type numel() const
Number of elements in the array.
Definition Array-base.h:440
Definition Cell.h:41
Vector representing the dimensions (size) of an Array.
Definition dim-vector.h:92
string_vector fieldnames() const
Definition oct-map.h:332
const Cell & contents(const_iterator p) const
Definition oct-map.h:310
octave_idx_type numel() const
Definition oct-map.h:368
void assign(const std::string &k, const Cell &val)
Definition oct-map.h:344
octave_idx_type numel() const
Definition str-vec.h:98
OCTAVE_BEGIN_NAMESPACE(octave) static octave_value daspk_fcn
void print_usage()
Definition defun-int.h:72
#define DEFUN(name, args_name, nargout_name, doc)
Macro to define a builtin function.
Definition defun.h:56
void warning(const char *fmt,...)
Definition error.cc:1083
void error(const char *fmt,...)
Definition error.cc:1008
#define OCTAVE_LOCAL_BUFFER(T, buf, size)
Definition oct-locbuf.h:44
octave_value_list ovl(const OV_Args &... args)
Construct an octave_value_list with less typing.
Definition ovl.h:217