-2

What's a better way to create a random 16-digit string? I've used this code, can you suggest a more efficient or elegant way to do it?

static string Random16DigitString() {
    var rand = new Random();
    return $"{rand.Next(100000000).ToString().PadLeft(8, '0')}{rand.Next(100000000).ToString().PadLeft(8, '0')}";
}

PS: My reason for making this is to create a string of the form 0.0000000000000000 so I would use it in the following way:

var myString = "0." + Random16DigitString();
bboyle1234
  • 4,859
  • 2
  • 24
  • 29

2 Answers2

2

Your solution depends on string manipulation that will slow it down.

Try:

private static Random r = new Random();

static string Random16DigitString() {
    var v = new char[16];
    for (var j = 0; j < 16; j++) v[j] = (char)(r.NextDouble()*10 + 48);
    return new string(v);
}

This will be faster since it doesn't depend on string operations like concatenation or interpolation. It just pokes random characters into a char array and then converts that array to a string. Executing your solution 100 million times takes about 47 seconds on my machine and my code takes about 27 seconds to produce the same results.

r.Next(10) + 48 would work in the above code but it's actually a little slower. r.Next(48,57) is even slower.

Your code could be simpler, also. $"{rand.Next(100000000):D8}{rand.Next(100000000):D8}" would do the same thing. It's about the same time to execute.

Jim Berg
  • 609
  • 4
  • 7
-2

Here's the code I ended up using:

    static readonly Random rnd = new Random();
    static string Q() {
        // https://stackoverflow.com/questions/767999/random-number-generator-only-generating-one-random-number/768001#768001
        // It was decided to use a lock instead of [ThreadStatic] because this api object is designed to be used by many threads simultaneously.
        lock (rnd) {
            // Get a string representing a positive number greater than 0 and less than 1 with exactly 16 decimal places.
            // Original implementation
            //return $"0.{rnd.Next(100000000).ToString().PadLeft(8, '0')}{rnd.Next(100000000).ToString().PadLeft(8, '0')}";
            // This works but is slow
            //return rnd.NextDouble().ToString("F16");
            // Found a better faster way: https://stackoverflow.com/questions/48455624/generate-random-16-digit-string/48457354#48457354
            var chars = new char[18];
            chars[0] = '0';
            chars[1] = '.';
            for (var i = 2; i < 18; i++)
                chars[i] = (char)(rnd.NextDouble() * 10 + 48);
            return new string(chars);
        }
    }

Here are the tests I used (with thanks to Jim Berg for his answer)

using System;
using System.Diagnostics;
using System.Text;

namespace NetCoreApp1 {
    class Program {
        static void Main(string[] args) {

            var sync = new object();
            var rnd = new Random();

            Time("method1", () => {
                var value = $"{rnd.Next(100000000).ToString().PadLeft(8, '0')}{rnd.Next(100000000).ToString().PadLeft(8, '0')}";
            });

            Time("method2", () => {
                var value = $"{rnd.Next(100000000):D8}{rnd.Next(100000000):D8}";
            });

            Time("next double", () => {
                var value = rnd.NextDouble().ToString("F16"); // turns out surprisingly slow, even slower than the first two
            });

            Time("method3", () => {
                var v = new char[16];
                for (var j = 0; j < 16; j++)
                    v[j] = (char)(rnd.NextDouble() * 10 + 48); // fastest
                var value = new string(v);
            });

            Time("method3 with lock", () => {
                lock (sync) {
                    var v = new char[16];
                    for (var j = 0; j < 16; j++)
                        v[j] = (char)(rnd.NextDouble() * 10 + 48); // a tiny bit slower with the lock
                    var value = new string(v);
                }
            });

            Time("method4", () => {
                var sb = new StringBuilder(16);
                for (var j = 0; j < 16; j++)
                    sb.Append((char)(rnd.NextDouble() * 10 + 48)); // slower than method 3
                var value = sb.ToString();
            });

            Console.WriteLine("Press Enter to exit.");
            Console.ReadLine();
        }

        static void Time(string testName, Action action) {
            var sw = Stopwatch.StartNew();
            for (var i = 0; i < 10000000; i++)
                action();
            sw.Stop();
            Console.WriteLine($"{testName}: {sw.ElapsedMilliseconds}ms");
        }

    }
}
bboyle1234
  • 4,859
  • 2
  • 24
  • 29