Mercurial > octave
changeset 33004:9c9f4df5e4c3
maint: Backed out changeset c9d869d9c989.
Newer changeset is available on bug #65244.
author | Rik <rik@octave.org> |
---|---|
date | Sat, 10 Feb 2024 22:27:19 -0800 |
parents | c9d869d9c989 |
children | 349b4adf686a |
files | libinterp/corefcn/perms.cc |
diffstat | 1 files changed, 82 insertions(+), 21 deletions(-) [+] |
line wrap: on
line diff
--- a/libinterp/corefcn/perms.cc Sat Feb 03 11:04:44 2024 +0800 +++ b/libinterp/corefcn/perms.cc Sat Feb 10 22:27:19 2024 -0800 @@ -28,7 +28,6 @@ #endif #include <algorithm> -#include <numeric> #include "defun.h" #include "error.h" @@ -49,42 +48,103 @@ // // Use C++ template to cater for the different octave array classes. // - template <typename T> static inline Array<T> -GetPerms (const Array<T>& ar_in, bool uniq_v = false) +GetPerms (const Array<T>& ar_in, bool uniq_v, bool do_sort = false) { octave_idx_type m = ar_in.numel (); double nr = Factorial (m); // Setup index vector filled from 0..m-1 OCTAVE_LOCAL_BUFFER (int, myvidx, m); - std::iota (&myvidx[0], &myvidx[m], 0); + for (int i = 0; i < m; i++) + myvidx[i] = i; + + // Interim array to sort ar_in for octave sort order and to implement + // "unique". + Array<T> ar (ar_in); + + if (uniq_v) + { + ar = ar.sort (ar.dims () (1) > ar.dims () (0) ? 1 : 0, ASCENDING); + const T *Ar = ar.data (); + int ctr = 0; + int N_el = 1; + + // Number of same elements where we need to remove permutations + // Number of unique permutations is n! / (n_el1! * n_el2! * ...) + for (octave_idx_type i = 0; i < m - 1; i++) + { + myvidx[i] = ctr; + if (Ar[i + 1] != Ar[i]) + { + nr /= Factorial (N_el); + ctr = i + 1; // index of next different element + N_el = 1; + } + else + N_el++; + } + myvidx[m - 1] = ctr; + nr /= Factorial (N_el); + } + else if (do_sort) + { + ar = ar.sort (ar.dims () (1) > ar.dims () (0) ? 1 : 0, ASCENDING); + } + + // Sort vector indices for inverse lexicographic order later. + std::sort (myvidx, myvidx + m, std::greater<int> ()); + + const T *Ar = ar.data (); + + // Set up result array + octave_idx_type n = static_cast<octave_idx_type> (nr); + Array<T> res (dim_vector (n, m)); + T *Res = res.rwdata (); + + // Do the actual job + octave_idx_type i = 0; + std::sort (myvidx, myvidx + m, std::greater<int> ()); + do + { + for (octave_idx_type j = 0; j < m; j++) + Res[i + j * n] = Ar[myvidx[j]]; + i++; + } + while (std::next_permutation (myvidx, myvidx + m, std::greater<int> ())); + + return res; +} + +// Template for non-numerical types (e.g. Cell) without sorting. +// The C++ compiler complains as the provided type octave_value does not +// support the test of equality via '==' in the above template. + +template <typename T> +static inline Array<T> +GetPermsNoSort (const Array<T>& ar_in, bool uniq_v = false) +{ + octave_idx_type m = ar_in.numel (); + double nr = Factorial (m); + + // Setup index vector filled from 0..m-1 + OCTAVE_LOCAL_BUFFER (int, myvidx, m); + for (int i = 0; i < m; i++) + myvidx[i] = i; const T *Ar = ar_in.data (); if (uniq_v) { - // Mutual Comparison is used to detect duplicated values. - // Using sort would be possible for numerical values and be of - // O(n*log (n)) complexity instead of O(n * (n / 2)). But sort - // is not supported for the octave_value container (structs/cells). - // As the perms element size n must very small, any potential gains - // would be minimal as nearly all CPU time is spent to create the - // actual permutations. - + // Mutual Comparison using is_equal to detect duplicated values int N_el = 1; // Number of unique permutations is n! / (n_el1! * n_el2! * ...) for (octave_idx_type i = 0; i < m - 1; i++) { for (octave_idx_type j = i + 1; j < m; j++) { - bool isequal; - if constexpr (std::is_same<T, octave_value>::value) - isequal = Ar[i].is_equal (Ar[j]); - else - isequal = (Ar[i] == Ar[j]); - if (myvidx[j] > myvidx[i] && isequal) + if (myvidx[j] > myvidx[i] && Ar[i].is_equal (Ar[j])) { myvidx[j] = myvidx[i]; // not yet processed... N_el++; @@ -199,7 +259,8 @@ || args (0).iscell () || args (0).is_scalar_type () || args (0).isstruct ())) { - error ("perms: input V must be a matrix, range, cell array, struct, or scalar."); + error ("perms: INPUT must be a matrix, a range, a cell array, " + "a struct or a scalar."); } std::string clname = args (0).class_name (); @@ -230,7 +291,7 @@ else if (clname == "uint64") retval = GetPerms<octave_uint64> (args (0).uint64_array_value (), uniq_v); else if (clname == "cell") - retval = GetPerms<octave_value> (args (0).cell_value (), uniq_v); + retval = GetPermsNoSort<octave_value> (args (0).cell_value (), uniq_v); else if (clname == "struct") { const octave_map map_in (args (0).map_value ()); @@ -251,7 +312,7 @@ { for (octave_idx_type i = 0; i < fn.numel (); i++) { - out.assign (fn (i), GetPerms<octave_value> + out.assign (fn (i), GetPermsNoSort<octave_value> (map_in.contents (fn (i)), uniq_v)); } }