GNU Octave 10.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-2025 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; // 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)
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 octave_idx_type N_el = 1;
78 double denom = 1.0;
79 // Number of unique permutations is n! / (n_el1! * n_el2! * ...)
80 for (octave_idx_type i = 0; i < m - 1; i++)
81 {
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 N_el++;
95 }
96 else
97 {
98 denom *= Factorial (N_el);
99 N_el = 1;
100 }
101 }
102 }
103 denom *= Factorial (N_el);
104 nr = Factorial (m) / denom;
105 }
106 else
107 nr = Factorial (m);
108
109 // Sort vector indices for inverse lexicographic order later.
110 std::sort (myvidx, myvidx + m, std::greater<octave_idx_type> ());
111
112 // Set up result array
113 octave_idx_type n = static_cast<octave_idx_type> (nr);
114 Array<T> res (dim_vector (n, m));
115 T *Res = res.rwdata ();
116
117 // Do the actual job
118 octave_idx_type i = 0;
119 do
120 {
121 for (octave_idx_type j = 0; j < m; j++)
122 Res[i + j * n] = Ar[myvidx[j]];
123 i++;
124 }
125 while (std::next_permutation (myvidx, myvidx + m, std::greater<int> ()));
126
127 return res;
128}
129
130DEFUN (perms, args, ,
131 doc: /* -*- texinfo -*-
132@deftypefn {} {@var{P} =} perms (@var{v})
133@deftypefnx {} {@var{P} =} perms (@var{v}, "unique")
134Generate all permutations of vector @var{v} with one row per permutation.
135
136Results are returned in reverse lexicographic order if @var{v} is in ascending
137order. If @var{v} is in a different permutation, then the result is permuted
138that way too. Consequently, an input in descending order yields a result in
139normal lexicographic order. The result has size
140@code{factorial (@var{n}) * @var{n}}, where @var{n} is the length of @var{v}.
141Any repeated elements are included in the output.
142
143If the optional argument @qcode{"unique"} is given then only unique
144permutations are returned, using less memory and taking less time than calling
145@code{unique (perms (@var{v}), "rows")}.
146
147Example 1
148
149@example
150@group
151perms ([1, 2, 3])
152@result{}
1533 2 1
1543 1 2
1552 3 1
1562 1 3
1571 3 2
1581 2 3
159@end group
160@end example
161
162Example 2
163
164@example
165@group
166perms ([1, 1, 2, 2], "unique")
167@result{}
1682 2 1 1
1692 1 2 1
1702 1 1 2
1711 2 2 1
1721 2 1 2
1731 1 2 2
174@end group
175@end example
176
177Programming Note: If the @qcode{"unique"} option is not used, the length of
178@var{v} should be no more than 10-12 to limit memory consumption. Even with
179@qcode{"unique"}, there should be no more than 10-12 unique elements in
180@var{v}.
181@seealso{permute, randperm, nchoosek}
182
183@end deftypefn */)
184{
185 int nargin = args.length ();
186
187 if (nargin < 1 || nargin > 2)
188 print_usage ();
189
190 octave_value retval;
191
192 // Parameter check "unique"
193 bool uniq_v = false;
194 if (nargin == 2)
195 {
196 const charMatrix opt = args (1).char_matrix_value ();
197 const char *str = opt.data ();
198 if (std::string (str, opt.cols ()) != "unique")
199 {
200 error ("perms: option must be the string \"unique\".");
201 }
202 uniq_v = true;
203 }
204
205 if (! (args (0).is_matrix_type () || args (0).is_range ()
206 || args (0).iscell () || args (0).is_scalar_type ()
207 || args (0).isstruct ()))
208 {
209 error ("perms: V must be a matrix, range, cell array, "
210 "struct, or a scalar.");
211 }
212
213 std::string clname = args (0).class_name ();
214
215 // Execute main permutation code for the different classes
216 if (clname == "double")
217 retval = GetPerms<double> (args (0).array_value (), uniq_v);
218 else if (clname == "single")
219 retval = GetPerms<float> (args (0).float_array_value (), uniq_v);
220 else if (clname == "logical")
221 retval = GetPerms<bool> (args (0).bool_array_value (), uniq_v);
222 else if (clname == "char")
223 retval = GetPerms<char> (args (0).char_array_value (), uniq_v);
224 else if (clname == "int8")
225 retval = GetPerms<octave_int8> (args (0).int8_array_value (), uniq_v);
226 else if (clname == "int16")
227 retval = GetPerms<octave_int16> (args (0).int16_array_value (), uniq_v);
228 else if (clname == "int32")
229 retval = GetPerms<octave_int32> (args (0).int32_array_value (), uniq_v);
230 else if (clname == "int64")
231 retval = GetPerms<octave_int64> (args (0).int64_array_value (), uniq_v);
232 else if (clname == "uint8")
233 retval = GetPerms<octave_uint8> (args (0).uint8_array_value (), uniq_v);
234 else if (clname == "uint16")
235 retval = GetPerms<octave_uint16> (args (0).uint16_array_value (), uniq_v);
236 else if (clname == "uint32")
237 retval = GetPerms<octave_uint32> (args (0).uint32_array_value (), uniq_v);
238 else if (clname == "uint64")
239 retval = GetPerms<octave_uint64> (args (0).uint64_array_value (), uniq_v);
240 else if (clname == "cell")
241 retval = GetPerms<octave_value> (args (0).cell_value (), uniq_v);
242 else if (clname == "struct")
243 {
244 const octave_map map_in (args (0).map_value ());
245 string_vector fn = map_in.fieldnames ();
246 if (fn.numel () == 0 && map_in.numel () != 0)
247 {
249 retval = out;
250 }
251 else
252 {
253 octave_map out;
254 if (fn.numel () == 0)
255 {
256 out = octave_map (dim_vector (1, 0));
257 }
258 else
259 {
260 for (octave_idx_type i = 0; i < fn.numel (); i++)
261 {
262 out.assign (fn (i), GetPerms<octave_value>
263 (map_in.contents (fn (i)), uniq_v));
264 }
265 }
266 retval = out;
267 }
268 }
269 else // none of the above class criteria were met
270 {
271 warning ("perms: unable to permute for class %s", clname.c_str ());
272 // retval stays empty
273 }
274 return ovl (retval);
275}
276
277/*
278
279%!assert (rows (perms (1:6)), factorial (6))
280%!assert (perms (pi), pi)
281%!assert (perms ([e, pi]), [pi, e; e, pi])
282%!assert (perms ([pi, e]), [e, pi; pi, e])
283%!assert (perms ([1 2 3]), [3 2 1; 3 1 2; 2 3 1; 2 1 3; 1 3 2; 1 2 3])
284%!assert (sortrows (perms (1:5)), sortrows (perms ([2 5 4 1 3]')))
285%!assert (perms ("abc"), char ("cba", "cab", "bca", "bac", "acb", "abc"))
286%!assert (sortrows (perms ("fobar")), unique (perms ("fobar"), "rows"))
287%!assert (unique (perms (1:5)(:))', 1:5)
288%!assert (perms (int8 (1:4)), int8 (perms (1:4)))
289
290%!assert (sortrows (perms ("abb", "unique")), ["abb"; "bab"; "bba"])
291%!assert (size (perms ([1 1 1 1 2 2 2 3 3], "unique")), [1260 9])
292%!assert (size (perms (int8([1 1 1 1 1 2 2 2 2 3 3 3]), "unique")), [27720 12])
293
294## Should work for any array type, such as cells and structs,
295## and not only for numeric data.
296
297%!assert <*52431> (perms ({1}), {1})
298%!assert <*52431> (perms ({0.1, "foo"}), {"foo", 0.1; 0.1, "foo"})
299%!assert <*52431> (perms ({"foo", 0.1}), {0.1, "foo"; "foo", 0.1})
300%!assert <*52431> (perms ({"foo"; 0.1}), {0.1, "foo"; "foo", 0.1})
301%!assert <*52431> (perms ({0.1; "foo"}), {"foo", 0.1; 0.1, "foo"})
302%!assert <*52431> (perms ({"foo", "bar"}), {"bar", "foo"; "foo", "bar"})
303%!assert <*52431> (perms ({"bar", "foo"}), {"foo", "bar"; "bar", "foo"})
304%!
305%!assert <*52431> (perms (struct ()), struct ())
306%!assert <*52431> (perms (struct ("foo", {1, 2})),
307%! struct ("foo", {2, 1; 1, 2}))
308%!assert <*52431> (perms (struct ("foo", {1, 2}, "bar", {3, 4})),
309%! struct ("foo", {2, 1; 1, 2}, "bar", {4, 3; 3, 4}))
310
311## Also sort logical input with order dependent on the input order and
312## not their values.
313
314%!assert <*52431> (perms (logical ([1 0])), logical ([0 1;, 1 0]))
315%!assert <*52431> (perms (logical ([0 1])), logical ([1 0; 0 1]))
316%!assert <*52431> (perms (logical ([0 1 0])),
317%! logical ([0 1 0; 0 0 1; 1 0 0; 1 0 0; 0 0 1; 0 1 0]))
318%!assert <*52431> (perms (logical ([0 1 1])),
319%! logical ([1 1 0; 1 0 1; 1 1 0; 1 0 1; 0 1 1; 0 1 1]))
320
321%!assert <*52432> (perms ([]), reshape ([], 1, 0))
322%!assert <*52432> (perms (single ([])), reshape (single ([]), 1, 0))
323%!assert <*52432> (perms (int8 ([])), reshape (int8 ([]), 1, 0))
324%!assert <*52432> (perms ({}), cell (1, 0))
325
326%!test <*52432>
327%! s = struct ();
328%! s(1) = [];
329%! assert (perms (reshape (s, 0, 0)), reshape (s, 1, 0));
330%! assert (perms (reshape (s, 0, 1)), reshape (s, 1, 0));
331
332## test if "unique" works also for cell arrays
333%!assert <*63965> (perms ({"foo"; "foo"}, "unique"), {"foo", "foo"})
334%!assert <*63965> (perms ({"foo", "foo", "bar"}, "unique"), ...
335%! {"bar", "foo", "foo";
336%! "foo", "bar", "foo";
337%! "foo", "foo", "bar"})
338
339## Test input validation
340%!error <Invalid call> perms ()
341%!error <option must be the string "unique"> perms (1:5, "foobar")
342%!error <option must be the string "unique"> perms (1:5, {"foo"})
343
344*/
345
346OCTAVE_END_NAMESPACE (octave)
N Dimensional Array with copy-on-write semantics.
Definition Array.h:130
octave_idx_type cols() const
Definition Array.h:473
const T * data() const
Size of the specified dimension.
Definition Array.h:665
octave_idx_type numel() const
Number of elements in the array.
Definition Array.h:418
Vector representing the dimensions (size) of an Array.
Definition dim-vector.h:90
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:1078
void error(const char *fmt,...)
Definition error.cc:1003
#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