元の数字列を並べ替える代わりに、数字グループを並べ替えます。どのように説明すればよいか分からないので、疑似コードを試してみます。
文字列「001222」の場合、数字グループは 2 つの 0、1 つの 1、および 3 つの 2 です。
permute(groups, permutation):
if there are no non-empty groups
print permutation
else
for each non-empty group
permutation += group.digit
--group.count
permute(groups, permutation)
すべての数字ではなくグループをループすることにより、各数字は複数回ではなく次の位置に 1 回だけ選択できるため、重複の生成が回避されます。あなたが得るランダムな順列を歩く
Permutation Digit Groups
0: 2, 1: 1, 2: 3 // start
0 0: 1, 1: 1, 2: 3
02 0: 1, 1: 1, 2: 2 // *
021 0: 1, 1: 0, 2: 2 // the 1 group is removed from the set
0212 0: 1, 1: 0, 2: 1
02120 0: 0, 1: 0, 2: 1 // the 0 group is removed from the set
021202 0: 0, 1: 0, 2: 0 // the 2 group is removed from the set
展開して * に戻ります。
02 0: 1, 1: 0, 2: 1
元の文字列のすべての (繰り返される) 数字ではなく、数字グループをループしているため、2 を再度選択することはできません。これは、プレフィックス「02」が一度だけ生成されるため、「02」で始まるすべての順列が一意になることを意味します。同じことがアルゴリズム全体に適用されます。
アップデート
入力 "001222" に対して 60 の順列を生成する簡単な PHP 実装を次に示します。
function permute(&$groups, &$count, $permutation) {
$done = true;
foreach ($groups as &$group) {
if ($group[1] > 0) {
--$group[1];
permute($groups, $count, $permutation . $group[0]);
++$group[1];
$done = false;
}
}
if ($done) {
echo $permutation . PHP_EOL;
++$count;
}
}
$groups = array(
array(0, 2),
array(1, 1),
array(2, 3),
);
$count = 0;
permute($groups, $count, '');
echo "\nTotal: $count\n";