Mappa un array 2D su un array 1D


90

Voglio rappresentare un array 2D con un array 1D. Una funzione passerà i due indici (x, y) e il valore da memorizzare. Questi due simboli rappresenterebbero un singolo elemento di una matrice 1D e lo imposteranno di conseguenza. So che l'array 1D deve avere la dimensione di arrayWidth × arrayHeight, ma non so come impostare ogni elemento.

Ad esempio, come faccio a distinguere (2,4,3) da (4,2,3)? Ho provato a impostare l'array come x * y, ma 2 * 4 e 4 * 2 risulterebbero nello stesso punto dell'array e ho bisogno che siano diversi.

Risposte:


163

È necessario decidere se gli elementi della matrice verranno archiviati in ordine di riga o di colonna e quindi essere coerenti al riguardo. http://en.wikipedia.org/wiki/Row-major_order

Il linguaggio C utilizza l'ordine delle righe per gli array multidimensionali

Per simulare questo con un array unidimensionale, moltiplica l'indice di riga per la larghezza e aggiungi l'indice di colonna in questo modo:

 int array[width * height];

 int SetElement(int row, int col, int value)
 {
    array[width * row + col] = value;  
 }

7
Penso che questa risposta sia più chiara, soprattutto per i principianti è meglio non scrivere le funzioni in una riga ... !! È una cattiva pratica comunque .. :)
Lipis

3
Questa risposta è utile anche per quando si dispone di un compilatore (ad es. Sistemi incorporati) che non ha un adeguato supporto per array multidimensionali
Alex Marshall

1
È incredibile quante persone possano rispondere correttamente alla stessa domanda, ma solo UNA di loro lo dice in un modo facile da capire. Questa è la risposta più semplice possibile. Tuttavia, John è l'unico a fornire effettivamente una buona risposta. Tutto il resto è spazzatura che può essere facilmente compresa solo da chi conosce già la risposta. Grazie John, per aver effettivamente parlato in inglese invece che in alieno. Questo dimostra quanto alcune persone siano pessime nell'insegnamento e come bravi insegnanti come John Knoeller sappiano come semplificare e comunicare in modo molto più efficace di chiunque altro.
user2948630

6
Sarebbe bene mostrare come invertire questa mappatura: se l'indice della matrice 1D è alpha, e l'array 2D ha dimensione Nin entrambe le direzioni con indici x, y, poi secondo @JohnKnoeller, alpha=x+N*y. Il modo per invertire questo sarebbe l'impostazione x=alpha%Ne y= (alpha-alpha%N)/N.
Tim

Vengo qui quasi tutti i giorni!
Felipe Gutierrez

23

La formula tipica per il ricalcolo degli indici di matrice 2D nell'indice di matrice 1D è

index = indexX * arrayWidth + indexY;

In alternativa puoi usare

index = indexY * arrayHeight + indexX;

(supponendo che arrayWidthsia misurato lungo l'asse X e arrayHeightlungo l'asse Y)

Naturalmente, si possono inventare molte formule diverse che forniscono mappature uniche alternative, ma normalmente non ce n'è bisogno.

Nei linguaggi C / C ++ gli array multidimensionali incorporati sono archiviati in memoria in modo che l'ultimo indice cambi il più velocemente, il che significa che per un array dichiarato come

int xy[10][10];

l'elemento xy[5][3]è immediatamente seguito da xy[5][4]in memory. Potresti anche seguire questa convenzione, scegliendo una delle due formule precedenti a seconda di quale indice (X o Y) consideri "l'ultimo" dei due.


17

Esempio: vogliamo rappresentare un array 2D di dimensioni SIZE_X e SIZE_Y. Ciò significa che avremo MAXY righe consecutive di dimensione MAXX. Quindi la funzione set è

void set_array( int x, int y, int val ) { array[ x * SIZE_Y + y ] = val; }

Il risultato sarebbe:

int get_array( int x, int y ) { return array[ x * SIZE_Y + y ]; }

1
I tuoi valori MAXXe MAXYsono denominati in modo confuso, perché i valori massimi di xe ysono rispettivamente MAXX - 1e MAXY - 1. Forse SIZE_Xe SIZE_Ypotrebbe essere migliore?
caf

3
[y * maxx + x] è l'ordine delle colonne, non l'ordine delle righe. Questo è il modo in cui funziona Matlab, ma non è il modo in cui normalmente funzionano gli array in C.
John Knoeller

@tutti: a meno che tu non mantenga i dati toccati ma puramente queste due funzioni get / set, e usano la stessa formula, puoi farlo come QUESTO o come QUELLO. (Garantito!)
imacake

Una macro potrebbe essere più appropriata qui, quindi non stai impilando chiamate di funzione non necessarie su quello che dovrebbe essere un accesso ai dati di basso livello (soprattutto perché l'indicizzazione 1d negli array pseudo-2D è a volte una tecnica di ottimizzazione.
krs013

Supponendo che il codice sia un membro della classe, questo codice verrà inserito inline. Altrimenti inline esplicito è MOLTO meglio di una macro.
Kornel Kisielewicz

7

Come altri hanno detto, mappe C in ordine di riga

   #include <stdio.h>

   int main(int argc, char **argv) {
   int i, j, k;
   int arr[5][3];
   int *arr2 = (int*)arr;

       for (k=0; k<15; k++) {
          arr2[k] = k;
          printf("arr[%d] = %2d\n", k, arr2[k]);
       }

       for (i=0; i<5; i++) {
         for (j=0; j< 3; j++) {
            printf("arr2[%d][%d] = %2d\n", i, j ,arr[i][j]);
         }
       } 
    } 

Produzione:

arr[0] =  0
arr[1] =  1
arr[2] =  2
arr[3] =  3
arr[4] =  4
arr[5] =  5
arr[6] =  6
arr[7] =  7
arr[8] =  8
arr[9] =  9
arr[10] = 10
arr[11] = 11
arr[12] = 12
arr[13] = 13
arr[14] = 14
arr2[0][0] =  0
arr2[0][1] =  1
arr2[0][2] =  2
arr2[1][0] =  3
arr2[1][1] =  4
arr2[1][2] =  5
arr2[2][0] =  6
arr2[2][1] =  7
arr2[2][2] =  8
arr2[3][0] =  9
arr2[3][1] = 10
arr2[3][2] = 11
arr2[4][0] = 12
arr2[4][1] = 13
arr2[4][2] = 14

3

utilizzando l'esempio di riga principale:

A(i,j) = a[i + j*ld]; // where ld is the leading dimension
                      // (commonly same as array dimension in i)

// matrix like notation using preprocessor hack, allows to hide indexing
#define A(i,j) A[(i) + (j)*ld]

double *A = ...;
size_t ld = ...;
A(i,j) = ...;
... = A(j,i);

1

È importante memorizzare i dati in modo che possano essere recuperati nelle lingue utilizzate. Il linguaggio C memorizza in ordine di riga maggiore (tutta la prima riga viene prima, poi tutta la seconda riga, ...) con ogni indice che va da 0 alla sua dimensione-1. Quindi l'ordine della matrice x [2] [3] è x [0] [0], x [0] [1], x [0] [2], x [1] [0], x [1] [ 1], x [1] [2]. Quindi, in linguaggio C, x [i] [j] è memorizzato nella stessa posizione di una voce di matrice unidimensionale x1dim [i * 3 + j]. Se i dati vengono archiviati in questo modo, è facile recuperarli in linguaggio C.

Fortran e MATLAB sono diversi. Memorizzano in ordine di colonna principale (tutta la prima colonna viene prima, poi tutta la seconda riga, ...) e ogni indice va da 1 alla sua dimensione. Quindi l'ordine dell'indice è il contrario di C e tutti gli indici sono maggiori di 1. Se memorizzi i dati nell'ordine del linguaggio C, FORTRAN può trovare X_C_language [i] [j] usando X_FORTRAN (j + 1, i + 1). Ad esempio, X_C_language [1] [2] è uguale a X_FORTRAN (3,2). Negli array unidimensionali, il valore dei dati è X1dim_C_language [2 * Cdim2 + 3], che è la stessa posizione di X1dim_FORTRAN (2 * Fdim1 + 3 + 1). Ricorda che Cdim2 = Fdim1 perché l'ordine degli indici è invertito.

MATLAB è lo stesso di FORTRAN. Ada è uguale a C tranne per gli indici che normalmente iniziano da 1. Qualsiasi lingua avrà gli indici in uno di quegli ordini C o FORTRAN e gli indici inizieranno da 0 o 1 e possono essere regolati di conseguenza per ottenere i dati memorizzati.

Scusa se questa spiegazione è confusa, ma penso che sia accurata e importante che un programmatore lo sappia.


-2

Dovresti essere in grado di accedere all'array 2d con un semplice puntatore in posizione. L'array [x] [y] sarà disposto nel puntatore come p [0x * width + 0y] [0x * width + 1y] ... [0x * width + n-1y] [1x * width + 0y] ecc. .

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.