tags:

views:

265

answers:

3

Hi all,

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

+12  A: 

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 (untested) pseudo-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^3) i = 0;
    return i;
}

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

    result.Insert(0, 'A' + (i % 26)); // some cast might be needed here when adding a char and an int?
    i /= 26;
    result.Insert(0, 'A' + (i % 26));
    i /= 26;
    result.Insert(0, 'A' + (i % 26));

    return result.ToString();
}
Heinzi
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.
meagar
@meagar: A divising is a simple machine instruction. Creating the StringBuilder instance for example costs *way* more than the divisions.
Guffa
@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.
meagar
@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
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
+1  A: 

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
+9  A: 

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);
}
meagar
+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
@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...
meagar
Thanks Meagar for your piece of code. It is compact, simple and clear.This way to solve my problem is really "elegant" !
fxkim