Inversione multidimensionale


23

Data una matrice ortogonale N (non sfilacciata) di numeri interi non negativi e un'indicazione di quali dimensioni invertire, restituisce la matrice ma invertita lungo quelle dimensioni. L'indicazione può essere data come un elenco booleano di lunghezza N o un elenco di un sottoinsieme delle prime N dimensioni indicizzate da 0 o 1.

Si prega di indicare i formati di input. Le spiegazioni del codice sono molto apprezzate.

Esempio da seguire

Ci viene fornita la matrice 3D a 2 colonne e 3 file a 4 righe

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

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

e uno di

[true,false,true](Elenco booleano)
[0,2]( Elenco 0 indicizzato)
[1,3]( Elenco 1 indicizzato)

Dobbiamo invertire l'ordine della prima e dell'ultima dimensione, ovvero i livelli e gli elementi delle righe (le colonne), ma non le righe di ciascun livello. Innanzitutto (l'ordine reale in cui lo fai non importa) invertiamo l'ordine dei livelli:

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

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

e quindi invertiamo l'ordine degli elementi di ogni riga:

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

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

Casi 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]]]]


Sento che la parte più difficile nella maggior parte dei linguaggi tipicamente statici sarà giocare a golf con le firme dei tipi coinvolti.
Οurous

@ Οurous In che modo tali lingue gestiscono normalmente dati di array arbitrari?
Adám,

1
ci sono tre casi per un uso "normale" come la vedo io: preoccuparsi solo di un livello dell'array (ad esempio: reversefunziona su array arbitrari ma si preoccupa solo del primo livello), generici o classi ricorsive (classi di tipo / oggetto a seconda della funzione o OOP, ma caso d'uso simile). Gli ultimi due sono generalmente molto più dettagliati.
1818

Possiamo memorizzare la matrice come matrici di puntatori a puntatori (in C o asm), invece di matrici multidimensionali appropriate in cui tutto è contiguo nella memoria? Sono abbastanza sicuro che tutti i normali linguaggi di livello superiore / tipicamente dinamici con annidamento arbitrario di elenchi già trattano le cose come elenchi di elenchi, non matrici, quindi suppongo che vada bene.
Peter Cordes,

@PeterCordes Certo, vai avanti.
Adám,

Risposte:


8

APL (Dyalog) , 20 9 byte

⊃{⌽[⍺]⍵}/

Provalo online!

Come?

/ - ridurre - prende l'elemento più a destra nell'input (l'array) e applica la funzione con l'elemento sinistro successivo come argomento sinistro

{⌽[⍺]⍵}- inverti nella dimensione left argument( )

- appiattire l'array incluso



8

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

8 byte salvati grazie a @Shaggy

Accetta input come (indications)(array), dove indicazioni è un elenco booleano.

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

Provalo online!

Commentate

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

Basta usare al rposto di r||-1 sembra funzionare .
Shaggy

Funzionerebbe f=([r,...b])=>a=>1/r?a.sort(_=>r).map(f(b)):a? Sul mio telefono, quindi non posso testare correttamente.
Shaggy

@Shaggy Nice! Mi piace questo flusso di elaborazione piuttosto insolito.
Arnauld,


5

Gelatina , 8 byte

”€ẋ”ṚpFv

Accetta un elenco di dimensioni con indice 0.

Provalo online!

Come funziona

”€ẋ”Ṛ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
È orribile. Molto bella!
Adám,

5

R , 80 78 77 byte

Crea la chiamata all'estrattore di R. [ creando un elenco di sequenze invertite dove indicato. In realtà contengono zeri, che vengono silenziosamente ignorati. Ildrop=F è necessario per evitare cadute di default di R di dimensioni. Abbiamo bisogno della revchiamata all'indicatore di inversione della dimensione, a causa del modo in cui R riempie le matrici.

-2 grazie @Giuseppe

-1 utilizzando l'assegnazione in linea.

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

Provalo online!

Menzione d'onore a @JayCe che ha proposto una variante che ottiene lo stesso risultato nella stessa lunghezza:

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

Provalo online!


1
78 byte risposta molto bella!
Giuseppe,

1
Risposta molto profonda. Mi ci è voluto un po 'per capirlo completamente. Ho provato a replicarlo senza usare do.call- è più lungo a 83 byte, pubblicando ancora questo qui come commento per riferimento: TIO
JayCe

Bene in realtà, @JayCe, la tua grande risposta può anche essere giocata a 78 byte!
J.Doe,

5

Haskell, 120 119 byte

la funzione f accetta l'elenco N-dimensionale e un elenco di valori booleari come input

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
Non hai bisogno di parentesi in giro F r.
Ørjan Johansen,

1
Collegamento TIO con casi di test e golf a 1 byte di OJ.
Khuldraeseth na'Barya,

4

05AB1E , 23 11 10 byte

'€s×'R«J.V

Provalo online.

-12 byte grazie a @ Mr.Xcoder .

Immettere come valori di verità indicizzati 0 (ovvero [0,2,3]), che è il primo input.

Spiegazione:

'€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

Ad esempio: se l'elenco di input degli indici è [0,2,3], creerà la seguente stringa:

R€€R€€€R

Quale:

    €€€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

Risposta originale a 23 byte:

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

Immettere come elenco booleano (ovvero [1,0,1,1]), che è il primo input.

Provalo online.

Spiegazione:

ć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

Ad esempio: Se l'elenco di input booleano è [1,0,1,1], creerà la seguente stringa:

εεεR}R} }R

Quale:

  ε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
Bella risposta, ma ... uhm ... questo 11 byte funzionerebbe?
Mr. Xcoder,

@ Mr.Xcoder Grazie! È davvero molto più semplice. E sono stato in grado di giocare a golf 1 byte in più aggiungendo ciascuno, invece di anteporre e quindi invertire.
Kevin Cruijssen,

@ Mr.Xcoder A proposito, perché 'x*funziona ripetere ripetutamente xn volte senza usare un swap, ma non funziona con '€*? .. MODIFICA: Solo nell'eredità però ..
Kevin Cruijssen

La versione legacy è piuttosto difettosa, potrebbe essere dovuta al fatto che è ancora analizzata come operatore anche se è in caratteri letterali? Non sono sicuro di essere onesto. Nella nuova versione, *non si comporta comunque allo stesso modo.
Mr. Xcoder,

3

JavaScript (Node.js) , 60 byte

Un approccio diverso (ricorsivo). non batte la risposta di Arnauld ... ancora ...

Prende l'input come 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 byte

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

Provalo qui!

Stranamente, la gestione del caso dell'elenco di dimensioni vuoto richiede non meno di 2 byte ... Preferirei usare ssal posto di jk.nma: | Presuppone che l'elenco da trasformare possa essere fornito nella sintassi Pyth nativa, come una stringa. Ho scritto un convertitore per la sintassi di Pyth per semplificare i test. Nel malaugurato caso in cui l'OP decida di non consentire ciò, un 17-byte lo "risolverà":

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

3

Japt , 15 14 byte

Con qualche ispirazione dalla soluzione di Arnauld .

Prende le indicazioni come primo input, come una matrice booleana di 1s e 0s.

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

Provalo


Spiegazione

                   :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

Pulito , 122 112 byte

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

Provalo online!

Una versione della risposta Haskell di Damien usando il sistema di tipo golfista di Clean. Mostra davvero le ampie somiglianze tra le due lingue.

Ha spiegato:

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 testato ma penso corretta. sguardi di output del compilatore ASM come quello che mi aspetto. Aggiornerà se / quando trovo il tempo per scrivere un test harness che crea e stampa questa struttura dati.)

GNU C ++ (portatile) 148 byte

#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 = puntatore e cade da una funzione non nulla UB) 120 byte

#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));}};

Questa è una struttura di contatore di profondità, lunghezza, matrice di {numeri interi o puntatori}. Nel livello inferiore di questo albero non binario ( depth==0), l'array di intptr_tè un array di numeri interi. Nei livelli più alti, è struct m*memorizzato inintptr_t . Traversal prende un cast.

La R()funzione inversa è una funzione membro perché salva la dichiarazione di un arg e salva molta p->sintassi per fare riferimento ai membri della struttura rispetto al thispuntatore implicito .

L'unica estensione GNU è la membro array flessibile C99 per creare una struttura di dimensioni variabili , che è supportata in C ++ come estensione GNU. Avrei potuto usare a*a membro che puntava ad un array allocato separatamente e avere questo ISO C ++ semplice. (E questo in realtà salverebbe un byte senza richiedere altre modifiche). Ho scritto questo come un'implementazione di mockup / riferimento per una versione asm.


La versione più corta con solo int dichiara anche R()di restituire intinvece divoid . Questi due frammenti di pirateria informatica non sono correlati; questa è solo la versione "funziona su almeno un'implementazione".

Dovrebbe funzionare bene su target a 32 bit (dove intpuò contenere un puntatore), purché si compili con gcc7 o precedente o si disabiliti le ottimizzazioni. ( gcc8 -O3presuppone che l'esecuzione non possa raggiungere il fondo di un non-void funzione perché sarebbe UB.) x86 gcc -m32 -O3dovrebbe funzionare bene con gcc7, come su Godbolt cui ho incluso entrambe le versioni (in spazi dei nomi diversi) e una versione non membro .

Ungolfed

La funzione arg,, int r[]è una matrice di numeri interi 0 / diversi da zero che indicano se una data profondità deve essere scambiata, a partire dal livello più esterno.

#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

Quando ricattiamo, passiamo r+1 , quindi controllare sempre la profondità attuale*r .

Una versione precedente è appena passata rinvariata e controllata r[d]. Con un membro dell'array flessibile, avevo bisogno di archiviare una sorta di indicatore dell'ultimo livello perché a[]non è un puntatore, è un array vero e senza riferimento indiretto. Ma con un intptr_t *amembro, non potevo proprio averlonullptr per il livello foglia, perché voglio che siano valori.

L'inversione del livello corrente prima o dopo l'attraversamento dell'albero non dovrebbe importare. Non ho provato a farlo durante .

Non sono sicuro che std::reversevalga il conteggio dei byte rispetto a un ciclo manuale, specialmente se posso lavorare per chiamare R()ciascun puntatore esattamente una volta da qualche parte all'interno di quel ciclo. Ma solo sed!=0


Whoa, impressionante.
Adám,

@Adám: grazie, si è rivelato sorprendentemente naturale e ben dopo averlo scritto. Sono curioso di vedere cosa posso fare nel codice macchina x86: P
Peter Cordes,

1

Mathematica, 7 byte

Reverse

Funzione. Dagli un elenco nidificato come primo argomento e l'elenco basato su 1 di livelli / dimensioni da invertire come secondo argomento. Provalo online!

Finalmente, un'altra sfida in cui Mathematica ha incorporato!


strati da invertire ? Collegamento TIO forse?
Adám,

@ Adám Cioè, l'elenco delle dimensioni da invertire, generalmente indicato come livelli in Mathematica.
LegionMammal978,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.