0

Let´s say I have twenty variables that all together results in a twenty digits number.

  • Each variable comprise between 1 and 3.
  • example: 11132211332212331233

I need to cycle all the possibles combinations and this is the way I found out:

StringBuffer myStr = new StringBuffer();

for(byte i1=1; i<21; i++)

  for(byte i2=1; i2<21; i2++)

    for(byte i3=1; i3<21; i3++)      // and so on till i20
      ...
      myStr.append (i+i1+i2...i20)

I guess there is a better way (reflection?...) to make it, so my question is how can I write this code in a better way.

Mariano
  • 19
  • 2
  • 1
    You can do this either with recursion, or by using an array for the 20 digits and incrementing/resetting them appropriately. Note that there are 3.4e9 combinations, so such a loop will take a while to run... – Thomas Apr 26 '16 at 14:05
  • If all those variables represent a _digit_ with the constraint of being 1, 2, or 3, why do the for-loops count from 1 till 20? – Seelenvirtuose Apr 26 '16 at 14:09
  • You can do it using array of counters instead of inner loops, look at example code here http://stackoverflow.com/a/20252193/2892277 – Nikolay Apr 26 '16 at 14:17
  • the final result comprise between 11111111111111111 - 33333333333333333333 – Mariano Apr 26 '16 at 14:17

2 Answers2

1

I would just use a simple integer counter and print it in base 3 :

for (int i=0; i < Math.pow(3, 20); i++) {
    System.out.println(Integer.toString(i, 20));
}

This is actually sufficient to represent your 20 variable object and generate all its states, however we have a lot of conversions to do to meet your requirements :

  • the number would naturally use the 0, 1 and 2 symbols, we have to shift them to 1, 2 and 3
  • the number wouldn't naturally be padded. We use the %1$20s format descriptor, which means "positional argument 1, as a 20 char string (padded left by default)". That pads with spaces however, so we must also replace spaces with 1.

Which gives us this final code :

for (int i=0; i < Math.pow(3, 20); i++) {
    System.out.println(String.format("%1$20s", Integer.toString(i, 20)).replace("2", "3").replace("1", "2").replace("0", "1").replace(" ", "1"));
}

Edit : why must I write ugly code when I can use streams and lambdas?

IntStream.range(0, (int)Math.pow(3, 20))                       // yields 0 to 3^20-1
         .mapToObj(number -> Integer.toString(number, 3))      // base 3 conversion
         .map(string -> String.format("%1$20s", string))       // 20 char padding
         .map(paddedString -> paddedString.replace("2", "3"))  // number shifting. must be made in this specific order not to fail
         .map(paddedString -> paddedString.replace("1", "2"))
         .map(paddedString -> paddedString.replace("0", "1"))
         .map(paddedString -> paddedString.replace(" ", "1"))
         .forEach(System.out::println);
Aaron
  • 24,009
  • 2
  • 33
  • 57
  • Well that's one huge and ugly line, consider breaking it into multiple commented lines ;) – Aaron Apr 26 '16 at 14:56
  • See my edit if you're using JDK8 and plan to make this code maintainable – Aaron Apr 26 '16 at 15:17
  • Hey I was checking out your solution with IntStream, but am getting some compilation errors. I think you meant to use -> instead of =>, also, I think the lamdas defined inside the map calls must return an int? – mdewit Apr 26 '16 at 15:19
  • Yeah, I had multiple errors in my initial Java 8 version, but I've tested the current one and it should work fine :) – Aaron Apr 26 '16 at 15:19
0
for(long count = 0; count <= 3486784400L; count++) {
    String val = "11111111111111111111" + Long.toString(count, 3).replaceAll("2", "3").replaceAll("1", "2").replaceAll("0", "1");
    System.out.println(val.substring(val.length() - 20));
}

Output:

11111111111111111111
11111111111111111112
11111111111111111113
11111111111111111121
11111111111111111122
11111111111111111123
11111111111111111131
...etc
mdewit
  • 2,016
  • 13
  • 31
  • The solutions is close, but generates duplicates: – Mariano Apr 26 '16 at 14:46
  • Try this: for(long count = 0; count < 11L; count++) { String val = "11" + Long.toString(count, 3).replaceAll("2", "3").replaceAll("1", "2").replaceAll("0", "1"); System.out.println(val.substring(val.length() - 2)); } – Mariano Apr 26 '16 at 14:46
  • In that example you should only count up to 8L. The reason being that the base10 equivalent of the base3 number 22 is 8L. Use for(long count = 0; count <= 8L; count++) {}. I do see that I used < instead of <= in my code aswell though so Ive edited that :) – mdewit Apr 26 '16 at 14:58