Ánh xạ mảng 2D lên mảng 1D


90

Tôi muốn biểu diễn một mảng 2D với một mảng 1D. Một hàm sẽ chuyển hai chỉ báo (x, y) và giá trị để lưu trữ. Hai chỉ báo này sẽ đại diện cho một phần tử duy nhất của mảng 1D và đặt nó cho phù hợp. Tôi biết mảng 1D cần có kích thước là arrayWidth × arrayHeight, nhưng tôi không biết cách đặt từng phần tử.

Ví dụ, làm cách nào để phân biệt (2,4,3) với (4,2,3)? Tôi đã thử đặt mảng là x * y, nhưng 2 * 4 và 4 * 2 sẽ dẫn đến cùng một vị trí trong mảng và tôi cần chúng khác nhau.

Câu trả lời:


163

Bạn cần quyết định xem các phần tử mảng sẽ được lưu trữ theo thứ tự hàng hay thứ tự cột và sau đó nhất quán về nó. http://en.wikipedia.org/wiki/Row-major_order

Ngôn ngữ C sử dụng thứ tự hàng cho mảng Đa chiều

Để mô phỏng điều này với mảng một chiều, bạn nhân chỉ số hàng với chiều rộng và thêm chỉ số cột như vậy:

 int array[width * height];

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

7
Tôi nghĩ câu trả lời này rõ ràng hơn, đặc biệt là đối với người mới bắt đầu, tốt hơn là không nên viết các hàm trong một dòng ... !! Dù sao thì đó cũng là một thực hành tồi .. :)
Lipis

3
Câu trả lời này cũng hữu ích cho khi bạn có một trình biên dịch (ví dụ như hệ thống nhúng) mà không có hỗ trợ mảng đa chiều thích hợp
Alex Marshall

1
Thật đáng kinh ngạc là có bao nhiêu người có thể trả lời đúng cùng một câu hỏi, nhưng chỉ MỘT người trong số họ nói theo cách dễ hiểu. Đây là một câu trả lời đơn giản như nó nhận được. Tuy nhiên, John là người duy nhất thực sự đưa ra câu trả lời xác đáng cho điều đó. Tất cả những thứ còn lại là rác rưởi mà chỉ những người đã biết câu trả lời mới có thể hiểu được. Cảm ơn John, vì đã thực sự nói bằng tiếng Anh thay vì người ngoài hành tinh. Chỉ để cho thấy một số người dạy kém như thế nào, và những giáo viên giỏi như John Knoeller biết cách đơn giản hóa và giao tiếp hiệu quả hơn nhiều so với những người khác.
user2948630

6
Sẽ rất tốt nếu chỉ ra cách đảo ngược ánh xạ này: nếu chỉ số của mảng 1D alphavà mảng 2D có thứ nguyên Ntheo cả hai hướng với các chỉ số x, y, thì theo @JohnKnoeller , alpha=x+N*y. Cách để đảo ngược điều này sẽ là thiết lập x=alpha%Ny= (alpha-alpha%N)/N.
Tim

Tôi đến đây hầu như mỗi ngày!
Felipe Gutierrez

23

Công thức điển hình để tính toán lại các chỉ số mảng 2D thành chỉ số mảng 1D là

index = indexX * arrayWidth + indexY;

Ngoài ra bạn có thể sử dụng

index = indexY * arrayHeight + indexX;

(giả sử rằng nó arrayWidthđược đo dọc theo trục X và arrayHeightdọc theo trục Y)

Tất nhiên, người ta có thể nghĩ ra nhiều công thức khác nhau để cung cấp các ánh xạ duy nhất thay thế, nhưng thông thường thì không cần.

Trong ngôn ngữ C / C ++, mảng đa chiều tích hợp sẵn được lưu trữ trong bộ nhớ để chỉ mục cuối cùng thay đổi nhanh nhất, nghĩa là đối với mảng được khai báo là

int xy[10][10];

xy[5][3]ngay sau đó là phần tử xy[5][4]trong bộ nhớ. Bạn cũng có thể muốn tuân theo quy ước đó, chọn một trong hai công thức trên tùy thuộc vào chỉ số nào (X hoặc Y) mà bạn coi là "cuối cùng" trong hai công thức.


17

Ví dụ: chúng ta muốn biểu diễn một mảng 2D có kích thước SIZE_X và SIZE_Y. Điều đó có nghĩa là chúng ta sẽ có MAXY hàng liên tiếp có kích thước MAXX. Do đó hàm thiết lập là

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

Nhận được sẽ là:

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

1
Giá trị MAXXvà của bạn MAXYđược đặt tên một cách khó hiểu vì giá trị lớn nhất của xyMAXX - 1MAXY - 1tương ứng. Có lẽ SIZE_XSIZE_Ycó thể tốt hơn?
caf

3
[y * maxx + x] là thứ tự cột, không phải thứ tự hàng. Đây là cách matlab hoạt động, nhưng không phải là cách các mảng thường làm việc trong C.
John Knoeller

@everyone: trừ khi bạn giữ dữ liệu được chạm vào nhưng hoàn toàn là hai hàm get / set này và chúng sử dụng cùng một công thức, bạn có thể làm như thế này hoặc như thế. (Được đảm bảo!)
imacake

Một macro có thể thích hợp hơn ở đây để bạn không phải xếp chồng các lệnh gọi hàm không cần thiết vào thứ được cho là quyền truy cập dữ liệu cấp thấp (đặc biệt vì lập chỉ mục 1d trong mảng giả 2d đôi khi là một kỹ thuật tối ưu hóa.
krs013

Giả sử rằng mã là một thành viên trong lớp, mã này sẽ được nội tuyến. Nếu không inline rõ ràng là NHIÊU tốt hơn so với một macro.
Kornel Kisielewicz

7

Như người khác đã nói bản đồ C theo thứ tự hàng

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

Đầu ra:

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

sử dụng ví dụ chính về hàng:

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

Điều quan trọng là phải lưu trữ dữ liệu theo cách có thể được truy xuất bằng các ngôn ngữ được sử dụng. Ngôn ngữ C lưu trữ theo thứ tự hàng chính (tất cả hàng đầu tiên đứng trước, sau đó đến hàng thứ hai, ...) với mọi chỉ mục chạy từ 0 đến thứ nguyên-1. Vậy thứ tự của mảng x [2] [3] là x [0] [0], x [0] [1], x [0] [2], x [1] [0], x [1] [ 1], x [1] [2]. Vì vậy, trong ngôn ngữ C, x [i] [j] được lưu trữ ở cùng một nơi như một mục nhập mảng 1 chiều x1dim [i * 3 + j]. Nếu dữ liệu được lưu trữ theo cách đó, rất dễ dàng truy xuất bằng ngôn ngữ C.

Fortran và MATLAB là khác nhau. Chúng lưu trữ theo thứ tự chính của cột (tất cả cột đầu tiên đứng trước, sau đó đến hàng thứ hai, ...) và mọi chỉ mục chạy từ 1 đến thứ nguyên của nó. Vì vậy, thứ tự chỉ số là đảo ngược của C và tất cả các chỉ số đều lớn hơn 1. Nếu bạn lưu trữ dữ liệu theo thứ tự ngôn ngữ C, FORTRAN có thể tìm thấy X_C_language [i] [j] bằng cách sử dụng X_FORTRAN (j + 1, i + 1). Ví dụ: X_C_language [1] [2] bằng X_FORTRAN (3,2). Trong mảng 1 chiều, giá trị dữ liệu đó ở X1dim_C_language [2 * Cdim2 + 3], cùng vị trí với X1dim_FORTRAN (2 * Fdim1 + 3 + 1). Hãy nhớ rằng Cdim2 = Fdim1 vì thứ tự của các chỉ số bị đảo ngược.

MATLAB cũng giống như FORTRAN. Ada giống với C ngoại trừ các chỉ số thường bắt đầu bằng 1. Bất kỳ ngôn ngữ nào cũng sẽ có các chỉ số ở một trong các lệnh C hoặc FORTRAN đó và các chỉ số sẽ bắt đầu từ 0 hoặc 1 và có thể được điều chỉnh cho phù hợp để lấy dữ liệu được lưu trữ.

Xin lỗi nếu lời giải thích này khó hiểu, nhưng tôi nghĩ nó là chính xác và quan trọng đối với một lập trình viên.


-2

Bạn sẽ có thể truy cập mảng 2d với một con trỏ đơn giản tại chỗ. Mảng [x] [y] sẽ được sắp xếp trong con trỏ dưới dạng p [0x * width + 0y] [0x * width + 1y] ... [0x * width + n-1y] [1x * width + 0y] v.v. .

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.