C言語における基本的な配列操作とアルゴリズムの実装

本記事では、C言語における配列の基本的なメモリ構造、各種の配列操作、そしていくつかのデータ処理アルゴリズムについて、具体的なコード例を交えながら解説します。一次元配列から多次元配列まで、またデータの入出力、変換、ソートといった基礎的ながらも重要なテクニックを習得することを目的とします。

配列のメモリ配置の確認

C言語において、配列の要素はメモリ上で連続して配置されます。以下のコード例では、一次元配列と二次元配列がどのようにメモリを占有し、各要素がどこに格納されているかを示します。sizeof演算子を用いて配列全体のサイズを確認し、各要素のアドレスを出力することで、その連続性を視覚的に理解できます。

一次元配列のメモリ配置

#include <stdio.h>

#define ARRAY_SIZE_1D 5

void demonstrate_1d_array_memory() {
    int data_array[ARRAY_SIZE_1D] = {10, 20, 30, 40, 50};
    int k;

    printf("一次元配列のサイズ: %zu バイト\n", sizeof(data_array)); // sizeof は size_t を返すため %zu を使用
    printf("配列の先頭アドレス: %p\n", (void*)data_array);

    for (k = 0; k < ARRAY_SIZE_1D; ++k) {
        printf("要素 data_array[%d] のアドレス: %p, 値: %d\n", k, (void*)&data_array[k], data_array[k]);
    }
}

int main() {
    printf("--- 一次元配列のメモリ配置テスト ---\n");
    demonstrate_1d_array_memory();
    printf("\n");
    return 0;
}

二次元配列のメモリ配置

#include <stdio.h>

#define ROWS 3
#define COLS 4

void demonstrate_2d_array_memory() {
    int matrix[ROWS][COLS] = {
        {1, 2, 3, 4},
        {5, 6, 7, 8},
        {9, 10, 11, 12}
    };
    int r, c;

    printf("二次元配列のサイズ: %zu バイト\n", sizeof(matrix));
    printf("配列の先頭アドレス: %p\n", (void*)matrix); // 配列全体の先頭アドレス

    for (r = 0; r < ROWS; ++r) {
        printf("行 matrix[%d] の先頭アドレス: %p\n", r, (void*)matrix[r]); // 各行の先頭アドレス
        for (c = 0; c < COLS; ++c) {
            printf("  要素 matrix[%d][%d] のアドレス: %p, 値: %d\n", r, c, (void*)&matrix[r][c], matrix[r][c]);
        }
    }
}

int main() {
    printf("--- 二次元配列のメモリ配置テスト ---\n");
    demonstrate_2d_array_memory();
    printf("\n");
    return 0;
}

配列から最大値・最小値を除外した平均値の計算

与えられた数値のリストから、最も大きい値と最も小さい値を除外した上で、残りの要素の平均値を計算するアルゴリズムです。これにより、外れ値の影響を低減した代表値を求めることができます。

#include <stdio.h>
#include <limits.h> // INT_MAX, INT_MIN のためにインクルード

#define MAX_ELEMENTS 100

// ユーザーから数値を入力する関数
void get_array_input(int elements[], int count) {
    printf("%d個の整数を入力してください:\n", count);
    for (int i = 0; i < count; ++i) {
        if (scanf("%d", &elements[i]) != 1) {
            fprintf(stderr, "入力エラーが発生しました。\n");
            // エラー処理(例:プログラム終了、再入力プロンプトなど)
            while (getchar() != '\n'); // 入力バッファをクリア
            return;
        }
    }
}

// 最大値と最小値を除外した平均値を計算する関数
double calculate_trimmed_mean(const int elements[], int count) {
    if (count < 3) { // 少なくとも3つの要素が必要(最大、最小、残り1つ)
        fprintf(stderr, "平均値を計算するには少なくとも3つの要素が必要です。\n");
        return 0.0; // エラーを示す値
    }

    int current_max = INT_MIN;
    int current_min = INT_MAX;
    long long sum_values = 0; // 合計値が大きくなる可能性を考慮して long long を使用

    for (int i = 0; i < count; ++i) {
        sum_values += elements[i];
        if (elements[i] > current_max) {
            current_max = elements[i];
        }
        if (elements[i] < current_min) {
            current_min = elements[i];
        }
    }

    // 最大値と最小値を除外
    sum_values -= current_max;
    sum_values -= current_min;

    // 残りの要素数で平均を計算
    return (double)sum_values / (count - 2);
}

int main() {
    int numbers[MAX_ELEMENTS];
    int num_count;
    double result_mean;

    while (printf("配列の要素数を入力してください (3以上、%d以下、EOFで終了): ", MAX_ELEMENTS),
           scanf("%d", &num_count) == 1) {

        if (num_count < 3 || num_count > MAX_ELEMENTS) {
            printf("無効な要素数です。3以上%d以下の値を入力してください。\n\n", MAX_ELEMENTS);
            while (getchar() != '\n'); // 入力バッファをクリア
            continue;
        }

        get_array_input(numbers, num_count);
        result_mean = calculate_trimmed_mean(numbers, num_count);
        if (result_mean != 0.0 || num_count >=3) { // エラーチェックを考慮
           printf("最大値と最小値を除外した平均値: %.2f\n\n", result_mean);
        }
    }

    return 0;
}

二次元配列の初期化と表示

このセクションでは、指定されたサイズの二次元配列(行列)を特定の単一の値で初期化し、その後その内容を整形して表示するC言語の関数を紹介します。これにより、行列操作の基本的な準備と結果の確認が行えます。

#include <stdio.h>

#define MATRIX_DIM_MAX 10

// 行列の要素を特定の値で初期化する関数
void initialize_matrix(int target_matrix[][MATRIX_DIM_MAX], int dimension, int fill_value) {
    for (int row = 0; row < dimension; ++row) {
        for (int col = 0; col < dimension; ++col) {
            target_matrix[row][col] = fill_value;
        }
    }
}

// 行列の内容を表示する関数
void display_matrix_contents(const int target_matrix[][MATRIX_DIM_MAX], int dimension) {
    for (int row = 0; row < dimension; ++row) {
        for (int col = 0; col < dimension; ++col) {
            printf("%4d ", target_matrix[row][col]); // 各要素を4桁幅で表示
        }
        printf("\n");
    }
}

int main() {
    int my_matrix[MATRIX_DIM_MAX][MATRIX_DIM_MAX];
    int matrix_size, initial_val;

    while (printf("行列の次元 (n) と初期値 (value) を入力してください (n value, EOFで終了): "),
           scanf("%d%d", &matrix_size, &initial_val) == 2) {

        if (matrix_size <= 0 || matrix_size > MATRIX_DIM_MAX) {
            printf("無効な次元です。1以上%d以下の値を入力してください。\n\n", MATRIX_DIM_MAX);
            while (getchar() != '\n');
            continue;
        }

        initialize_matrix(my_matrix, matrix_size, initial_val);
        printf("初期化された行列:\n");
        display_matrix_contents(my_matrix, matrix_size);
        printf("\n");
    }

    return 0;
}

配列の中央値(メディアン)の計算

一連の数値データから中央値を求める関数です。中央値はデータを昇順に並べたときの中央に位置する値であり、データセットの代表値として利用されます。要素数が奇数の場合は中央の値を、偶数の場合は中央の2つの値の平均を返します。

#include <stdio.h>
#include <stdlib.h> // qsort のためにインクルード

#define MEDIAN_MAX_SIZE 100

// qsort用の比較関数
int compare_integers(const void *a, const void *b) {
    return (*(int*)a - *(int*)b);
}

// 配列に数値を入力する関数(再利用)
void get_numbers_input(int arr[], int num_elements) {
    printf("%d個の整数を入力してください:\n", num_elements);
    for (int i = 0; i < num_elements; ++i) {
        scanf("%d", &arr[i]);
    }
}

// 配列の中央値を計算する関数
double compute_array_median(int arr[], int num_elements) {
    if (num_elements == 0) {
        fprintf(stderr, "中央値を計算するための要素がありません。\n");
        return 0.0;
    }

    // 配列を昇順にソート
    qsort(arr, num_elements, sizeof(int), compare_integers);

    if (num_elements % 2 == 1) {
        // 要素数が奇数の場合、中央の値を返す
        return (double)arr[num_elements / 2];
    } else {
        // 要素数が偶数の場合、中央の2つの値の平均を返す
        int mid1 = arr[num_elements / 2 - 1];
        int mid2 = arr[num_elements / 2];
        return (double)(mid1 + mid2) / 2.0;
    }
}

int main() {
    int values[MEDIAN_MAX_SIZE];
    int count;
    double calculated_median;

    while (printf("数値の個数を入力してください (1以上%d以下、EOFで終了): ", MEDIAN_MAX_SIZE),
           scanf("%d", &count) == 1) {

        if (count <= 0 || count > MEDIAN_MAX_SIZE) {
            printf("無効な個数です。1以上%d以下の値を入力してください。\n\n", MEDIAN_MAX_SIZE);
            while (getchar() != '\n');
            continue;
        }

        get_numbers_input(values, count);
        calculated_median = compute_array_median(values, count);
        printf("計算された中央値: %g\n\n", calculated_median);
    }
    return 0;
}

二次元配列の行を右にシフト

このプログラムは、正方行列の各行を右方向に1つシフトさせる機能を提供します。例えば、行 [a, b, c, d][d, a, b, c] となります。行列の内容を入力し、変換前と変換後の両方を表示します。

#include <stdio.h>

#define MATRIX_MAX_DIMENSION 10

// 行列に数値を入力する関数
void populate_matrix(int matrix_data[][MATRIX_MAX_DIMENSION], int dimension) {
    printf("%d x %d 行列の要素を入力してください:\n", dimension, dimension);
    for (int i = 0; i < dimension; ++i) {
        for (int j = 0; j < dimension; ++j) {
            scanf("%d", &matrix_data[i][j]);
        }
    }
}

// 行列の内容を整形して出力する関数
void print_matrix(const int matrix_data[][MATRIX_MAX_DIMENSION], int dimension) {
    for (int i = 0; i < dimension; ++i) {
        for (int j = 0; j < dimension; ++j) {
            printf("%5d", matrix_data[i][j]);
        }
        printf("\n");
    }
}

// 各行を右に1つシフトする関数
void perform_row_right_shift(int matrix_data[][MATRIX_MAX_DIMENSION], int dimension) {
    for (int r = 0; r < dimension; ++r) {
        int temp_last_element = matrix_data[r][dimension - 1]; // 各行の最後の要素を一時保存

        // 要素を右にシフト
        for (int c = dimension - 1; c > 0; --c) {
            matrix_data[r][c] = matrix_data[r][c - 1];
        }
        matrix_data[r][0] = temp_last_element; // 保存した最後の要素を先頭に配置
    }
}

int main() {
    int square_matrix[MATRIX_MAX_DIMENSION][MATRIX_MAX_DIMENSION];
    int dim_size;

    printf("正方行列の次元 (n) を入力してください (1以上%d以下): ", MATRIX_MAX_DIMENSION);
    if (scanf("%d", &dim_size) != 1 || dim_size <= 0 || dim_size > MATRIX_MAX_DIMENSION) {
        fprintf(stderr, "無効な入力です。プログラムを終了します。\n");
        return 1;
    }

    populate_matrix(square_matrix, dim_size);

    printf("--- 元の行列 ---\n");
    print_matrix(square_matrix, dim_size);

    perform_row_right_shift(square_matrix, dim_size);

    printf("\n--- 行が右にシフトされた行列 ---\n");
    print_matrix(square_matrix, dim_size);

    return 0;
}

十進数をN進数に変換

このセクションでは、与えられた十進整数を、指定された基数(N進数)に変換して表示するC言語のプログラムを提示します。特に、二進数、八進数、十六進数への変換例を通して、汎用的な基数変換ロジックを解説します。変換結果は文字列として逆順に構築され、その後正しい順序で出力されます。

#include <stdio.h>

// 十進数を任意の基数(n進数)に変換して出力する関数
void convert_base_representation(int decimal_value, int target_base) {
    if (decimal_value == 0) {
        printf("0");
        return;
    }

    char result_buffer[33]; // int の最大値 (約2*10^9) は2進数で31ビット + 1 (終端null)
    int buffer_index = 0;
    int current_value = decimal_value; 

    // 負の数も考慮する場合、ここで符号を処理し、絶対値で変換を行う
    if (current_value < 0) {
        printf("-"); // 負の記号を出力
        current_value = -current_value; // 絶対値に変換
    }

    while (current_value > 0) {
        int remainder = current_value % target_base;
        if (remainder < 10) {
            result_buffer[buffer_index++] = remainder + '0'; // 0-9
        } else {
            result_buffer[buffer_index++] = remainder - 10 + 'A'; // A-F (for base > 10)
        }
        current_value /= target_base;
    }

    // 逆順に格納されているため、逆順に出力
    for (int i = buffer_index - 1; i >= 0; --i) {
        printf("%c", result_buffer[i]);
    }
}

int main() {
    int input_decimal;

    while (printf("十進整数を入力してください (EOFで終了): "), scanf("%d", &input_decimal) == 1) {
        printf("%d (10進数) の変換結果:\n", input_decimal);

        printf("  二進数: ");
        convert_base_representation(input_decimal, 2);
        printf("\n");

        printf("  八進数: ");
        convert_base_representation(input_decimal, 8);
        printf("\n");

        printf("  十六進数: ");
        convert_base_representation(input_decimal, 16);
        printf("\n\n");
    }

    return 0;
}

魔方陣の判定

魔方陣とは、n行n列の正方行列において、各行、各列、そして2つの主対角線上の要素の合計がすべて同じ値になる特殊な行列です。このプログラムは、ユーザーが入力した行列が魔方陣の条件を満たすかどうかを判定します。

#include <stdio.h>
#include <stdbool.h> // bool 型のためにインクルード

#define MAGIC_SQUARE_MAX_DIM 10

// 行列に数値を入力する関数(再利用)
void get_square_matrix_input(int matrix_data[][MAGIC_SQUARE_MAX_DIM], int dimension) {
    printf("要素を一つずつ入力してください:\n");
    for (int r = 0; r < dimension; ++r) {
        for (int c = 0; c < dimension; ++c) {
            scanf("%d", &matrix_data[r][c]);
        }
    }
}

// 行列の内容を整形して出力する関数(再利用)
void print_square_matrix(const int matrix_data[][MAGIC_SQUARE_MAX_DIM], int dimension) {
    printf("入力された行列:\n");
    for (int r = 0; r < dimension; ++r) {
        for (int c = 0; c < dimension; ++c) {
            printf("%5d", matrix_data[r][c]);
        }
        printf("\n");
    }
}

// 行列が魔方陣であるかを判定する関数
bool check_if_magic_square(const int matrix_data[][MAGIC_SQUARE_MAX_DIM], int dimension) {
    if (dimension <= 0) return false;

    // 基準となる合計値を計算(最初の行の合計)
    int reference_sum = 0;
    for (int c = 0; c < dimension; ++c) {
        reference_sum += matrix_data[0][c];
    }

    // 各行の合計をチェック
    for (int r = 0; r < dimension; ++r) {
        int row_sum = 0;
        for (int c = 0; c < dimension; ++c) {
            row_sum += matrix_data[r][c];
        }
        if (row_sum != reference_sum) {
            return false;
        }
    }

    // 各列の合計をチェック
    for (int c = 0; c < dimension; ++c) {
        int col_sum = 0;
        for (int r = 0; r < dimension; ++r) {
            col_sum += matrix_data[r][c];
        }
        if (col_sum != reference_sum) {
            return false;
        }
    }

    // 主対角線(左上から右下)の合計をチェック
    int main_diag_sum = 0;
    for (int i = 0; i < dimension; ++i) {
        main_diag_sum += matrix_data[i][i];
    }
    if (main_diag_sum != reference_sum) {
        return false;
    }

    // 副対角線(右上から左下)の合計をチェック
    int anti_diag_sum = 0;
    for (int i = 0; i < dimension; ++i) {
        anti_diag_sum += matrix_data[i][dimension - 1 - i];
    }
    if (anti_diag_sum != reference_sum) {
        return false;
    }

    return true; // すべての条件を満たせば魔方陣
}

int main() {
    int input_square[MAGIC_SQUARE_MAX_DIM][MAGIC_SQUARE_MAX_DIM];
    int matrix_dimension;

    while (printf("判定する魔方陣の次元 n を入力してください (1以上%d以下、EOFで終了): ", MAGIC_SQUARE_MAX_DIM),
           scanf("%d", &matrix_dimension) == 1) {

        if (matrix_dimension <= 0 || matrix_dimension > MAGIC_SQUARE_MAX_DIM) {
            printf("無効な次元です。1以上%d以下の値を入力してください。\n\n", MAGIC_SQUARE_MAX_DIM);
            while (getchar() != '\n');
            continue;
        }

        get_square_matrix_input(input_square, matrix_dimension);
        print_square_matrix(input_square, matrix_dimension);

        if (check_if_magic_square(input_square, matrix_dimension)) {
            printf("これは魔方陣です。\n\n");
        } else {
            printf("これは魔方陣ではありません。\n\n");
        }
    }

    return 0;
}

タグ: C言語 配列 データ構造 メモリ管理 アルゴリズム

7月2日 23:10 投稿