-1

I have a string. I want to generate a random string from this string, by replacing a number by a random number. lower character by lower character and upper character by upper character. And remaining characters as it is.

I have written the below code. I need to call this method millions of time on different strings (string length is not more than 100 characters), It's taking too much time.

private static string GenerateRandomAlphanumericValue(string input) {
    char[] newStr = new char[input.Length];
    char[] alphaU = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".ToCharArray();
    char[] alphaL = "abcdefghijklmnopqrstuvwxyz".ToCharArray();
    char[] number = "0123456789".ToCharArray();
    Random random = new Random();
    for (int i = 0; i < input.Length; i++) {
        if (char.IsNumber(input[i])) {

            int index = random.Next(0, number.Length);
            newStr[i] = number[index];
        }
        else if (char.IsUpper(input[i])) {
            int index = random.Next(0, alphaU.Length);
            newStr[i] = alphaU[index];
        }
        else if (char.IsLower(input[i])) {
            int index = random.Next(0, alphaL.Length);
            newStr[i] = alphaL[index];
        }
        else {
            newStr[i] = input[i];
        }
    }

    return string.Join("", newStr);
}

I need help in optimizing the code or there any be a different approach to solve the problem.

Input: vivEK123$% ~a

Output: ajrLW854$% ~w

Vivek Nuna
  • 25,472
  • 25
  • 109
  • 197
  • Didn't this help https://stackoverflow.com/questions/4739903/shuffle-string-c-sharp – SKS Jul 20 '19 at 08:30
  • Shuffling is different, read the question again. – Vivek Nuna Jul 20 '19 at 08:33
  • You can eliminate the Join by just appending characters to end of of string newStr (change from char[]). You can also eliminate ToCharArray() by use string SubString method. – jdweng Jul 20 '19 at 08:33
  • @jdweng ToCharArray() to SubString? I didn't get you. how? – Vivek Nuna Jul 20 '19 at 08:36
  • 1
    The scope of `IsUpper` might have a big impact on its performance. It has to account for culture and all Unicode codepoints in the general category Lu. You, apprently, are only interested in the letters and digits in the [C0 Controls and Basic Latin](http://www.unicode.org/charts/nameslist/index.html) block- a much simpler problem space. – Tom Blodget Jul 20 '19 at 14:23
  • @TomBlodget I didn't get your point? So how to solve the problem – Vivek Nuna Jul 20 '19 at 19:50

4 Answers4

1

Try this

static char[] alphaU = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".ToCharArray();
static char[] alphaL = "abcdefghijklmnopqrstuvwxyz".ToCharArray();
static char[] number = "0123456789".ToCharArray();
static Random random = new Random();
static StringBuilder sb = new StringBuilder(100);

private static string GenerateRandomAlphanumericValue(string input)
{
    sb.Clear();
    for (int i = 0; i < input.Length; i++)
    {
        if (char.IsNumber(input[i]))
        {
            int index = random.Next(0, number.Length);
            sb.Append(number[index]);
        }
        else if (char.IsUpper(input[i]))
        {
            int index = random.Next(0, alphaU.Length);
            sb.Append(alphaU[index]);
        }
        else if (char.IsLower(input[i]))
        {
            int index = random.Next(0, alphaL.Length);
            sb.Append(alphaL[index]);
        }
        else
        {
            sb.Append(input[i]);
        }
    }

    return sb.ToString();
}
DotNet Developer
  • 2,973
  • 1
  • 15
  • 24
0

Instead of using arrays, you could just randomize based on the ASCII-value range of characters. I'd also suggest using StringBuilder to build the result string.

public class Randomizer{
    private static Random rng = new Random();
    public static string RandomizeString(string input){
        StringBuilder sb = new StringBuilder();
        foreach(char c in input){
            if(Char.IsNumber(c)){
                sb.Append(rng.Next(0,10));
            }
            else if(Char.IsLower(c)){
                sb.Append((char)rng.Next(97,123));
            }
            else if(Char.IsUpper(c)){
                sb.Append((char)rng.Next(65,91));
            }
            else{
                sb.Append(c);
            }
        }
        return sb.ToString();
    }
}

Note: a-z is ASCII 97-122, A-Z is ASCII 65-90, and 0-9 is just an integer cast to a string.

pappbence96
  • 1,164
  • 2
  • 12
  • 20
  • Benchmarking your solution against OP's original and @Bahrom's, while moving the Random generation alone cut down the execution by half, casting (either implicit in appending int for number part, and explicit in appending the lower and upper) increase your solution execution time about 50% against Bahrom's who avoided casting – Martheen Jul 20 '19 at 09:47
  • `'A', 'Z' - 'A' + 1` would be easier to read (and calculated by the compiler). – Tom Blodget Jul 20 '19 at 14:14
  • BTW-`char` is a UTF-16 code unit; Nothing to do with ASCII. – Tom Blodget Jul 20 '19 at 14:24
  • @martheen didn't get you. Can you explain by example – Vivek Nuna Jul 20 '19 at 16:28
  • @viveknuna The most expensive part of your original code is generating Random for each call. Which also would make the result for repeated calls with the same input to result with the same output (since the seed rely on time, they will be the same). Use a single Random, you halved the execution time. Put out the initialization for AlphaU, AlphaL and number, plus use StringBuilder which is optimized for this case, you got Bahrom's answer. – Martheen Jul 21 '19 at 14:07
0

This is a little more efficient. I'm not sure about execution time :

       private static string GenerateRandomAlphanumericValue(string input)
        {
            string newStr = "";
            string alphaU = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
            string alphaL = "abcdefghijklmnopqrstuvwxyz";
            string number = "0123456789";
            Random random = new Random();
            for (int i = 0; i < input.Length; i++)
            {
                if (char.IsNumber(input[i]))
                {

                    int index = random.Next(0, number.Length);
                    newStr  += number[index];
                }
                else if (char.IsUpper(input[i]))
                {
                    int index = random.Next(0, alphaU.Length);
                    newStr += alphaU[index];
                }
                else if (char.IsLower(input[i]))
                {
                    int index = random.Next(0, alphaL.Length);
                    newStr += alphaL[index];
                }
                else
                {
                    newStr += input[i];
                }
            }

            return newStr;
        }
jdweng
  • 33,250
  • 2
  • 15
  • 20
0

The general principle is to move as much as possible out of loops and to use the least expensive techniques inside loops.

Random only needs to be constructed once.

StringBuilder is one of the more efficient ways of moving individually acquire Chars into a String. And, it needs to be constructed only once (but introduces thread safety issues).

Array indexing is one fast way to replace chain logic but might not be the fastest since there are only three ranges. (if >= && <= times three could be faster.)

IsUpper et al might have a big impact on its performance. They have to account for all Unicode codepoints in the general category Lu, Ll or Nd.

private static readonly Random random = new Random();
private static readonly StringBuilder sb = new StringBuilder(100);

// min and max are the replacement range for indexing Char.
private static readonly Char[] min = new Char[Char.MaxValue + 1];
private static readonly Char[] max = new Char[Char.MaxValue + 1];

static UserQuery()
{
    foreach (var range in new[] { 
        (min: 'A', max: 'Z'), 
        (min: 'a', max: 'z'), 
        (min: '0', max: '9') })
    {
        for (var c = range.min; c <= range.max; c++)
        {
            min[c] = range.min;
            max[c] = range.max;
        }
    }
}

private static String GenerateRandomAlphanumericValue(String input)
{
    sb.Clear();
    foreach (var c in input)
    {
        sb.Append((Char)random.Next(min[c], max[c]));
    }
    return sb.ToString();  
}
Tom Blodget
  • 20,260
  • 3
  • 39
  • 72
  • How to use UserQuery method? – Vivek Nuna Jul 21 '19 at 12:50
  • 1
    It's the identifier for the class; Change I it to the name of your class. I made it a static initialization block because all the other state is static. You could make it all instance methods. One advantage of that could be to construct a few and run them in parallel if you can partition your millions of strings. – Tom Blodget Jul 21 '19 at 12:57
  • Yep, two million strings of 100 characters takes about 3 seconds when run on 3 or more threads vs 6 seconds on one thread (YMMV). – Tom Blodget Jul 21 '19 at 22:50