11

I need to code a method that increment a string value from AAA to ZZZ with cyclic rotation (next value after ZZZ is AAA)

Here is my code:

    public static string IncrementValue(string value) {
        if (string.IsNullOrEmpty(value) || value.Length != 3) {
            string msg = string.Format("Incorrect value ('{0}' is not between AAA and ZZZ)", value);
            throw new ApplicationException(msg);
        }
        if (value == "ZZZ") {
            return "AAA";
        }
        char pos1 = value[0];
        char pos2 = value[1];
        char pos3 = value[2];

        bool incrementPos2 = false;
        bool incrementPos1 = false;

        if (pos3 == 'Z') {
            pos3 = 'A';
            incrementPos2 = true;
        } else {
            pos3++;
        }

        if (incrementPos2 && pos2 == 'Z') {
            pos2 = 'A';
            incrementPos1 = true;
        } else {
            if (incrementPos2) {
                if (pos2 == 'Z') {
                    pos2 = 'A';
                    incrementPos1 = true;
                }
                pos2++;
            }
        }

        if (incrementPos1) {
            pos1++;
        }

        return pos1.ToString() + pos2.ToString() + pos3.ToString();
    }

I know this piece of code is quite dirty and not very efficient but I dont know how to do it properly.

How is secured this snippet? (this will only run on windows plaform)

How can I optimize-it and make it more readable ?

Thanks for your comments

fxkim
  • 363
  • 1
  • 5
  • 11

5 Answers5

20

Think about it mathematically: Your strings (AAA, AAB, ...) behave just like natural numbers (000, 001, ...), with the exception of being base 26 instead of base 10.

So, you can use the same principle. Here is some code:

// iterate cyclicly from 0 to 26^3 - 1
int incrementValue(int i) {
    // a verbose way of writing "return (i + 1) % 26^3"
    i++;
    if (i == 26*26*26) i = 0;
    return i;
}

// convert 0 to AAA, 1 to AAB, ...
string formatValue(int i) {
    var result = new StringBuilder();

    result.Insert(0, (char)('A' + (i % 26)));
    i /= 26;
    result.Insert(0, (char)('A' + (i % 26)));
    i /= 26;
    result.Insert(0, (char)('A' + (i % 26)));

    return result.ToString();
}
Heinzi
  • 167,459
  • 57
  • 363
  • 519
  • I like it, performs the incrementing on a simple integer and converts to base-26 notation on demand. A slight performance hit though, with all that extra division. – user229044 Jun 15 '10 at 16:50
  • @meagar: A divising is a simple machine instruction. Creating the StringBuilder instance for example costs *way* more than the divisions. – Guffa Jun 15 '10 at 17:20
  • @Guffa Was under the impression that division was inherently more expensive than addition, but my knowledge is admittedly dated; I don't spend any time in lower-level languages these days. – user229044 Jun 15 '10 at 17:29
  • 1
    @meagar: Division being more expensive than addition does not change the fact that the price of division is tiny compared to the price of allocating memory. If I was worried about efficiency, I would switch to using array of 3 characters and return it as a `string`. In my experience, this is consistently faster than `StringBuilder` by a large margin, though not always practical (it is, here). However, these concerns are stupid; until speed becomes an issue *and* a profiler discovers that `formatValue` a bottleneck, I'd focus on readability. – Brian Jun 15 '10 at 18:00
  • Thanks Heinzi for your solution. Your code seems efficient and allows me to save the current counter as an integer instead of a string (a better in my case). I'll just say it's a bit difficult to understand for a novice programmer. But again thanks a lot for your contribution – fxkim Jun 15 '10 at 20:32
12

Perhaps I'm missing something, but I think this reasonably trivial solution works, and not just for three digit numbers; any arbitary length base 26 number can be incremented. It will wrap from ZZZZ to AAAA as per the question, rather than incrementing "correctly" from ZZZZ to AAAAA.

// Increment a base 26 number (composed of "digits" A..Z), wrapping around
// from ZZZ... to AAA...
string increment(string str) {        
  char[] digits = str.ToCharArray();

  for (int i = str.length - 1; i >= 0; --i) {
    if (digits[i] == 'Z') {
      digits[i] = 'A';
    } else {
      digits[i] += 1;
      break;
    }
  }
  return new string(digits);
}
user229044
  • 232,980
  • 40
  • 330
  • 338
  • 2
    +1, a nice and compact algorithmic generalization of the "add" algorithm we learned in kindergarten. BTW: Fixing i to 3 would allow you to drop the `if` in the beginning of the method and would result in a "natural" wrap-around. – Heinzi Jun 15 '10 at 17:08
  • @Heinzi Yes, removed it. The algorithm for this specialized kind of incrementing keeps getting simpler the more I think about it. It's about as simple as it's going to get now... – user229044 Jun 15 '10 at 17:27
  • Thanks Meagar for your piece of code. It is compact, simple and clear. This way to solve my problem is really "elegant" ! – fxkim Jun 15 '10 at 20:37
1

I think it's easier to parse it to an integer, do the increment, then format the result as a string. Note that if you just need to iterate over the numbers to generate the range of combinations, then you don't really need the increment/parse. You can simply have a for loop on the integer range and use the format method to convert the integer to a string.

public static string IncrementValue(string value) {
    if (string.IsNullOrEmpty(value) || value.Length != 3) {
        string msg = string.Format("Incorrect value ('{0}' is not between AAA and ZZZ)", value);
        throw new ApplicationException(msg);
    }
    if (value == "ZZZ") {
        return "AAA";
    }
    int thisValue = Parse( value );
    thisValue = (thisValue + 1) % 17576; // 26 * 26 * 26
    return Format( thisValue );
}

private static int Parse( string value )
{
     int result = 0;
     foreach (var c in value)
     {
         result += ('Z' - c);  // might need to cast to int?
     }
     return result;
}

private static string[] Alphabet = new string[] { 'A', 'B', ... };
private static string Format( int value )
{
   int digit0 = value % 26;
   int digit1 = (value / 26) % 26;
   int digit2 = value / 676;
   return Alphabet[digit2] + Alphabet[digit1] + Alphabet[digit0];
}
tvanfosson
  • 524,688
  • 99
  • 697
  • 795
-1
import java.util.*;

import java.io.*;

public class abc{

public static void main (String arg[])throws Exception{

int  i;

String s;

BufferedReader br=new BufferedReader(new InputStreamReader(System.in));

System.out.println("...");\\just for get length of vector example 3 for aaa to zzz
i= Integer.parseInt(br.readLine());  
char[] guess = new char[i];

Arrays.fill(guess, 'a');


 do {
 System.out.println("Current guess:  " + new String(guess));


 int n = guess.length - 1;

 while (n >= 0) {

guess[n]++;

if (guess[n] > 'z') {

                if (n > 0) {

                    guess[n] = 'a';

                }

                n--;

            }

    else {

                break;

            }

        }


    }
 while (guess[0] <= 'z');

}
Nick Dickinson-Wilde
  • 1,015
  • 2
  • 15
  • 21
-1

In 'C' I wrote the following that will do exactly as requested, i.e. given AA will increment to AB. Given ZZZ will increment to AAA (cycle).

main(int argc, char **argv)
{

     int i;
     char *s = argv[1];

     for(i=strlen(s)-1; i >= 0; i--) {
          if(++s[i] > 'Z')
               s[i] = 'A';
          else
               break;
     }

     printf("%s\n",s);
}
Edwin de Koning
  • 14,209
  • 7
  • 56
  • 74
PaoloG
  • 1