本稿では、C言語を用いた代表的なアルゴリズム課題を再構成し、各関数の設計意図と改善点を技術的に解説します。コード例は意図的に構造・変数名・制御フローを変更し、教育的かつ実用的な書き直しを行っています。
成績マッピング関数(文字列評価)
整数スコアを10点刻みで分類し、対応する等級記号を返す関数です。入力範囲に応じてA~Eの5段階評価を実施します。
#include <stdio.h>
char evaluate_grade(int raw_score) {
const int threshold = raw_score / 10;
switch (threshold) {
case 10: case 9: return 'A';
case 8: return 'B';
case 7: return 'C';
case 6: return 'D';
default: return 'F'; // 59以下はF(修正:E→F、国際標準に準拠)
}
}
int main() {
int input;
while (scanf("%d", &input) == 1) {
printf("得点: %d → 評価: %c\n", input, evaluate_grade(input));
}
return 0;
}
整数の桁和計算(反復 vs 再帰)
正の整数の各桁の数字の合計を求める処理を、反復版と再帰版で実装しています。再帰版は末尾再帰ではなく、自然な分割統治スタイルを採用。
// 反復実装
int digit_sum_iterative(int value) {
int total = 0;
int abs_val = (value < 0) ? -value : value; // 符号無視
do {
total += abs_val % 10;
abs_val /= 10;
} while (abs_val > 0);
return total;
}
// 再帰実装(ベースケース:1桁以下)
int digit_sum_recursive(int value) {
int abs_val = (value < 0) ? -value : value;
if (abs_val < 10) return abs_val;
return (abs_val % 10) + digit_sum_recursive(abs_val / 10);
}
効率的なべき乗計算(二分累乗法)
指数nが大きい場合でも対数時間でxnを計算する再帰アルゴリズム。偶数指数では中間結果を再利用し、計算量をO(log n)に削減。
long long fast_power(long long base, unsigned int exp) {
if (exp == 0) return 1;
if (exp == 1) return base;
long long half = fast_power(base, exp / 2);
if (exp % 2 == 0) {
return half * half;
} else {
return half * half * base;
}
}
双子素数の列挙(最適化済み判定)
100未満の双子素数ペア(p, p+2)を検出し、その総数をカウントします。素数判定には√nまで試し割りを行い、不要なループを排除。
#include <stdbool.h>
bool is_prime_optimized(int candidate) {
if (candidate < 2) return false;
if (candidate == 2) return true;
if (candidate % 2 == 0) return false;
for (int i = 3; i * i <= candidate; i += 2) {
if (candidate % i == 0) return false;
}
return true;
}
void list_twin_primes_under_100() {
int pair_count = 0;
printf("100未満の双子素数ペア:\n");
for (int p = 3; p + 2 < 100; p += 2) {
if (is_prime_optimized(p) && is_prime_optimized(p + 2)) {
printf("(%d, %d)\n", p, p + 2);
pair_count++;
}
}
printf("合計: %d 組\n", pair_count);
}
ハノイの塔シミュレータ(移動回数計測付き)
再帰的手順でディスク移動を可視化し、総移動回数を正確に集計。各ステップで「ディスク番号: 出発杭 → 目標杭」の形式で出力します。
int simulate_hanoi(unsigned int disks, char source, char auxiliary, char target) {
static int move_counter = 0;
if (disks == 1) {
printf("%u: %c → %c\n", disks, source, target);
move_counter++;
return 1;
}
int steps = 0;
steps += simulate_hanoi(disks - 1, source, target, auxiliary);
printf("%u: %c → %c\n", disks, source, target);
steps += 1;
steps += simulate_hanoi(disks - 1, auxiliary, source, target);
return steps;
}
組み合わせ数計算(二項係数)
n個からm個を選ぶ組み合わせ数 C(n,m) を、数値オーバーフローを回避する工夫で実装。再帰版はパスカルの三角形の漸化式 C(n,m) = C(n−1,m) + C(n−1,m−1) を直接表現。
// 安全な反復実装(乗算/除算を交互に実行)
unsigned long long binomial_iterative(unsigned int n, unsigned int m) {
if (m > n || m < 0) return 0;
if (m == 0 || m == n) return 1;
// C(n,m) = C(n,n-m) を利用して計算量削減
if (m > n - m) m = n - m;
unsigned long long result = 1;
for (unsigned int i = 0; i < m; ++i) {
result = result * (n - i) / (i + 1);
}
return result;
}
// 再帰実装(メモ化なし、小規模入力向け)
unsigned long long binomial_recursive(unsigned int n, unsigned int m) {
if (m > n || m < 0) return 0;
if (m == 0 || m == n) return 1;
return binomial_recursive(n - 1, m) + binomial_recursive(n - 1, m - 1);
}
三数の最大公約数(ユークリッド拡張)
3つの整数a,b,cのGCDを求める関数。単純な試し割りではなく、gcd(gcd(a,b),c) の性質を利用した効率的な実装。
int gcd_two(int x, int y) {
while (y != 0) {
int temp = y;
y = x % y;
x = temp;
}
return x;
}
int gcd_three(int a, int b, int c) {
return gcd_two(gcd_two(a, b), c);
}