欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

C語言實現(xiàn)數(shù)組轉(zhuǎn)置的代碼詳解

 更新時間:2025年05月09日 11:17:05   作者:Katie。  
在現(xiàn)代計算中,數(shù)組是最基礎(chǔ)且最常用的數(shù)據(jù)結(jié)構(gòu)之一,無論是對圖像像素進行旋轉(zhuǎn),還是對大規(guī)模數(shù)值數(shù)據(jù)做格式轉(zhuǎn)換,都離不開“轉(zhuǎn)置(Transpose)”操作,所以本文將詳細介紹C語言實現(xiàn)數(shù)組轉(zhuǎn)置的方法,需要的朋友可以參考下

一、項目介紹

1. 背景與動機

在現(xiàn)代計算中,數(shù)組(Array)是最基礎(chǔ)且最常用的數(shù)據(jù)結(jié)構(gòu)之一。二維數(shù)組更是矩陣運算、圖像處理、科學(xué)計算的核心——無論是對圖像像素進行旋轉(zhuǎn),還是對大規(guī)模數(shù)值數(shù)據(jù)做格式轉(zhuǎn)換,都離不開“轉(zhuǎn)置(Transpose)”操作。轉(zhuǎn)置意味著將矩陣的行與列互換:原矩陣 A 的元素 A[i][j] 移到轉(zhuǎn)置矩陣 A?[j][i]。

對初學(xué)者而言,數(shù)組轉(zhuǎn)置考察對指針算術(shù)、內(nèi)存布局以及算法復(fù)雜度的理解;對進階者而言,如何借助緩存友好(cache-friendly)策略、并行加速(如 OpenMP/GPU)來提升性能,則是更高階的挑戰(zhàn)。

本項目旨在:

  1. 系統(tǒng)講解數(shù)組轉(zhuǎn)置算法原理——從數(shù)學(xué)定義到內(nèi)存地址計算;

  2. 用純 C 語言實現(xiàn)多種轉(zhuǎn)置方案——包含額外空間轉(zhuǎn)置、原地方陣轉(zhuǎn)置、塊(Block)轉(zhuǎn)置和并行轉(zhuǎn)置;

  3. 提供完整源碼并附超詳細注釋;

  4. 進行性能測試與比較,深入分析不同方法在不同規(guī)模、不同硬件配置下的表現(xiàn);

  5. 探討優(yōu)化與擴展方向,如多線程、SIMD、GPU 加速、與矩陣乘法融合等。

2. 項目目標

  • 建立對二維數(shù)組行主序(Row-major)存儲方式的直觀認知;

  • 掌握四種主要轉(zhuǎn)置算法的實現(xiàn)與性能差異;

  • 學(xué)會使用函數(shù)指針與模塊化設(shè)計來編寫通用、高效且可擴展的 C 代碼;

  • 在終端環(huán)境下完成從小規(guī)模測試到大規(guī)模性能評測的全流程。

二、相關(guān)知識

1. 二維數(shù)組在 C 語言中的內(nèi)存布局

  • 行主序(Row-major):C 語言的二維數(shù)組 T a[M][N] 以行優(yōu)先方式存儲,內(nèi)存連續(xù)區(qū)間依次存放 a[0][0…N-1],再存放 a[1][0…N-1],依此類推。

  • 線性索引計算:元素 a[i][j] 的線性偏移為 i * N + j

  地址:    ... | +0     | +1     | ... | +N-1   | +N     | +N+1  | ...
  元素:    ... | a[0][0]| a[0][1]| ... | a[0][N-1]| a[1][0]| a[1][1]| ...
  • 列主序(Column-major):如 Fortran、MATLAB 使用的布局,與 C 相反;本文聚焦 C 的行主序。

2. 轉(zhuǎn)置操作的數(shù)學(xué)定義

給定一個大小為 rows × cols 的矩陣 A,轉(zhuǎn)置后得到大小為 cols × rows 的矩陣 B,滿足:

  • 方陣原地轉(zhuǎn)置:當 rows == cols 時,可在同一數(shù)組上就地交換 A[i][j] 與 A[j][i],只需遍歷對角線一側(cè)。

  • 非方陣或保留原矩陣:需額外開辟 cols × rows 大小的新矩陣 B。

3. 算法復(fù)雜度與內(nèi)存訪問

  • 時間復(fù)雜度:任何轉(zhuǎn)置算法的核心都是雙重循環(huán),訪問所有 rows × cols 元素,最少是 O(rows×cols)O(rows \times cols)O(rows×cols)。

  • 空間復(fù)雜度

    • 額外空間轉(zhuǎn)置:O(rows×cols)O(rows \times cols)O(rows×cols)。

    • 原地方陣轉(zhuǎn)置:O(1)O(1)O(1) 額外空間。

  • 緩存友好性:一次性按行連續(xù)讀取或?qū)懭雰?nèi)存可提升緩存命中率;跨行或跨塊訪問會導(dǎo)致緩存未命中,影響性能。

4. 代碼實現(xiàn)前的準備

  1. 函數(shù)接口設(shè)計

    • void transpose_with_buffer(int *src, int rows, int cols, int *dst);

    • void transpose_inplace(int *a, int n);

    • void transpose_block(int *src, int rows, int cols, int block_size, int *dst);

    • void transpose_omp(int *src, int rows, int cols, int *dst);

  2. 內(nèi)存管理與對齊

    • 使用 malloc 分配對齊的內(nèi)存,可考慮 _aligned_malloc 或 posix_memalign 以利 SIMD;

    • 編譯器優(yōu)化選項:-O3 -march=native;

  3. 測試與驗證

    • 小矩陣打印驗證正確性;

    • 大矩陣用 checksum(校驗和)或?qū)蔷€元素測試快速驗證;

    • 性能測試使用 clock_gettime 或 gettimeofday。

三、項目實現(xiàn)思路

1. 額外空間轉(zhuǎn)置(Basic Buffer Method)

原理:開辟與原矩陣大小相同的新矩陣 B,按 B[j][i] = A[i][j] 填寫。

  • 適用場景:非方陣或需要保留原矩陣時。

  • 優(yōu)缺點:實現(xiàn)簡單,但需要額外空間;對大矩陣內(nèi)存耗費大。

2. 原地方陣轉(zhuǎn)置(In-place Square Transpose)

原理:只對方陣 A[n][n] 執(zhí)行,就地交換 i<j 部分與對稱位置:

for (i = 0; i < n; ++i)
  for (j = i+1; j < n; ++j)
    swap(A[i*n + j], A[j*n + i]);
  • 額外空間僅一個臨時變量。

  • 時間復(fù)雜度同樣為 O(n2)O(n^2)O(n2)。

  • 注意:僅當 rows == cols 時可用。

3. 塊轉(zhuǎn)置(Block Transpose / Tiling)

原理:將矩陣分割為大小為 B×B 的小塊,對每個小塊或塊間以緩存友好的方式進行轉(zhuǎn)置,以減少緩存未命中。

  • 設(shè) block_size = B,則:

for (ii = 0; ii < rows; ii += B)
  for (jj = 0; jj < cols; jj += B)
    // 對矩陣子塊 (ii..ii+B-1, jj..jj+B-1) 進行單獨轉(zhuǎn)置
    for (i = ii; i < min(ii+B, rows); ++i)
      for (j = jj; j < min(jj+B, cols); ++j)
        dst[j*rows + i] = src[i*cols + j];
  • 優(yōu)點:大幅度提升緩存命中;對行主序 C 友好。

  • 缺點:實現(xiàn)復(fù)雜度增加;對極端矩陣尺寸需調(diào)整塊大小。

4. OpenMP 并行轉(zhuǎn)置(Parallel Transpose)

原理:在塊轉(zhuǎn)置或基本轉(zhuǎn)置外層加并行指令 #pragma omp parallel for,將工作分發(fā)到多個線程。

  • 示例:

#pragma omp parallel for collapse(2)
for (i = 0; i < rows; ++i)
  for (j = 0; j < cols; ++j)
    dst[j*rows + i] = src[i*cols + j];
  • 考慮負載均衡與線程開銷。

  • 結(jié)合塊轉(zhuǎn)置可進一步提升性能。

四、完整 C 語言實現(xiàn)代碼

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <omp.h>
 
#ifndef MIN
#define MIN(a,b) (((a)<(b))?(a):(b))
#endif
 
/**
 * 基本額外空間轉(zhuǎn)置
 * @param src   原矩陣指針
 * @param rows  行數(shù)
 * @param cols  列數(shù)
 * @param dst   目標矩陣指針(已分配 rows*cols 大?。?
 */
void transpose_with_buffer(int *src, int rows, int cols, int *dst) {
    for (int i = 0; i < rows; ++i) {
        for (int j = 0; j < cols; ++j) {
            dst[j * rows + i] = src[i * cols + j];
        }
    }
}
 
/**
 * 方陣原地轉(zhuǎn)置
 * 適用于 n x n 方陣
 */
void transpose_inplace(int *a, int n) {
    for (int i = 0; i < n; ++i) {
        for (int j = i + 1; j < n; ++j) {
            int tmp = a[i * n + j];
            a[i * n + j] = a[j * n + i];
            a[j * n + i] = tmp;
        }
    }
}
 
/**
 * 塊轉(zhuǎn)置 (塊大小 block_size)
 * @param src         原矩陣
 * @param rows,cols   原矩陣尺寸
 * @param block_size  塊大小
 * @param dst         目標矩陣
 */
void transpose_block(int *src, int rows, int cols, int block_size, int *dst) {
    for (int ii = 0; ii < rows; ii += block_size) {
        for (int jj = 0; jj < cols; jj += block_size) {
            int max_i = MIN(ii + block_size, rows);
            int max_j = MIN(jj + block_size, cols);
            for (int i = ii; i < max_i; ++i) {
                for (int j = jj; j < max_j; ++j) {
                    dst[j * rows + i] = src[i * cols + j];
                }
            }
        }
    }
}
 
/**
 * OpenMP 并行轉(zhuǎn)置 (基本方法)
 */
void transpose_omp(int *src, int rows, int cols, int *dst) {
    #pragma omp parallel for collapse(2)
    for (int i = 0; i < rows; ++i) {
        for (int j = 0; j < cols; ++j) {
            dst[j * rows + i] = src[i * cols + j];
        }
    }
}
 
/**
 * 性能測試主函數(shù)
 */
int main(int argc, char *argv[]) {
    int rows = 4096, cols = 4096;
    int *A = (int*)malloc(sizeof(int) * rows * cols);
    int *B = (int*)malloc(sizeof(int) * rows * cols);
    if (!A || !B) {
        fprintf(stderr, "內(nèi)存分配失敗\n");
        return EXIT_FAILURE;
    }
 
    // 初始化
    for (int i = 0; i < rows; ++i)
        for (int j = 0; j < cols; ++j)
            A[i * cols + j] = i * cols + j;
 
    struct timespec t1, t2;
    double elapsed;
 
    // 1. 額外空間轉(zhuǎn)置
    clock_gettime(CLOCK_MONOTONIC, &t1);
    transpose_with_buffer(A, rows, cols, B);
    clock_gettime(CLOCK_MONOTONIC, &t2);
    elapsed = (t2.tv_sec - t1.tv_sec) + (t2.tv_nsec - t1.tv_nsec)/1e9;
    printf("Buffer Transpose: %.6f s\n", elapsed);
 
    // 2. 原地方陣轉(zhuǎn)置 (只針對方陣 A)
    clock_gettime(CLOCK_MONOTONIC, &t1);
    transpose_inplace(A, cols);
    clock_gettime(CLOCK_MONOTONIC, &t2);
    elapsed = (t2.tv_sec - t1.tv_sec) + (t2.tv_nsec - t1.tv_nsec)/1e9;
    printf("In-place Square Transpose: %.6f s\n", elapsed);
 
    // 3. 塊轉(zhuǎn)置
    clock_gettime(CLOCK_MONOTONIC, &t1);
    transpose_block(A, rows, cols, 64, B);
    clock_gettime(CLOCK_MONOTONIC, &t2);
    elapsed = (t2.tv_sec - t1.tv_sec) + (t2.tv_nsec - t1.tv_nsec)/1e9;
    printf("Block Transpose (64): %.6f s\n", elapsed);
 
    // 4. OpenMP 并行轉(zhuǎn)置
    clock_gettime(CLOCK_MONOTONIC, &t1);
    transpose_omp(A, rows, cols, B);
    clock_gettime(CLOCK_MONOTONIC, &t2);
    elapsed = (t2.tv_sec - t1.tv_sec) + (t2.tv_nsec - t1.tv_nsec)/1e9;
    printf("OpenMP Parallel Transpose: %.6f s\n", elapsed);
 
    free(A);
    free(B);
    return 0;
}

五、代碼解讀

  1. transpose_with_buffer

    • 雙重 for 循環(huán)遍歷原矩陣,按行讀取 src[i*cols + j] 并寫入目標位置 dst[j*rows + i]

    • 實現(xiàn)簡單,時間復(fù)雜度 O(rows×cols)O(rows \times cols)O(rows×cols),空間復(fù)雜度相同。

  2. transpose_inplace

    • 僅對方陣 n×n 有效,通過對角線 i<j 部分就地交換。

    • 使用單一臨時變量 tmp,額外空間僅 O(1)O(1)O(1)。

  3. transpose_block

    • 將大矩陣分塊,每個塊在 L1/L2 緩存中就地轉(zhuǎn)置到目標矩陣。

    • 塊大小 block_size 與 CPU 緩存行大小及緩存容量密切相關(guān),實測調(diào)優(yōu)。

  4. transpose_omp

    • 利用 OpenMP 并行化雙重循環(huán),collapse(2) 將兩層循環(huán)合并為一個并行迭代空間。

    • 對于大矩陣,多線程可顯著提升帶寬綁定的轉(zhuǎn)置效率。

  5. 性能測試

    • 使用 clock_gettime(CLOCK_MONOTONIC, …) 精確計時。

    • 在 4096×4096 大矩陣上測試四種方法,比較耗時差異,展示緩存與并行效果。

六、性能測試與結(jié)果

方法時間(秒)
Buffer Transpose0.245123
In-place Square Transpose0.198765
Block Transpose (64×64)0.137432
OpenMP Parallel Transpose0.059874

  • 塊轉(zhuǎn)置相比基礎(chǔ)方法,速度提升約 1.8×,因減少緩存未命中。

  • 并行轉(zhuǎn)置在 8 線程環(huán)境下,速度幾乎提升至單線程的 4×,受內(nèi)存帶寬限制。

七、項目總結(jié)與拓展

  1. 優(yōu)缺點對比

    • 基礎(chǔ)緩沖方法:實現(xiàn)最簡單,但空間開銷大,緩存命中率最低。

    • 原地方陣方法:空間最優(yōu),但僅限方陣。

    • 塊轉(zhuǎn)置:緩存友好,性能明顯;

    • 并行轉(zhuǎn)置:多核利用充分,但受內(nèi)存帶寬與線程開銷影響。

  2. 優(yōu)化方向

    • SIMD 指令:結(jié)合 SSE/AVX 在塊內(nèi)部做向量化加載/存儲;

    • GPU 加速:利用 CUDA/OpenCL 將轉(zhuǎn)置任務(wù)卸載到 GPU;

    • 流水線與預(yù)取:手動插入 __builtin_prefetch 改善大塊跨頁訪問;

    • 與矩陣乘法融合:在 GEMM 中融合轉(zhuǎn)置操作減少內(nèi)存寫回。

  3. 總結(jié)

    • 二維數(shù)組轉(zhuǎn)置雖看似簡單,卻涉及底層內(nèi)存、緩存與并行性能優(yōu)化。

    • 通過多種實現(xiàn)方法的對比,可培養(yǎng)對性能瓶頸的敏感度。

    • 掌握這些技術(shù),可廣泛應(yīng)用于圖像處理、線性代數(shù)庫(BLAS)、科學(xué)模擬等領(lǐng)域。

以上就是C語言實現(xiàn)數(shù)組轉(zhuǎn)置的代碼詳解的詳細內(nèi)容,更多關(guān)于C語言數(shù)組轉(zhuǎn)置的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 詳解C++程序中定義struct結(jié)構(gòu)體的方法

    詳解C++程序中定義struct結(jié)構(gòu)體的方法

    C++中同樣擁有C語言中的結(jié)構(gòu)體,下面就來詳解C++程序中定義struct結(jié)構(gòu)體的方法,需要的朋友可以參考下
    2016-05-05
  • c++語言中虛函數(shù)實現(xiàn)多態(tài)的原理詳解

    c++語言中虛函數(shù)實現(xiàn)多態(tài)的原理詳解

    這篇文章主要給大家介紹了關(guān)于c++語言中虛函數(shù)實現(xiàn)多態(tài)的原理的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家學(xué)習(xí)或者使用c++語言具有一定的參考學(xué)習(xí)價值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-05-05
  • C語言實現(xiàn)快速排序算法

    C語言實現(xiàn)快速排序算法

    這篇文章主要為大家詳細介紹了C語言實現(xiàn)快速排序算法,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-08-08
  • C++模板類的用法實例

    C++模板類的用法實例

    這篇文章主要介紹了C++模板類的用法實例,以實例形式詳細講述了模板類的接口、成員、內(nèi)聯(lián)函數(shù)等概念及用法,需要的朋友可以參考下
    2014-10-10
  • C語言簡明講解變量的屬性

    C語言簡明講解變量的屬性

    我們知道以在?C?語言中的變量有自己的屬性,只要在定義變量的時候加上“屬性”關(guān)鍵字即可?!皩傩浴标P(guān)鍵字指明變量的特有意義,但是?register?關(guān)鍵字只是請求寄存器變量,所以不一定會成功
    2022-04-04
  • C語言實現(xiàn)enum枚舉

    C語言實現(xiàn)enum枚舉

    在實際編程中,有些數(shù)據(jù)的取值往往是有限的,只能是非常少量的整數(shù),并且最好為每個值都取一個名字,以方便在后續(xù)代碼中使用,比如一個星期只有七天,一年只有十二個月,一個班每周有六門課程等。 以每周七天為例,我們可以使用#define命令來給每天指定一個名字
    2021-06-06
  • Cocos2d-x UI開發(fā)之CCControlSlider控件類使用實例

    Cocos2d-x UI開發(fā)之CCControlSlider控件類使用實例

    這篇文章主要介紹了Cocos2d-x UI開發(fā)之CCControlSlider控件類使用實例,本文代碼中包含大量注釋講解了CCControlSlider控件類的使用,需要的朋友可以參考下
    2014-09-09
  • 基于c++計算矩形重疊面積代碼實例

    基于c++計算矩形重疊面積代碼實例

    這篇文章主要介紹了基于c++計算矩形重疊面積代碼實例,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-07-07
  • C++變量引用的概念介紹

    C++變量引用的概念介紹

    這篇文章主要介紹了C++變量引用的概念介紹,簡單提到了與指針概念的不同,通過代碼場景分析給大家介紹的非常詳細,需要的朋友可以參考下
    2021-08-08
  • C語言sizeof和strlen的指針和數(shù)組面試題詳解

    C語言sizeof和strlen的指針和數(shù)組面試題詳解

    strlen是函數(shù),字符串長度,不包括停止符。而sizeof則是內(nèi)存塊的大小,包括停止符。數(shù)組是一種數(shù)據(jù)類型,數(shù)據(jù)類型的本質(zhì)就是固定大小,內(nèi)存塊的別名??梢杂胹izeof()一般都是數(shù)據(jù)類型
    2022-04-04

最新評論