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

Java 打印由一组n个字符组成的长度为k的所有排列?,java,c++,string,algorithm,combinatorics,Java,C++,String,Algorithm,Combinatorics,应该是 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 它必须与不同的集合和k一起工作。比如说 00 01 02 03 13 --> 1 Char change. OK 12 11 10 20 -- > 1 Char change. OK 21 22

应该是

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
它必须与不同的集合和k一起工作。比如说

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
我花了两天时间试图找到一个解决方案,但到目前为止什么都没有。java、C++或伪代码的解决方案,没问题。
谢谢

您只是在更改最不重要元素的迭代方向。如果要为容器生成置换,则可以每隔一次sizeset置换,反转sizeset置换的顺序

另一种方法是编写自己的置换生成器,为您解决这个问题。例如,在C++中,一个简单的排列发生器和打印机看起来就是这样:

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 
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);
您需要做的修改是,在最低有效迭代器每次到达集合的末尾时,交替该迭代器的迭代方向,如下所示:

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 
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);

这个问题实际上就像在长度k上的base sizeofset中计数一样,假设该集合最多有10个项目

例如,对于长度为2的{0,1,2}集,从00到22,基数为3进行计数

要解决“仅更改一位数”的限制,请在下一次更改前执行该操作,而不是不断计数。然后递减,然后再递增等等

例如在上面的例子中

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;
    }
}
在长度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
递归算法是一种方法。函数深度0负责左第一位数字,即最高的第十位数字,并根据其当前数字状态向上或向下计数。如果为0,则向上计数,否则向下计数。对于每个状态,在递增之前,函数将使用下一个右位状态(0或集合中的最后一项)递归调用自身。最大深度为长度k

将数字状态保留在长度为k的数组中。数组已初始化为{0…0}。为函数指定数组中以0开头的索引。对于每个迭代,如果我们处于最大深度,即i==k-1,则打印数组;否则,使用i+1递归调用函数

伪码

000 -> 002, 012 -> 010, 020 -> 022
122 -> 120, 110 -> 112, 102 -> 100
200 -> 202, 212 -> 210, 220 -> 222
请注意,您应该始终获得Nk号码

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

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
一个迭代版本,基本上做相同的事情

f(0);

那么,你的问题到底是什么?你发布的代码有什么问题?既然代码是java,C++为什么也被标记了呢?我们不清楚你到底在问什么。@AlgirdasPreidžius第一个输出是错误的,如果你运行上面的代码,就会得到这个输出。因为它应该像输出后的文字一样应该在帖子中。从03到10可以看到,2个字符的值正在变化。我被要求做的是只改变1个字符。03应更改为13,只有字符0更改为1。我已经标记了C++,因为如果有人在C++中发布了一个解决方案,那么就可以了。所以,你被要求做修改。你试过什么吗?或者你想让我们为你写代码?代码编写服务也是如此。相关阅读:.@AlgirdasPreidžius是的,我试过了,你不必写代码。你可以给我一些想法或者试着帮助我。如果我不能寻求解决方案或帮助,这个网站是关于什么的???@Mattia是的,你的措辞方式可能没有帮助。FWIW,我不是选民中的一员,现在看来你已经在ringø的帮助下找到了答案,我取消了其中一个。这很有道理。非常感谢你。我要试着把它翻译成代码。一个小时过去了:如果伪代码不够清晰,添加了生成上述结果的C代码。很清楚,但我在将它翻译成Java时犯了一些错误,正如你在这里看到的,我正在尝试修复它。现在更容易从C.ThanksHmm中修复它。尝试将集合初始化为{0,0,0}以获得更清晰的结果。。。algo假设每个数字都在0和sizeofset-1ok之间,我修正了它。我将尝试让它变得更好,可能通过生成N、k和数组全局变量,而不是将它们传递给我对Java不熟悉的函数。我会在完成后更新主帖子。非常感谢您的帮助和算法。谢谢,您的解决方案将花费我更多的时间,因为它使用向量和迭代器。我过去没有太多使用过这个库。@Mattia这是一个有趣的问题+1你可能只是得到了反对票,因为你包括了这个库,所以我继续编写了一个可能的答案。你会注意到它大大增加了复杂性,所以如果你可以选择避免这样做,那是值得的?按标准方式做?例如,当它达到03时,它是10而不是13?我必须做这件事,只有一位数的变动限制。我在网上到处搜索,不需要问就可以找到解决方案,因为我包含了C++,就像你说的一样,我可能会得到否决。看看这两个代码之间增加的复杂性
我提供的样品!这只是更多的工作。不管怎么说,是的,这对不必要的否决票来说是残酷的。坚持下去,在这个网站上有很多很好的知识。你的第二个解决方案可以工作到凌晨3点。它应该是030->130,而不是030->100。我会尝试自己找出错误,或者使用之前发布的伪代码解决方案。
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--;
        }
    }
}