打印可以由一组n个字符组成的长度为k的所有排列

Print all permutation of length k that can be formed from a set of n characters?

本文关键字:排列 字符 一组 打印      更新时间:2023-10-16
public class PrintStrLenK {
public static void main(String[] args){
int k = 2;
char[] set = {'0', '1'};
char[] str = new char[k];
generate(k, set, str, 0);
}
static void generate(int k, char[] set, char[] str, int index){
if (index == k){
System.out.println(new String(str));
}
else {
for (int i = 0; i < set.length; i++){
str[index] = set[i];
generate(k, set, str, index + 1);
}
}
} 
}

我发现了这个代码,问题是我被要求在排列之间只更改一个字符

输出:

00
01
02
03
10 --> 2 Char changes. Not OK.
11
12
13
20 --> 2 Char changes. Not OK.
21
22
23
30 --> 2 Char changes. Not OK.
31
32
33

应该是

00
01
02
03
13 --> 1 Char change. OK
12
11
10
20 -- > 1 Char change. OK
21
22
23
33 -- > 1 Char change. OK
32
31
30

它必须与不同的集合和k一起工作。例如

set = {'0', '1'} and k= 3.
000 001 011 010 110 111 101 100 
set = {'0', '1','2','3'} and k= 3.
000 001 002 003 013 012 011 010 020 021 022 023 033 032 031 030 130 131 132 133 123 122 121 120 110 111 112 113 103 102 101 100 200 201 202 203 213 212 211 210 220 221 222 223 233 232 231 230 330 331 332 333 323 322 321 320 310 311 312 313 303 302 301 300 

我花了两天时间试图找到解决方案,但到目前为止什么都没有。Java、C++或伪代码作为解决方案都可以。感谢

这个问题实际上就像是在长度k上以sizeof(set)为基数计数(假设集合最多有10个项目)。

例如,对于长度为2的一组{ 0, 1, 2 },可以从00计数到22,基数为3。

要解决"仅一位数更改"的限制,而不是增加计数,只进行计数,直到下一个第10次更改为止。然后计数递减,然后再递增等…

例如,在上面的示例中

00 -> 02 then increase the next tenth (12), then count downward
12 -> 10 then again +10 to get 20, then go up again
20 -> 22

在长度3上,保持相同的推理,更改下一个10th,然后根据当前数字的初始值向上或向下

000 -> 002, 012 -> 010, 020 -> 022
122 -> 120, 110 -> 112, 102 -> 100
200 -> 202, 212 -> 210, 220 -> 222

递归算法是一种方法。函数深度0负责第一个(左)数字,即最高的第10个,并根据其当前数字状态向上或向下计数。如果是0,则向上计数,否则向下计数。对于每种状态,在递增之前,函数都会以下一个(右)数字状态(要么是0,要么是集合中的最后一项)递归地调用自己。最大深度为长度k

将数字状态保持在长度为k的数组中。数组初始化为{0 ... 0}。为函数指定数组中的索引(从0开始)。对于每次迭代,如果我们处于最大深度(即i == k-1),则打印数组;否则递归调用具有CCD_ 8的函数。

伪码

k length of number (number of digits)
N size of set (1 .. 10), values from 0 to N-1
A array of size k
A[0 .. k-1] = 0
function f ( i )
begin
inc = -1 if (A[i] > 0), 1 otherwise     # Increment
end =  0 if (A[i] > 0), N-1 otherwise   # Max value
j is the counter
j = A[ i ]   # Init
while ( (inc<0 AND j>=end) OR (inc>0 AND j<=end) )
do
A[ i ] = j
if (i < k-1) call f ( i+1 )
otherwise print array A
j = j + inc
done
end
call f ( 0 )

这是N = 3, and k = 4应该得到的

0000 0001 0002 0012 0011 0010 0020 0021 0022
0122 0121 0120 0110 0111 0112 0102 0101 0100
0200 0201 0202 0212 0211 0210 0220 0221 0222
1222 1221 1220 1210 1211 1212 1202 1201 1200
1100 1101 1102 1112 1111 1110 1120 1121 1122
1022 1021 1020 1010 1011 1012 1002 1001 1000
2000 2001 2002 2012 2011 2010 2020 2021 2022
2122 2121 2120 2110 2111 2112 2102 2101 2100
2200 2201 2202 2212 2211 2210 2220 2221 2222

注意,你应该总是得到Nk的数字。。。


这是生成上述内容的C代码:

int a[20] = {0}; // Put here the right size instead of 20, or use #define...
int N,k;
void f(int i) {
int inc = a[i] ? -1:1;
int end = a[i] ? 0:N-1;
int j;
for(j=a[i] ; inc<0 && j>=end || inc>0 && j<=end ; j+=inc) {
a[i] = j;
if (i < k-1) f(i+1);
else {
int z;
for(z=0 ; z<k ; z++) printf("%d", a[z]);
printf("n");
}
}
}

main()中初始化Nk并调用

f(0);

一个迭代版本,基本上做相同的事情

void fi() {
int z,i,inc[k];
for(i=0 ; i<k ; i++) {
a[i] = 0;     // initialize our array if needed
inc[i] = 1;   // all digits are in +1 mode
}
int p = k-1;    // p, position: start from last digit (right)
while(p >= 0) {
if (p == k-1) {
for(z=0 ; z<k ; z++) printf("%d", a[z]);
printf("n");
}
if (inc[p]<0 && a[p]>0 || inc[p]>0 && a[p]<N-1) {
a[p] += inc[p];
p = k-1;
}
else {
inc[p] = -inc[p];
p--;
}
}
}

您只是在更改最不重要元素的迭代方向。如果要生成容器的排列,则可以每隔一个size(set)排列反转size(set)排列的顺序。

另一种选择是编写自己的置换生成器,为您处理此问题。例如,在C++中,一个简单的排列生成器和打印机看起来像这样:

vector<vector<int>::const_iterator> its(k, cbegin(set));
do {
transform(cbegin(its), cend(its), ostream_iterator<int>(cout), [](const auto& i) { return *i; });
cout << endl;
for (auto it = rbegin(its); it != rend(its) && ++*it == cend(set); ++it) *it = cbegin(set);
} while (count(cbegin(its), cend(its), cbegin(set)) != k);

实时示例

您需要进行的修改是,每次到达集合的末尾时,都要交替最不重要迭代器的迭代方向,类似于以下内容:

vector<vector<int>::const_iterator> its(k, cbegin(set));
vector<bool> ns(k);
for(int i = k - 1; its.front() != cend(set); its[i] = next(its[i], ns[i] ? -1 : 1), i = k - 1) {
transform(cbegin(its), cend(its), ostream_iterator<int>(cout), [](const auto& i) { return *i; });
cout << endl;
while (i > 0 && (!ns[i] && its[i] == prev(cend(set)) || ns[i] && its[i] == cbegin(set))) {
ns[i] = !ns[i];
--i;
}
}

实时示例