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

I found this code, the problem is that I was asked to have just one char change between permutations

Output:

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

Should Be

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

It has to works with different sets and k. For Example

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 

It's 2 days that I'm trying to find a solution and nothing so far. Java, C++ or pseudocode for a solution it's ok. Thanks

Mattia
  • 130
  • 2
  • 11
  • So, what exactly is your question? What's wrong with the code that you posted? Since the code is Java, why is C++ tagged as well? It's unclear what, exactly, you are asking about. – Algirdas Preidžius Jan 05 '17 at 13:18
  • 1
    @AlgirdasPreidžius The first output, the one that you get if you run the code above, is wrong. Because it should be like the output after the text "Should be" in the post. As you can see from 03 to 10, 2 char value are changing. What I was asked to do is to change only 1 char. 03 should change to 13, only the char 0 change to 1. I've tagged C++ because is ok if someone post a solution in c++. – Mattia Jan 05 '17 at 13:26
  • So, you were tasked to do the modifications. Did you try _anything_? Or do you want _us_ to write the code for you? SO is not code writing service. Related read: [Open letter to students with homework problems](http://meta.softwareengineering.stackexchange.com/questions/6166/open-letter-to-students-with-homework-problems). – Algirdas Preidžius Jan 05 '17 at 13:29
  • @AlgirdasPreidžius Yes I tried, you don't have to write the code. You can give me some idea or try to help me. If I can't ask for a solution or some help, what is this website about?? – Mattia Jan 05 '17 at 13:38
  • @Mattia This website is about: programmers who basically know what they're doing (or know how to search for the information they're missing from the countless tutorial/reference sites around the 'net) and and have demonstrated an attempt to solve a problem themselves. It's _not_ about people who (as far as we can tell) have been given a homework-like problem and (appear to have) made no effort beyond finding some code that doesn't quite work and asking others to fix it. Do you understand what the code you found is doing an why? If not, you first need to work though that ... [cont] – TripeHound Jan 05 '17 at 13:48
  • [cont]... line-by-line, either with pen-and-paper or in a debugger to see what it's doing and why. With that insight, you may well be able to solve the problem yourself (and be well on the way to being a better programmer). If you can't solve it yourself, you will be in a better position to ask a more meaningful question along the lines of "I understand what this code is doing (and why it's not what I want) ... I've tried to modify it in XXXX way... but can't quite get it right". That's more likely to get useful help, as it shows that you've understood the code and made an effort to fix it. – TripeHound Jan 05 '17 at 13:52
  • @TripeHound ok maybe I wasn't clear. Yes I perfectly know how that code that I found works. I used the debugger a lot and also written down the recursion tree, but nothing helped me to get the solution. I also tried to write a different version by myself. I posted that code that i found to make it faster for someone else to help me by having already something that works to look and made some changes. – Mattia Jan 05 '17 at 14:18
  • @Mattia OK. I was possibly too harsh, but the question _looks_ very much like you've been given a homework problem, googled the closest bit of code you could find and then thrown it on SO for someone else to fix it. Even (especially?) if it didn't work, showing _what_ you've tried (and, more importantly _that_ you've tried) is always going to get a better response (SO accepts people don't know things; they don't like people that don't appear to have tried). – TripeHound Jan 05 '17 at 14:27
  • @AlgirdasPreidžius He put the code in the question that he tried. He has a clear explanation of the problem using an example of expected input and output. This is a good question, frankly I'd be happy if all the specs I got were this clear and intriguing. – Jonathan Mee Jan 05 '17 at 18:55
  • @TripeHound I believe he was asking for even psuedocode to solve this. He did paste in the work he'd already put in, and a clear example of how it wasn't solving the problem along with expected input and output. I felt like it was a well formed question without a good answer. I'm really just responding to you out of aggravation I feel that a good question has received a steady stream of downvotes. – Jonathan Mee Jan 05 '17 at 18:59
  • @TripeHound probably "I found this code" at the beginning and "It's 2 days that I'm trying to find a solution and nothing so far." near the end could be the reason of downvotes. – Mattia Jan 05 '17 at 19:25
  • 1
    @Mattia Yes, the way you phrased things probably didn't help. FWIW, I wasn't one of the downvoters, and since it appears you've now worked to an answer with ringø's help, I've cancelled-out one of them. – TripeHound Jan 05 '17 at 19:51

2 Answers2

1

The problem is actually like counting in base sizeof(set) on length k (assuming the set has 10 items maximum).

For instance, with a set of { 0, 1, 2 } on length 2, you count from 00 to 22, base 3.

To solve the "one digit change only" constraint, instead of counting increasingly, do that only until the next 10th change. Then count decreasingly, then again increasingly etc...

For instance in the example above

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

On length 3, keep the same reasoning, change the next 10th then go up or down depending on the initial value of the current digit

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

A recursive algorithm is one approach. The function depth 0 takes care of the first (left) digit, i.e. the highest 10th, and count up or down depending on its current digit state. If 0, count up, and down otherwise. For each state, before incrementing, the function calls itself recursively with the next (right) digit status (which is either 0 or the last item in the set). The maximal depth being the length k.

Keep the digits status in an array of length k. Array is initialized to {0 ... 0}. Give the function the index in the array (starting with 0). For each iteration, if we're at max depth (ie i == k-1), print the array ; otherwise call recursively the function with i+1.

Pseudo code

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 )

This what you should get for 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

Note that you should always get Nk numbers...


This is the C code that generated the above:

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

in main() initialize N and k and call

f(0);

An iterative version, that does basically the same thing

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--;
        }
    }
}
Déjà vu
  • 28,223
  • 6
  • 72
  • 100
  • makes perfect sense. Thank you so much. Gonna try to translate it to code. – Mattia Jan 05 '17 at 14:43
  • One hour has passed: added the C code that generated the results given above, if the pseudo-code is not clear enough. – Déjà vu Jan 05 '17 at 15:58
  • It was clear, but I made some mistakes when translating it to Java as you can see here http://ideone.com/TMRPJz, I was trying to fix it. Now it's easier to fix it from C. Thanks – Mattia Jan 05 '17 at 16:10
  • Hmm try to initialize *set* to {0,0,0} to get cleaner results... The algo assumes each digit will be between *0* and *sizeof(set)-1* – Déjà vu Jan 05 '17 at 16:13
  • ok I fixed it http://ideone.com/bjalbn. I will try to make it better, by maybe making N,k and the array global variable instead of passing them to a function (I'm new to Java). I will update the main post when done. Thanks a lot for the help and the algo. – Mattia Jan 05 '17 at 16:25
  • But just out of curiosity is it possible to do it using an iterative method? – Mattia Jan 05 '17 at 17:20
  • Any recursive function can be converted to iterative. See [this answer](http://stackoverflow.com/questions/159590/way-to-go-from-recursion-to-iteration) for instance. Recursion makes convenient use of the stack used by the language under the hood, usually the thread stack (for data and return address), thus a program may create its own stack (one or more array(s), more or less complex) and iteratively work with it/them. – Déjà vu Jan 05 '17 at 23:58
  • Added iterative version, based on same principle. – Déjà vu Jan 06 '17 at 07:10
0

You're just changing the iteration direction of your least significant element. If you're generating the permutations to a container you can invert the order of size(set) permutations every other size(set) permutations.

The alternative is to write your own permutation generator that takes care of this for you. For example in C++ a simple permutation generator and printer would look like this:

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

Live Example

The modification you would need to make would be to alternate the iteration direction of the least significant iterator each time it reached an end of the set, something like this:

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

Live Example

Jonathan Mee
  • 37,899
  • 23
  • 129
  • 288
  • Thank you, your solution is going to take me a little bit more time because it uses vector and iterators. I haven't used this library so much in the past. – Mattia Jan 05 '17 at 15:00
  • @Mattia It's an interesting problem (+1 you probably just got the downvotes cause you included the [tag:c++]) so I've gone ahead and coded up a possible answer. You'll notice that it greatly increases complexity, so if you could optionally avoid doing it this way it would be worth your while. – Jonathan Mee Jan 05 '17 at 15:30
  • what do you mean by avoid doing it this way? Doing it the "standard" way? When it reach 03 for example it does 10 and not 13? I must do it with "one digit change only" constraint. I searched everywhere in the internet to find a solution without asking here, I probably get the downvotes because I included c++ as you said. – Mattia Jan 05 '17 at 15:36
  • @Mattia Oh I meant avoiding the constraint. Just look at the increased complexity between the 2 code samples I've presented! It's just a lot more work. Anyway, yeah the [tag:c++] is brutal for unnecessary downvoting. Hang in there though, a lot of good knowledge on this site. – Jonathan Mee Jan 05 '17 at 15:45
  • Your second solution works until 030. It should be 030 --> 130 not 030 --> 100. I will try to find the mistake by myself or use the pseudocode solution posted before. – Mattia Jan 05 '17 at 15:53
  • Thanks. What I have to study to understand this part of code: "transform(cbegin(its), cend(its), ostream_iterator(cout), [](const auto& i) { return *i; });" in particular { return *i; }? – Mattia Jan 05 '17 at 19:30
  • @Mattia [`transform`](http://en.cppreference.com/w/cpp/algorithm/transform) performs an operation on the iterator range, copying the result of each operation to the output iterator. Which in this case is just an [`ostream_iterator`](http://en.cppreference.com/w/cpp/iterator/ostream_iterator) which will pipe the values to the output. – Jonathan Mee Jan 05 '17 at 19:53
  • I found out that what I asked is to create a generator for for n-bit and different bases [Gray Codes](https://en.wikipedia.org/wiki/Gray_code). I also realized that the only constraint (that I was asked to respect) is the one digit only change, the output can be in any order. Do you think it's easier with a different output order? And thanks for the code, now I know is possible to create a vector of vector of iterators and initialize them like that :) – Mattia Jan 09 '17 at 00:37
  • @Mattia I don't think that much improvement could be offered on the algorithm the wikipedia article has already presented: https://en.wikipedia.org/wiki/Gray_code#n-ary_Gray_code – Jonathan Mee Jan 09 '17 at 19:18