C言語での順列生成プログラムの実装例と自分なりのわかりやすい解説をまとめました。
順列生成とはつまりN!
通りの順列を全て表示させるプログラムです。
AtCoderの過去問を解いているときに、C言語における順列の生成法が分からなかったので調べたのですが、それでもわかりにくかったので自分で書きました。
C言語での再帰の順列生成プログラムの実装例
#include <stdio.h>
#include <stdlib.h>
void permutations(int *P, int *used, int total, int N)
{
int i;
int n;
i = 1; //as permutation does not contain 0
while (i <= total)
{
if (!used[i]) //if i is unused in the current permutation
{
used[i] = 1; //mark i as "used"
P[N] = i;
if (N == 1) //print when one permutation is complete
{
n = total;
while (n)
{
printf("%d ", P[n]);
n--;
}
printf("\n");
}
else // set num for P[N - 1]
permutations(P, used, total, N - 1);
used[i] = 0; //set i as "unused"
}
i++;
}
}
int main()
{
int N;
int *P;
int *used;
printf("total num : ");
scanf("%d\n", &N);
P = (int *)malloc(sizeof(int) * (N + 1));
used = (int *)calloc((N + 1), sizeof(int));
permutations(P, used, N, N);
free(P);
free(used);
return (0);
}
以下で簡単にコードの説明をしています。
main関数の挙動
-
標準入力から生成する順列
N!
の値Nを読み取る -
順列をストックするための配列
P
をmalloc
で動的確保する -
一つの順列の中で、既に使われた数をトラックするための配列
used
をmalloc
で動的確保する -
配列を生成する関数
permutations
に先ほど確保した配列のP
とused
、そして配列の合計のN
と現在の配列の番地を指し示すN
を引数として渡す -
動的確保した
P
とused
をfree
関数で開放する
(注意)2と3の時N + 1
分の配列を確保しているのは、参照している値をわかりやすくするために、順列に含まれない0を飛ばしてそれぞれP[1]
、used[1]
から配列を使用しているためです。
(追記)
used
の配列が0で初期化されていなかったので、malloc
の代わりにcalloc
を使用してはとのアドバイスをいただきました。ありがとうございます!used
の配列はcalloc
を使って確保するように書き換えました。
permutations関数の挙動
この関数は再起的に呼び出されることによって順列を生成しています
全体の流れとしては、
- まず再帰の最下層である
N = 1
まで進み、その時点で完成されたP
が表示される - 使用されていた
i
が解放され、N = 2
の層に戻る(N = 1
ではi = total
のためwhile
ループから抜けるから) - この時点で、
i = total
になるまでwhile
ループに入っているので、まずP[2]
とP[1]
の値が入れ替わったものが次に表示される。 - 使用されていた
i
が解放され、N = 3
の層に戻る(N = 1
とN = 2
ではi = total
のためwhile
ループから抜けるから) - この時点で
while
ループが終わるまでi
の値の異なる順列を表示
という流れとして再帰されて順列が生成されます。