Inversion multidimensionnelle


23

Étant donné un tableau orthogonal à N dimensions (non irrégulier) d'entiers non négatifs et une indication des dimensions à inverser, renvoyez le tableau mais inversé le long de ces dimensions. L'indication peut être donnée sous la forme d'une liste booléenne de longueur N ou d'une liste d'un sous-ensemble des N premières dimensions indexées de 0 ou 1.

Veuillez indiquer vos formats d'entrée. Les explications du code sont très appréciées.

Exemple guidé

On nous donne le tableau 3D à 2 couches, 3 rangées et 4 colonnes

[[[ 1, 2, 3, 4],
  [ 5, 6, 7, 8],
  [ 9,10,11,12]],

 [[13,14,15,16],
  [17,18,19,20],
  [21,22,23,24]]]

et l'un des

[true,false,true](Liste booléenne)
[0,2](liste indexée 0)
[1,3](liste indexée 1)

Nous devons inverser l'ordre des première et dernière dimensions, c'est-à-dire les couches et les éléments des lignes (les colonnes), mais pas les lignes de chaque couche. Tout d'abord (l'ordre réel dans lequel vous le faites n'a pas d'importance), nous inversons l'ordre des couches:

[[[13,14,15,16],
  [17,18,19,20],
  [21,22,23,24]],

 [[ 1, 2, 3, 4],
  [ 5, 6, 7, 8],
  [ 9,10,11,12]]]

puis on inverse l'ordre des éléments de chaque ligne:

[[[16,15,14,13],
  [20,19,18,17],
  [24,23,22,21]],

 [[ 4, 3, 2, 1],
  [ 8, 7, 6, 5],
  [12,11,10, 9]]]

Cas de test

[[[1,2,3,4],[5,6,7,8],[9,10,11,12]],[[13,14,15,16],[17,18,19,20],[21,22,23,24]]]
[true,false,true]/ [0,2]/ [1,3]
 ↓ 
[[[16,15,14,13],[20,19,18,17],[24,23,22,21]],[[4,3,2,1],[8,7,6,5],[12,11,10,9]]]


[[1,2,3],[4,5,6]]
[true,false]/ [0]/ [1]
 ↓
[[4,5,6],[1,2,3]]


[[1],[4]]
[true,false]/ [0]/ [1]
 ↓
[[4],[1]]


[[7]]
[true,true]/ [0,1]/ [1,2]
 ↓
[[7]]


[1,2,3,4,5,6,7]
[true]/ [0]/ [1]
 ↓
[7,6,5,4,3,2,1]


[]
[true]/ [0]/ [1]
 ↓
[]


[[],[]]
[false,false]/ []/ []
 ↓
[[],[]]


[[[[3,1,4,1],[5,9,2,6]],[[5,3,5,8],[9,7,9,3]]],[[[2,3,8,4],[6,2,6,4]],[[3,3,8,3],[2,7,9,5]]]]
[true,false,true,true]/ [0,2,3]/ [1,3,4]
 ↓
[[[[4,6,2,6],[4,8,3,2]],[[5,9,7,2],[3,8,3,3]]],[[[6,2,9,5],[1,4,1,3]],[[3,9,7,9],[8,5,3,5]]]]


[[[[3,1,4,1],[5,9,2,6]],[[5,3,5,8],[9,7,9,3]]],[[[2,3,8,4],[6,2,6,4]],[[3,3,8,3],[2,7,9,5]]]]
[false,true,false,false]/ [1]/ [2]
 ↓
[[[[5,3,5,8],[9,7,9,3]],[[3,1,4,1],[5,9,2,6]]],[[[3,3,8,3],[2,7,9,5]],[[2,3,8,4],[6,2,6,4]]]]


[[[[3,1,4,1],[5,9,2,6]],[[5,3,5,8],[9,7,9,3]]],[[[2,3,8,4],[6,2,6,4]],[[3,3,8,3],[2,7,9,5]]]]
[false,false,false,false]/ []/ []
 ↓
[[[[3,1,4,1],[5,9,2,6]],[[5,3,5,8],[9,7,9,3]]],[[[2,3,8,4],[6,2,6,4]],[[3,3,8,3],[2,7,9,5]]]]


J'ai l'impression que la partie la plus difficile dans la plupart des langages à typage statique sera de jouer avec les signatures de type impliquées.
Οurous

@ Οurous Comment ces langues gèrent-elles normalement les données de tableaux arbitraires?
Adám

1
il y a trois cas pour une utilisation "normale" telle que je la vois: ne se soucier que d'un niveau du tableau (par exemple: reversefonctionne sur des tableaux arbitraires mais ne se soucie que du premier niveau), des génériques ou des classes récursives (classes de type / objet selon la fonction ou POO, mais cas d'utilisation similaire). Les deux derniers sont généralement beaucoup plus verbeux.
2018 22urous

Pouvons-nous stocker la matrice sous forme de tableaux de pointeurs vers des pointeurs (en C ou asm), au lieu de tableaux multidimensionnels appropriés où tout est contigu en mémoire? Je suis à peu près sûr que tous les langages normaux de niveau supérieur / à typage dynamique avec imbrication arbitraire de listes traitent déjà les choses comme des listes de listes, pas des matrices, donc je vais supposer que tout va bien.
Peter Cordes

@PeterCordes Bien sûr, allez-y.
Adám

Réponses:


8

APL (Dyalog) , 20 9 octets

⊃{⌽[⍺]⍵}/

Essayez-le en ligne!

Comment?

/ - réduire - prendre l'élément le plus à droite dans l'entrée (le tableau) et appliquer la fonction avec l'élément gauche suivant comme argument gauche

{⌽[⍺]⍵}- inverser dans la dimension left argument( )

- aplatir le tableau fermé



8

JavaScript (Node.js) , 58 55 53 45 octets

8 octets enregistrés grâce à @Shaggy

Prend l'entrée comme (indications)(array), où les indications sont une liste booléenne.

f=([r,...b])=>a=>1/r?a.sort(_=>r).map(f(b)):a

Essayez-le en ligne!

Commenté

f = (                // f is a function taking:
  [r,                //   r = next 'reverse' Boolean flag
      ...b]          //   b = array of remaining flags
) =>                 // and returning an anonymous function taking:
  a =>               //   a = array (or sub-array) to process, or atomic element
    1 / r ?          // if r is defined:
      a.sort(_ => r) //   reverse a if r = 1; leave it unchanged otherwise
      .map(f(b))     //   for each element in the resulting array: do a recursive call,
                     //   using f to generate a new callback function for the next flag
    :                // else:
      a              //   a must be an atomic element and is simply left unchanged

Le rsimple fait d' utiliser inplace of r||-1 semble fonctionner .
Shaggy

Ça f=([r,...b])=>a=>1/r?a.sort(_=>r).map(f(b)):amarcherait? Sur mon téléphone, je ne peux donc pas tester correctement.
Shaggy

@Shaggy Nice! J'aime ce flux de traitement plutôt inhabituel.
Arnauld


5

Gelée , 8 octets

”€ẋ”ṚpFv

Prend une liste de dimensions indexée 0.

Essayez-le en ligne!

Comment ça marche

”€ẋ”ṚpFv  Main link. Left arg: D (dimensions, 0-based), Right arg: A (array)

”€ẋ       Repeat '€' d times, for each d in D.
   ”Ṛp    Perform Cartesian product of ['Ṛ'] and each string of '€'s, prepending a
          'Ṛ' to each string of '€'s.
      F   Flatten the result.
          If, e.g., D = [0,2,4], we build the string "ṚṚ€€Ṛ€€€€".
       v  Eval the resulting string, using A as left argument.

1
C'est horrible. Très agréable!
Adám

5

R , 80 78 77 octets

Créez l'appel à l'extracteur de R [en créant une liste de séquences inversées où indiqué. Ils contiennent en fait des zéros, qui sont ignorés en silence. Le drop=Fest nécessaire pour empêcher la chute par défaut des dimensions de R. Nous avons besoin de l' revappel à l'indicateur inverse de dimension, en raison de la façon dont R remplit les tableaux.

-2 merci @Giuseppe

-1 en utilisant l'affectation en ligne.

function(x,a,d=dim(x))do.call("[",c(list(x),Map(seq,r<-d*rev(a),d-r),drop=F))

Essayez-le en ligne!

Mention honorable à @JayCe qui a proposé une variation qui obtient le même résultat dans la même longueur:

function(x,a,d=dim(x))array(x[t(t(expand.grid(Map(seq,r<-d*rev(a),d-r))))],d)

Essayez-le en ligne!


1
78 octets très belle réponse!
Giuseppe

1
Réponse très profonde. Il m'a fallu un certain temps pour le comprendre complètement. J'ai essayé de le répliquer sans utiliser do.call- il est plus long à 83 octets, en postant toujours ceci ici comme commentaire pour référence: TIO
JayCe

Eh bien en fait, @JayCe, votre excellente réponse peut également être jouée à 78 octets!
J.Doe

5

Haskell, 120 119 octets

la fonction f prend la liste N-dimensionnelle et une liste de bool comme entrée

class F r where f::[Bool]->r->r
instance F Int where f=seq
instance F r=>F[r]where f(a:y)=last(id:[reverse|a]).map(f y)

1
Vous n'avez pas besoin de parenthèses autour F r.
Ørjan Johansen

1
Lien TIO avec les cas de test et le golf 1 octet d'OJ.
Khuldraeseth na'Barya

4

05AB1E , 23 11 10 octets

'€s×'R«J.V

Essayez-le en ligne.

-12 octets grâce à @ Mr.Xcoder .

Entrez en tant que valeurs de vérité indexées 0 (c'est-à-dire [0,2,3]), qui est la première entrée.

Explication:

'€s×           # Repeat "€" the indices amount of times
               #  i.e. [0,2,3] → ["","€€","€€€"]
    'R«        # Append each with "R"
               #  i.e. ["","€€","€€€"] → ["R","€€R","€€€R"]
        J      # Join them all together
               #  i.e. ["R","€€R","€€€R"] → R€€R€€€R
         .V    # Execute string as 05AB1E code

Par exemple: si la liste d'entrée des index est [0,2,3], elle créera la chaîne suivante:

R€€R€€€R

Qui va:

    €€€R    # Reverse the items in the most inner (4th level) lists
 €€R        # Reverse the most inner (3rd level) lists themselves
            # Do nothing with the inner (2nd level) lists 
R           # Reverse the entire outer (1st level) list

Réponse originale de 23 octets:

ćURvy„ RèJ…εÿ}}„ RXèJ.V

Entrez comme boolean-list (ie [1,0,1,1]), qui est la première entrée.

Essayez-le en ligne.

Explication:

ćU                 # Pop and save the first boolean in variable `X`
  R                # Reverse the remaining boolean-list
   v    }          # Loop `y` over each of them:
     Rè           #  Take the string "R ", and index the current boolean (0 or 1) in it
    J              #  Join it together with the string of the previous iteration
    …εÿ}           #  Surround it with "ε" and "}"
          RXè     # Index variable `X` also in "R "
              J    # Join it together with the rest
.V                 # Execute string as 05AB1E code

Par exemple: si la liste d'entrées booléenne est [1,0,1,1], elle créera la chaîne suivante:

εεεR}R} }R

Qui va:

  εR}         # Reverse the items in the most inner (4th level) lists
 ε   R}       # Reverse the most inner (3rd level) lists themselves
ε       }     # Do nothing with the inner (2nd level) lists 
         R    # Reverse the entire outer (1st level) list

1
Belle réponse, mais ... euh ... est-ce que ce 11 octets fonctionnerait?
M. Xcoder du

@ Mr.Xcoder Merci! C'est en effet beaucoup plus facile. Et j'ai pu jouer au golf 1 octet de plus en ajoutant chacun, au lieu de faire précéder puis inverser.
Kevin Cruijssen

@ Mr.Xcoder Btw, pourquoi fonctionne-t-il 'x*pour répéter xn fois sans utiliser de swap, mais cela ne fonctionne pas avec '€*? .. EDIT: Seulement dans l'héritage cependant ..
Kevin Cruijssen

La version héritée est assez boguée, cela pourrait être dû au fait qu'elle est toujours analysée en tant qu'opérateur même si elle est dans un littéral char? Pas sûr d'être honnête. Dans la nouvelle version, *ne se comporte pas néanmoins de la même manière.
M. Xcoder du

3

JavaScript (Node.js) , 60 octets

Une approche différente (récursive). ne bat pas la réponse d'Arnauld ... pour l'instant ...

Prend l'entrée comme array, boolean list

f=(a,r)=>r>[]?(r[0]?a.reverse():a).map(c=>f(c,r.slice(1))):a

f=(a,r)=>r>[]?(r[0]?a.reverse():a).map(c=>f(c,r.slice(1))):a

console.log(f([[[[3,1,4,1],[5,9,2,6]],[[5,3,5,8],[9,7,9,3]]],[[[2,3,8,4],[6,2,6,4]],[[3,3,8,3],[2,7,9,5]]]],[true,false,true,true]))


3

Pyth , 15 octets

.v+jk.n*\_*L\ME

Essayez-le ici!

De façon ennuyeuse, la gestion du cas de liste de dimensions vide ne prend pas moins de 2 octets ... Je préfère utiliser ssà la place de jk.nmais: | Suppose que la liste à transformer peut être donnée en syntaxe Pyth native, sous forme de chaîne. J'ai écrit un convertisseur en syntaxe Pyth pour faciliter les tests. Dans le cas malheureux que le PO choisit de ne pas autoriser cela, un 17 octets le "réparera":

.v+jk.n*\_*L\ME\E

3

Japt , 15 14 octets

Avec une certaine inspiration de la solution d' Arnauld .

Prend les indications comme première entrée, comme un tableau booléen de 1s et 0s.

Ê?Vn@ÎãßUÅX:V

Essayez-le


Explication

                   :Implicit input of boolean array U=indications and multi-dimensional integer array V
Ê                  :Get the length of U
 ?                 :If truthy (i.e., >0)
  Vn               :  Sort V
    @ÎÃ            :   Function that gets the first element of U; 0 will leave the array untouched, 1 will reverse it.
       £           :  Map each X
        ß          :  Run the programme again with the following inputs
         UÅ        :   U with the first element removed
           X       :   X will serve as the new value of V
            :      :Else
             V     :  Just return V

3

Nettoyer , 122 112 octets

import StdEnv
class$r::[Bool]->r->r
instance$r where$_=id
instance$[r]| $r where$[a:y]=if(a)reverse id o map($y)

Essayez-le en ligne!

Une version de la réponse Haskell de Damien utilisant le système de type golfeur de Clean. Montre vraiment les similitudes importantes entre les deux langues.

Expliqué:

import StdEnv                 // import basic stuff
class $ r :: [Bool] -> r -> r // $ takes a boolean list and returns a function on r to r
instance $ r                  // instance on all types, taken when no more specific instances exist
    where $ _ = id            // return the identity function for all arguments
instance $ [r] | $ r          // instance for lists of a type which has an instance itself
    where $ [a: y]
        = if(a) reverse id    // reverse if the head of the argument is true
            o map ($ y)       // composed with the map function acting on $ applied to the tail


1

(non testé mais je pense que c'est correct. La sortie asm du compilateur ressemble à ce que j'attends. Mettra à jour si / quand je trouve le temps d'écrire un faisceau de test qui crée et imprime cette structure de données.)

GNU C ++ (portable) 148 octets

#include<algorithm>
#include<cstdint>
struct m{intptr_t d,l,a[];void R(int*r){if(*r)std::reverse(a,a+l);for(int i=0;d&&i<l;((m*)a[i++])->R(r+1));}};

GNU C ++ (int = pointeur et tombe d'une fonction non vide UB) 120 octets

#include<algorithm>
struct m{int d,l,a[],R(int*r){if(*r)std::reverse(a,a+l);for(int i=0;d&&i<l;((m*)a[i++])->R(r+1));}};

Il s'agit d'une structure de compteur de profondeur, de longueur, d'un tableau de {entiers ou pointeurs}. Au niveau inférieur de cet arbre non binaire ( depth==0), le tableau de intptr_test un tableau d'entiers. Dans les niveaux supérieurs, c'est un struct m*stocké dans intptr_t. Traversal prend un plâtre.

La R()fonction inverse est une fonction membre, car elle enregistre la déclaration d'un argument et enregistre beaucoup de p->syntaxe pour référencer les membres de la structure par rapport au thispointeur implicite .

La seule extension GNU est le membre de tableau flexible C99 pour créer une structure de taille variable , qui est prise en charge en C ++ en tant qu'extension GNU. J'aurais pu utiliser un *amembre pointant vers un tableau alloué séparément et que ce soit ISO C ++ simple. (Et cela sauverait en fait un octet sans nécessiter d'autres modifications). J'ai écrit ceci comme une implémentation de maquette / référence pour une version asm.


La version courte avec juste intdéclare également R()comme renvoyant intau lieu de void. Ces deux morceaux de piratage ne sont pas liés; ce n'est que la version "fonctionne sur au moins une implémentation".

Cela devrait fonctionner correctement sur les cibles 32 bits (où intpeut contenir un pointeur), tant que vous compilez avec gcc7 ou une version antérieure, ou désactivez les optimisations. ( gcc8 -O3suppose que l'exécution ne peut pas atteindre le bas d'une non- voidfonction car ce serait UB.) x86 gcc -m32 -O3devrait fonctionner correctement avec gcc7, comme sur Godbolt où j'ai inclus les deux versions (dans des espaces de noms différents) et une version sans fonction membre. .

Non golfé

La fonction arg,, int r[]est un tableau d'entiers 0 / non nul qui indique si une profondeur donnée doit être permutée, en commençant par le niveau le plus externe.

#include<algorithm>  // for std::reverse
#include<cstdint>    // for intptr_t.  GNU C defines __intptr_t, so we could use that...

struct m{
    __intptr_t d,l,a[];    // depth = 0 means values, >0 means pointers.
    // l = length
    //__intptr_t a[];  // flexible array member: array contiguous with the struct

    void R(int r[]) {
        if(*r)
            std::reverse(a, a+l);   // *r && std::reverse() doesn't work because it returns void.

        if(d) // recurse if this isn't the bottom depth
            for(int i=0 ; i<l ; i++)  // tree traversal
                ((m*)a[i])->R(r+1);   // with the rest of the depth list
    }

}; // struct m

Lorsque nous récurons, nous passons r+1, donc vérifier la profondeur actuelle est toujours *r.

Une version antérieure est passée rinchangée et vérifiée r[d]. Avec un membre de tableau flexible, j'avais besoin de stocker une sorte d'indicateur de dernier niveau car ce a[]n'est pas un pointeur, c'est un vrai tableau sans indirection. Mais avec un intptr_t *amembre, je ne pouvais pas simplement avoir cela nullptrpour le niveau feuille, parce que je veux que ce soit des valeurs.

Inverser le niveau actuel avant ou après la traversée de l'arbre ne devrait pas avoir d'importance. Je n'ai pas essayé de le faire pendant .

Je ne suis pas sûr que cela std::reversevaut le nombre d'octets par rapport à une boucle manuelle, surtout si je peux travailler en appelant R()chaque pointeur exactement une fois quelque part dans cette boucle. Mais seulement sid!=0


Whoa, impressionnant.
Adám

@ Adám: merci, il a joué de manière surprenante naturellement et bien après l'avoir écrit. Je suis curieux de voir ce que je peux faire en code machine x86: P
Peter Cordes

1

Mathematica, 7 octets

Reverse

Une fonction. Donnez-lui une liste imbriquée comme premier argument et la liste basée sur 1 de niveaux / dimensions à inverser comme deuxième argument. Essayez-le en ligne!

Enfin, un autre défi où Mathematica a une fonction intégrée!


couches à inverser ? Lien TIO peut-être?
Adám

@ Adám C'est-à-dire la liste des dimensions à inverser, généralement appelées niveaux dans Mathematica.
LegionMammal978
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.