Как я могу генерировать случайные буквенно-цифровые строки? [закрыто]

Сделать val mutable as:

mutable int val;

Теперь вы можете изменять / изменять / мутировать val, даже если foo const:

void f(const Foo & foo)
{
     foo.val = 10;  //ok
     foo.id  = 11;  //compilation error - id is not mutable.
}

By Кстати, из вашего кода вы, кажется, думаете, что если p.second истинно, то значение уже существует в наборе, и поэтому вы обновляете связанное значение. Думаю, вы ошибаетесь. На самом деле это наоборот. doc в cpluscplus говорит:

Пара :: второй элемент в паре имеет значение true, если новый элемент был вставлен или false, если элемент с тем же

blockquote>

, что является правильным, на мой взгляд.


Однако, если вы используете std::map, ваше решение будет простым:

void update(std::map & m, std::pair value) 
{
    m[value.first] += value.second;
}

Что делает этот код? m[value.first] создает новую запись, если ключ не существует на карте, а значением новой записи является значение по умолчанию int, которое равно нулю. Поэтому он добавляет value.second в zero. Или, если ключ существует, он просто добавляет к нему value.second. То есть приведенный выше код эквивалентен этому:

void update(std::map & m, std::pair value) 
{
    std::map::iterator it = m.find(value);
    if ( it != m.end()) //found or not?
           it.second += value; //add if found
    else
    {
           m.insert(value); //insert if not found
    }
}

Но это слишком много, не так ли? Это не очень хорошо. Более ранний из них более лаконичен и очень эффективен.

832
задан Andrew 22 April 2019 в 22:44
поделиться

9 ответов

Я слышал, что LINQ - это новый черный цвет, поэтому вот моя попытка использовать LINQ:

private static Random random = new Random();
public static string RandomString(int length)
{
    const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
    return new string(Enumerable.Repeat(chars, length)
      .Select(s => s[random.Next(s.Length)]).ToArray());
}

(Примечание: использование класса Random делает этот непригодным для чего-либо, связанного с безопасностью , например для создания паролей или токенов. Используйте класс RNGCryptoServiceProvider , если вы нужен сильный генератор случайных чисел.)

1572
ответ дан 22 November 2019 в 21:06
поделиться

Существует один из потрясающих пакеты самородка , которые делают это настолько простым.

var myObject = new Faker<MyObject>()
.RuleFor(p => p.MyAlphaNumericProperty, f => f.Random.AlphaNumeric(/*lenght*/ 7))
.Generate();

Один из хорошего примера здесь .

0
ответ дан 22 November 2019 в 21:06
поделиться

Вот пример, который я позаимствовал из примера Сэма Аллена на Dot Net Perls

. Если вам нужно всего 8 символов, используйте Path.GetRandomFileName () в пространстве имен System.IO . Сэм говорит, что использование метода «Path.GetRandomFileName здесь иногда лучше, потому что он использует RNGCryptoServiceProvider для лучшей случайности. Однако он ограничен 11 случайными символами».

GetRandomFileName всегда возвращает 12-символьную строку с точкой в ​​9-м символе. . Поэтому вам нужно удалить точку (поскольку это не случайно), а затем взять 8 символов из строки. На самом деле, вы можете просто взять первые 8 символов и не беспокоиться о точке.

public string Get8CharacterRandomString()
{
    string path = Path.GetRandomFileName();
    path = path.Replace(".", ""); // Remove period.
    return path.Substring(0, 8);  // Return 8 character string
}

PS: спасибо Сэм

65
ответ дан 22 November 2019 в 21:06
поделиться

Решение 1 - самый большой «диапазон» с наиболее гибкой длиной

string get_unique_string(int string_length) {
    using(var rng = new RNGCryptoServiceProvider()) {
        var bit_count = (string_length * 6);
        var byte_count = ((bit_count + 7) / 8); // rounded up
        var bytes = new byte[byte_count];
        rng.GetBytes(bytes);
        return Convert.ToBase64String(bytes);
    }
}

Это решение имеет больший диапазон, чем использование GUID, потому что GUID имеет пару фиксированных битов, которые всегда одинаковы и, следовательно, не случайным образом, например, 13-значный шестнадцатеричный символ всегда равен «4» - по крайней мере, в GUID версии 6.

Это решение также позволяет генерировать строку любой длины.

Решение 2 - Одна строка кода - хорошо до 22 символов

Convert.ToBase64String(Guid.NewGuid().ToByteArray()).Substring(0, 8);

Вы не можете генерировать строки, пока Решение 1 и строка не имеет того же диапазона из-за фиксированных битов в GUID, но во многих случаях это будет сделай свою работу.

Решение 3 - Немного меньше кода

Guid.NewGuid().ToString("n").Substring(0, 8);

В основном сохраняю это здесь для исторических целей. Он использует немного меньше кода,

196
ответ дан 22 November 2019 в 21:06
поделиться

Ужасно, я знаю, но я просто не мог с собой поделать:


namespace ConsoleApplication2
{
    using System;
    using System.Text.RegularExpressions;

    class Program
    {
        static void Main(string[] args)
        {
            Random adomRng = new Random();
            string rndString = string.Empty;
            char c;

            for (int i = 0; i < 8; i++)
            {
                while (!Regex.IsMatch((c=Convert.ToChar(adomRng.Next(48,128))).ToString(), "[A-Za-z0-9]"));
                rndString += c;
            }

            Console.WriteLine(rndString + Environment.NewLine);
        }
    }
}

4
ответ дан 22 November 2019 в 21:06
поделиться

ОБНОВЛЕНО на основе комментариев. Исходная реализация генерировала ah ~ 1,95% времени, а оставшиеся символы ~ 1,56% времени. Обновление генерирует все символы в ~ 1,61% случаев.

ПОДДЕРЖКА РАМКИ - .NET Core 3 (и будущие платформы, поддерживающие .NET Standard 2.1 или выше) предоставляет криптографически надежный метод RandomNumberGenerator.GetInt32 () для генерации случайного целого числа в желаемом диапазоне.

В отличие от некоторых из представленных альтернатив, эта криптографически надежна .

using System;
using System.Security.Cryptography;
using System.Text;

namespace UniqueKey
{
    public class KeyGenerator
    {
        internal static readonly char[] chars =
            "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890".ToCharArray(); 

        public static string GetUniqueKey(int size)
        {            
            byte[] data = new byte[4*size];
            using (RNGCryptoServiceProvider crypto = new RNGCryptoServiceProvider())
            {
                crypto.GetBytes(data);
            }
            StringBuilder result = new StringBuilder(size);
            for (int i = 0; i < size; i++)
            {
                var rnd = BitConverter.ToUInt32(data, i * 4);
                var idx = rnd % chars.Length;

                result.Append(chars[idx]);
            }

            return result.ToString();
        }

        public static string GetUniqueKeyOriginal_BIASED(int size)
        {
            char[] chars =
                "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890".ToCharArray();
            byte[] data = new byte[size];
            using (RNGCryptoServiceProvider crypto = new RNGCryptoServiceProvider())
            {
                crypto.GetBytes(data);
            }
            StringBuilder result = new StringBuilder(size);
            foreach (byte b in data)
            {
                result.Append(chars[b % (chars.Length)]);
            }
            return result.ToString();
        }
    }
}

На основе обсуждения альтернатив здесь и обновлено / изменено на основе комментариев ниже.

Вот небольшая тестовая программа, которая демонстрирует распределение символов в старом и обновленном выводе. Для подробного обсуждения анализа случайности , посетите random.org.

using System;
using System.Collections.Generic;
using System.Linq;
using UniqueKey;

namespace CryptoRNGDemo
{
    class Program
    {

        const int REPETITIONS = 1000000;
        const int KEY_SIZE = 32;

        static void Main(string[] args)
        {
            Console.WriteLine("Original BIASED implementation");
            PerformTest(REPETITIONS, KEY_SIZE, KeyGenerator.GetUniqueKeyOriginal_BIASED);

            Console.WriteLine("Updated implementation");
            PerformTest(REPETITIONS, KEY_SIZE, KeyGenerator.GetUniqueKey);
            Console.ReadKey();
        }

        static void PerformTest(int repetitions, int keySize, Func<int, string> generator)
        {
            Dictionary<char, int> counts = new Dictionary<char, int>();
            foreach (var ch in UniqueKey.KeyGenerator.chars) counts.Add(ch, 0);

            for (int i = 0; i < REPETITIONS; i++)
            {
                var key = generator(KEY_SIZE); 
                foreach (var ch in key) counts[ch]++;
            }

            int totalChars = counts.Values.Sum();
            foreach (var ch in UniqueKey.KeyGenerator.chars)
            {
                Console.WriteLine($"{ch}: {(100.0 * counts[ch] / totalChars).ToString("#.000")}%");
            }
        }
    }
}
309
ответ дан 22 November 2019 в 21:06
поделиться
var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
var stringChars = new char[8];
var random = new Random();

for (int i = 0; i < stringChars.Length; i++)
{
    stringChars[i] = chars[random.Next(chars.Length)];
}

var finalString = new String(stringChars);

Не такое элегантное, как решение Linq.

(Примечание: использование класса Random делает его непригодным для чего-либо, связанного с безопасностью , например, создание паролей или токенов. Используйте класс RNGCryptoServiceProvider , если вам нужен надежный генератор случайных чисел.)

344
ответ дан 22 November 2019 в 21:06
поделиться

После просмотра других ответов и рассмотрения комментариев CodeInChaos, а также ответов CodeInChaos, все еще предвзятых (хотя и менее), я подумал, что необходимо окончательное окончательное решение для вырезания и вставки . Поэтому, обновляя свой ответ, я решил выложиться на полную.

Чтобы получить последнюю версию этого кода, посетите новый репозиторий Hg на Bitbucket: https://bitbucket.org/merarischroeder/secureswiftrandom . Я рекомендую вам скопировать и вставить код с сайта: https://bitbucket.org/merarischroeder/secureswiftrandom/src/6c14b874f34a3f6576b0213379ecdf0ffc7496ea/Code/Alivate.SolidSwiftRandom=Somolid-Saultwift 11/Somolid-Deview&SomolidSwift ] (не забудьте нажать кнопку Raw, чтобы упростить копирование, и убедитесь, что у вас последняя версия, Я думаю, что эта ссылка указывает на конкретную версию кода, а не на последнюю).

Обновленные примечания:

  1. Относительно некоторых других ответов - Если вы знаете длину вывода, вам не нужен StringBuilder, а при использовании ToCharArray это создает и заполняет массив (вам не нужно сначала создавать пустой массив)
  2. Относительно некоторых других ответов - вы должны использовать NextBytes, а не получать по одному для производительности
  3. Технически вы можете закрепить байтовый массив для более быстрого доступа ... обычно оно того стоит, когда вы повторяете более 6-8 раз по байтовому массиву. (Здесь не выполняется)
  4. Использование RNGCryptoServiceProvider для наилучшей случайности
  5. Использование кэширования буфера случайных данных размером 1 МБ - сравнительный анализ показывает, что скорость доступа к кешированным однобайтам примерно в 1000 раз выше - принимая 9 мс более 1 МБ против 989 мс без кэширования.
  6. Оптимизировано отклонение зоны смещения в моем новом классе.

Окончательное решение вопроса:

static char[] charSet =  "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".ToCharArray();
static int byteSize = 256; //Labelling convenience
static int biasZone = byteSize - (byteSize % charSet.Length);
public string GenerateRandomString(int Length) //Configurable output string length
{
    byte[] rBytes = new byte[Length]; //Do as much before and after lock as possible
    char[] rName = new char[Length];
    SecureFastRandom.GetNextBytesMax(rBytes, biasZone);
    for (var i = 0; i < Length; i++)
    {
        rName[i] = charSet[rBytes[i] % charSet.Length];
    }
    return new string(rName);
}

Но вам нужен мой новый (непроверенный) класс:

/// <summary>
/// My benchmarking showed that for RNGCryptoServiceProvider:
/// 1. There is negligable benefit of sharing RNGCryptoServiceProvider object reference 
/// 2. Initial GetBytes takes 2ms, and an initial read of 1MB takes 3ms (starting to rise, but still negligable)
/// 2. Cached is ~1000x faster for single byte at a time - taking 9ms over 1MB vs 989ms for uncached
/// </summary>
class SecureFastRandom
{
    static byte[] byteCache = new byte[1000000]; //My benchmark showed that an initial read takes 2ms, and an initial read of this size takes 3ms (starting to raise)
    static int lastPosition = 0;
    static int remaining = 0;

    /// <summary>
    /// Static direct uncached access to the RNGCryptoServiceProvider GetBytes function
    /// </summary>
    /// <param name="buffer"></param>
    public static void DirectGetBytes(byte[] buffer)
    {
        using (var r = new RNGCryptoServiceProvider())
        {
            r.GetBytes(buffer);
        }
    }

    /// <summary>
    /// Main expected method to be called by user. Underlying random data is cached from RNGCryptoServiceProvider for best performance
    /// </summary>
    /// <param name="buffer"></param>
    public static void GetBytes(byte[] buffer)
    {
        if (buffer.Length > byteCache.Length)
        {
            DirectGetBytes(buffer);
            return;
        }

        lock (byteCache)
        {
            if (buffer.Length > remaining)
            {
                DirectGetBytes(byteCache);
                lastPosition = 0;
                remaining = byteCache.Length;
            }

            Buffer.BlockCopy(byteCache, lastPosition, buffer, 0, buffer.Length);
            lastPosition += buffer.Length;
            remaining -= buffer.Length;
        }
    }

    /// <summary>
    /// Return a single byte from the cache of random data.
    /// </summary>
    /// <returns></returns>
    public static byte GetByte()
    {
        lock (byteCache)
        {
            return UnsafeGetByte();
        }
    }

    /// <summary>
    /// Shared with public GetByte and GetBytesWithMax, and not locked to reduce lock/unlocking in loops. Must be called within lock of byteCache.
    /// </summary>
    /// <returns></returns>
    static byte UnsafeGetByte()
    {
        if (1 > remaining)
        {
            DirectGetBytes(byteCache);
            lastPosition = 0;
            remaining = byteCache.Length;
        }

        lastPosition++;
        remaining--;
        return byteCache[lastPosition - 1];
    }

    /// <summary>
    /// Rejects bytes which are equal to or greater than max. This is useful for ensuring there is no bias when you are modulating with a non power of 2 number.
    /// </summary>
    /// <param name="buffer"></param>
    /// <param name="max"></param>
    public static void GetBytesWithMax(byte[] buffer, byte max)
    {
        if (buffer.Length > byteCache.Length / 2) //No point caching for larger sizes
        {
            DirectGetBytes(buffer);

            lock (byteCache)
            {
                UnsafeCheckBytesMax(buffer, max);
            }
        }
        else
        {
            lock (byteCache)
            {
                if (buffer.Length > remaining) //Recache if not enough remaining, discarding remaining - too much work to join two blocks
                    DirectGetBytes(byteCache);

                Buffer.BlockCopy(byteCache, lastPosition, buffer, 0, buffer.Length);
                lastPosition += buffer.Length;
                remaining -= buffer.Length;

                UnsafeCheckBytesMax(buffer, max);
            }
        }
    }

    /// <summary>
    /// Checks buffer for bytes equal and above max. Must be called within lock of byteCache.
    /// </summary>
    /// <param name="buffer"></param>
    /// <param name="max"></param>
    static void UnsafeCheckBytesMax(byte[] buffer, byte max)
    {
        for (int i = 0; i < buffer.Length; i++)
        {
            while (buffer[i] >= max)
                buffer[i] = UnsafeGetByte(); //Replace all bytes which are equal or above max
        }
    }
}

Для истории - мое старое решение для этого ответа, used Random object:

    private static char[] charSet =
      "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".ToCharArray();

    static rGen = new Random(); //Must share, because the clock seed only has Ticks (~10ms) resolution, yet lock has only 20-50ns delay.
    static int byteSize = 256; //Labelling convenience
    static int biasZone = byteSize - (byteSize % charSet.Length);
    static bool SlightlyMoreSecurityNeeded = true; //Configuration - needs to be true, if more security is desired and if charSet.Length is not divisible by 2^X.
    public string GenerateRandomString(int Length) //Configurable output string length
    {
      byte[] rBytes = new byte[Length]; //Do as much before and after lock as possible
      char[] rName = new char[Length];
      lock (rGen) //~20-50ns
      {
          rGen.NextBytes(rBytes);

          for (int i = 0; i < Length; i++)
          {
              while (SlightlyMoreSecurityNeeded && rBytes[i] >= biasZone) //Secure against 1/5 increased bias of index[0-7] values against others. Note: Must exclude where it == biasZone (that is >=), otherwise there's still a bias on index 0.
                  rBytes[i] = rGen.NextByte();
              rName[i] = charSet[rBytes[i] % charSet.Length];
          }
      }
      return new string(rName);
    }

Производительность:

  1. SecureFastRandom - Первый одиночный запуск = ~ 9-33ms . Незаметный. Выполняется : 5 мс (иногда до 13 мс) за 10 000 итераций, с одной средней итерацией = 1,5 микросекунды. . Примечание. Обычно требуется 2, но иногда до 8 обновлений кэша - в зависимости от того, сколько отдельных байтов превышает зону смещения
  2. Случайно - Первое одиночное выполнение = ~ 0–1 мс . Незаметный. Выполняется : 5 мс более 10 000 итераций. С одной средней итерацией = 0,5 микросекунды. . Примерно такая же скорость.

Также проверьте:

Эти ссылки представляют собой другой подход. В эту новую кодовую базу можно было бы добавить буферизацию, но наиболее важным было изучение различных подходов к устранению предвзятости и оценка скорости и плюсов / минусов.

5
ответ дан 22 November 2019 в 21:06
поделиться

Если ваши значения не полностью случайны, но на самом деле могут зависеть от чего-то - вы можете вычислить хэш md5 или sha1 этого "чего-то", а затем усечь его до нужной длины.

Также вы можете сгенерировать и усечь guid.

0
ответ дан 22 November 2019 в 21:06
поделиться
Другие вопросы по тегам:

Похожие вопросы: