blood-moon
@blood-moon
Фрилансер

Генерация уникального рандомного значения?

Здравствуйте , есть массив строк , нужно было к каждой строке добавить уникальное десятизначное число .
Сделал вот так но работает слишком медленно
1. генерация числа
2. сверка его с листом уже сгенерированных
3. если такого в листе нет то добавляем его в лист и отдаём обратно
3/3 если такой есть то генерируем новое , пока не будет уникальное
Вот код

private string GetRandom(int count)
        {
            string pool = "987654321";
            string tmp = "";
            while (true)
            {
                Random R = new Random();
                for (int x = 0; x < count; x++)
                {
                    tmp += pool[R.Next(0, pool.Length)].ToString();
                }
                if (!listRandom.Contains(tmp))
                {
                    listRandom.Add(tmp);
                    break;
                }
                tmp = "";
            }
            return tmp;
        }

Данный код решает мою задачу но уж больно медленный (я так понимаю из-за постоянной сверки с уже созданными), подскажите как правильно реализовать подобную уникальную генерацию ?
  • Вопрос задан
  • 322 просмотра
Пригласить эксперта
Ответы на вопрос 2
vabka
@vabka Куратор тега C#
Токсичный шарпист
Вспомнил олимпиадное программирование.
Посмотрел по коду:
Для генерации случайной строки размером 10 используется словарь из 9 символов
=> информации в этой строке около 31.7 бит. Что даёт нам 3486784401 разных вариантов. Что спокойно умещается в UInt32
=> задачу можно упростить до генерации ОДНОГО числа в этом диапазоне с последующим форматированием в эту десятисимвольную строку.

Дополнительно можно немного оптимизировать, если для гарантии уникальности использовать не список, а hashset.
А скорость генерации строки и требуемую память - путём выделения буфера на 10 символов заранее.
И ещё немного оптимизации, путём создания одного экземпляра Random
Вот такая реализация получилась:
public sealed class UniqueStringGenerator
{
    private const long MAX = 3486784401;

    private readonly Random _random;
    private readonly HashSet<long> _history;

    public UniqueStringGenerator(int seed)
    {
        _random = new Random(seed);
        _history = new HashSet<long>();
    }

    public UniqueStringGenerator()
    {
        _random = new Random();
        _history = new HashSet<long>();
    }

    public string Next() => Format(NextNumber());

    public void Reset()
    {
        _history.TrimExcess();
        _history.Clear();

    }

    private long NextNumber()
    {

        if (_history.Count >= int.MaxValue)
        {
            throw new InvalidOperationException("Variants exceeded. Please reset");
        }
        var next = _random.NextInt64(0, MAX);
        while (_history.Contains(next))
        {
            next = _random.NextInt64(0, MAX);
        }
        _history.Add(next);
        return next;
    }

    private static string Format(long number) => string.Create<long>(10, number,
        static (span, number) =>
        {
             // Алгоритм по-лучше придумать не смог.
             // Проходимся по каждому биту числа, понемногу сужая выбор между числами в словаре.
            const string DICTIONARY = "123456789";
            for (var i = 0; i < span.Length; i++)
            {
                var range = 0..DICTIONARY.Length;
                while (!range.Start.Equals(range.End.Value))
                {
                    var length = range.End.Value - range.Start.Value;
                    var step = length / 2;
                    if (step == 0)
                    {
                        break;
                    }
                    if ((number & 1) == 0)
                    {
                        range = range.Start..(range.End.Value - step);
                    }
                    else
                    {
                        range = (range.Start.Value + step)..range.End;
                    }
                    number >>= 1;
                }
                span[i] = DICTIONARY[range.Start];
            }
        });
}

100к строк генерирует менее чем за секунду на моём ноуте.
Хотя есть два недостатка (достаточно серьёзных):
1. "1" и "9" - две наиболее редкие цифры
2. последние две цифры - всегда единицы.
Ответ написан
Комментировать
GavriKos
@GavriKos
1. Зачем каждый раз конструировать Random? Делайте это один раз.
2. Вот вся эта кракозябра в for - она как то не попадает под описаное вам в алгоритме. Судя по всему вы зачем то пытаетесь сгенерировать число на основе словаря. Вангую что вот именно эта часть приводит к диким тормозам.
3. Можно использовать не list, а hashset (вроде) - поиск по нему намного быстрее.
4. Лучше в контейнере держать не строку, а все таки число если это возможно - сравнение строк - долгая операция может быть
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы