前にも同じような記事を書いたけど、考えてみれば 重複順列 / 重複組み合わせ も一緒だなあと思い、まとめてみました。
関数版
// 共通の内部再帰関数
const innerPC = filterFunc => selecteds => k => options =>
(k === 0)? [selecteds]
: options.flatMap(
(e, i) =>
innerPC( filterFunc )( [...selecteds, e] )( k-1 )( filterFunc(i)(options) )
)
// 順列
const permFilter = i => options =>
[...options.slice(0, i), ...options.slice(i + 1)]
const perm =
innerPC( permFilter )( [] )
// 組み合わせ
const combiFilter = i => options =>
options.slice(i + 1)
const combi =
innerPC( combiFilter )( [] )
// 重複順列
const repPermFilter = i => options =>
options
const repPerm =
innerPC( repPermFilter )( [] )
// 重複組み合わせ
const repCombiFilter = i => options =>
options.slice(i)
const repCombi =
innerPC( repCombiFilter )( [] )
// 使用例:
perm(2)([0, 1, 2])
/*
[ [ 0, 1 ], [ 0, 2 ], [ 1, 0 ], [ 1, 2 ], [ 2, 0 ], [ 2, 1 ] ]
*/
combi(2)([0, 1, 2])
/*
[ [ 0, 1 ], [ 0, 2 ], [ 1, 2 ] ]
*/
repPerm(3)([0, 1])
/*
[ [ 0, 0, 0 ],
[ 0, 0, 1 ],
[ 0, 1, 0 ],
[ 0, 1, 1 ],
[ 1, 0, 0 ],
[ 1, 0, 1 ],
[ 1, 1, 0 ],
[ 1, 1, 1 ] ]
*/
repCombi(3)([0, 1])
/*
[ [ 0, 0, 0 ], [ 0, 0, 1 ], [ 0, 1, 1 ], [ 1, 1, 1 ] ]
*/
ジェネレータ版
const innerPCG = filterFunc => selecteds => k => options => function*(){
if(k===0){
yield selecteds
return
}
for(const i of options.keys()) {
yield* innerPCG( filterFunc )(
[...selecteds, options[i]] )( k-1 )( filterFunc(i)(options) )
}
}()
// 順列
const permFilter = i => options =>
[...options.slice(0, i), ...options.slice(i + 1)]
const permG = innerPCG( permFilter )( [] )
// 組み合わせ
const combiFilter = i => options =>
options.slice(i + 1)
const combiG = innerPCG( combiFilter )( [] )
// 重複順列
const repPermFilter = i => options =>
options
const repPermG =
innerPCG( repPermFilter )( [] )
// 重複組み合わせ
const repCombiFilter = i => options =>
options.slice(i)
const repCombiG =
innerPCG( repCombiFilter )( [] )
// 使用例:
;
[...permG(2)([0,1,2])];
[...combiG(2)([0,1,2])];
[...repPermG(3)([0,1])];
[...repCombiG(3)([0,1])];
// 結果はそれぞれ関数版に同じ
おかしなところがあれば、コメントよろしくです。