Генерировать Случайный Уникальный Код

Мне нужно сгенерировать девятизначный цифровой код (предпочтительно случайный), который уникален для данного дня (то же число не может быть сгенерировано снова в тот же день). Я думал использовать HHMMSSmmm (часы, минуты, секунды и миллисекунды) для генерации уникального кода, но на самом деле это не случайно. Этот метод генерации кода может быть доступен несколькими методами одновременно, поэтому мне придется заблокировать метод. Но будет ли это гарантировать, что число является уникальным, как это возможно, что число генерация может занять менее миллисекунды, и два потока получат одинаковое число?

есть ли лучший способ генерировать случайный уникальный цифровой код, который уникален для данного дня? Количество цифр может быть от 6 до 9 цифр.

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

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

генерация случайных чисел должна происходить в ASP.NET приложение MVC.

13 ответов


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

вот пример того, о чем я говорю:

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

Если вы запустите его несколько раз, вы увидите, что произойдет. У обоих будут свои уникальные номера.

Это не будет хранить все полученные цифры, как требуется!

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

using System;
using System.IO;
namespace _5893408
{
    class Program
    {
        static void Main(string[] args)
        {
            Random rand = new Random();
            var futureTime = DateTime.Now.AddSeconds(60);
            while (DateTime.Now < futureTime)
                Console.WriteLine(GetNextNumber(rand));
        }

        public static int GetNextNumber(Random rand)
        {
            var now = DateTime.Now;
            string filePath = @"C:\num.txt";
            FileStream fileStream = null;
            while (fileStream == null)
            {
                try { fileStream = File.Open(filePath, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None); }
                catch { }
            }
            using (fileStream)
            {
                DateTime date;
                int prevNum;
                if (fileStream.Length == 0)
                {
                    date = now;
                    prevNum = rand.Next(100000, 999999);
                }
                else
                {
                    var reader = new StreamReader(fileStream);
                    {
                        date = DateTime.Parse(reader.ReadLine());
                        prevNum = int.Parse(reader.ReadLine());
                    }
                    if (date.DayOfYear != now.DayOfYear)
                        prevNum = rand.Next(100000, 999999);
                }
                int nextNum = prevNum + rand.Next(10, 1000);
                fileStream.Seek(0, SeekOrigin.Begin);
                using (var writer = new StreamWriter(fileStream))
                {
                    writer.WriteLine(now);
                    writer.WriteLine(nextNum);
                }
                return nextNum;
            }
        }
    }
}

Я думаю, что это соответствует вашим требованиям... я не так?

Если я, просто скажите, и я постараюсь помочь больше.


должен ли он быть уникальным только в рамках процесса?

любая причина не просто держать счетчик, который вы увеличиваете атомарно каждый раз, а затем сбросить его, если дата переворачивается?


Я

  1. генерировать набор уникальных случайных чисел в начале каждого дня, а точнее, перед каждым днем в соответствующее время

  2. последовательно возьмите число из стека каждый раз, когда это необходимо


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

public class NumMaker
{
  static long num=0;
  public static synchronized next()
  {
    return ++num;
  }
}

Если есть несколько JVMs, проще всего было бы сохранить номер в базе данных и использовать блокировку базы данных, чтобы сохранить номера уникальными.

обновление

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

в зависимости от того, что вы пытаетесь выполнить, вы можете придумать схема, которая присваивает номера не последовательно, но в жесткой последовательности, поэтому для некоторых целей они будут казаться случайными. Например, вы можете увеличить число, которое очень велико по отношению к максимуму и относительно простое с максимумом, а затем каждый раз, когда следующее приращение будет проходить, вычесть максимум. Например, чтобы уменьшить его, предположим, что вы назначаете 2-значные числа вместо 9 цифр. Прирост на 37. Затем вы назначаете 37, 74, 111 обертывания 11, 48, 85, 122 обертывания 22 и т. д.


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

поэтому, если код просто должен быть уникальным в день, просто используйте дату в качестве кода...

возможно, YYYYmmdd, который даст вам (для сегодняшней даты) 20110505, а завтра будет 20110506.


вы НЕ МОГУ убедитесь, что случайные числа не будут повторяться. (потому что они случайны)

без сравнения с уже сгенерированными числами вы можете иметь:

  • случайных чисел!--1-->или
  • уникальный номер.

вам нужен уникальный номер с некоторым случайным взглядом. Если миллисекунд с начала дня не хватает случайного взгляда, попробуйте объединить его с другое уникальная число.

например:

  • объединить количество миллисекунд, и
  • атомарный счетчик (что такое приращение на единицу каждый раз, когда вы генерируете число)

например, когда вы суммируете их, вы можете создать: 999999999-86400000 = 913599999 уникальных номеров в день.

хотя они не будут случайными, они будут уникальными - и предсказуемыми только в 00:00.

вот варианты для этого, например не сброс счетчика в 00: 00.


Как насчет модифицированной версии Перемешать Сумка. Вот как это будет работать -

  1. перед началом дня вы кладете N различных чисел, удовлетворяющих вашему критерию, в сумку для перетасовки
  2. в течение дня вы запрашиваете номер из сумки shuffle.
  3. Shuffle bag дает вам случайное число из мешка и отбрасывает его - т. е. не будет возвращать тот же номер снова.
  4. в конце дня будет ясно сумка готова к следующему дню.

преимущества

  • гарантирует, что номер не используется повторно, без проверки существующего списка
  • цифры будут случайными, без какой-либо последовательности
  • применить простые правила здравомыслия для инициализации перетасовать мешок, например, нет общих / повторяющихся последовательностей не допускается (1111111 или 123456789)
  • просто инициализировать shuffle bag - используйте случайные последовательные номера. т. е. начните с шестизначного числа, продолжайте добавлять небольшое случайное число для инициализации мешка.
  • легко изменить размер сумка на основе использования.
  • очень простая потокобезопасная реализация в C#.

первоисточник здесь - модифицированная версия может служить вашей цели.


добавьте идентификатор потока в конец кода для работы с параллелизмом.


Используйте Guid.NewGuid (), чтобы получить что-то вроде:

0f8fad5b-d9cb-469f-a165-70867728950e

затем избавьтесь от '- ' s и преобразуйте Буквы в их ASCII-аналоги. (где a=97)

затем преобразовать в число.


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

HHMMSS+NNN-дает вам пространство для 999 случайных чисел в секунду.

HH+NNNNNNNN-дает вам пространство для 9999999 случайных чисел в час. Так далее.

Если распределение вызовов метода генерации чисел во времени равномерно, то любой шаблон почти равен.

в любом случае, учитывая ограничение длины случайного числа, всегда есть ограничение на количество вызовов метода перед столкновениями будет apper. Е. Г. если метод вызывается > 1000 раз в секунду.


я продолжил вашу идею использовать текущее время, я просто добавил многопоточную синхронизацию и хранение использованных randoms для сравнения всех следующих randoms с ними, чтобы обеспечить уникальность.

private static DateTime DateInArray = DateTime.Today;
private static ICollection<string> UsedTodayRandoms = new List<string>();

[MethodImpl(MethodImplOptions.Synchronized)]
public static string RandomUniqueToday()
{
    if (! DateTime.Today.Equals(DateInArray) ) {
        UsedTodayRandoms.Clear();
        DateInArray = DateTime.Today;
    }

    string result = null;
    DateTime timeToGenerateUnique = DateTime.Now;
    do
    {
        result = timeToGenerateUnique.ToString("HHmmssfff");
        timeToGenerateUnique = timeToGenerateUnique.AddMilliseconds(-1);
    } while (UsedTodayRandoms.Contains(result));
    UsedTodayRandoms.Add(result);

    return result;
}

public double GetRandomNumber() {object operation = new object(); lock(operation) { return new Random().NextDouble(); } } будет делать.

Это не зависит от дня/времени, следовательно, его еще более случайным, тогда ваше требование. Теперь тот факт, что число меньше 1, я оставлю вам в качестве упражнения ...

кроме того, если вы хотите отслеживать все числа, сгенерированные для данного дня - сохранить список из них и регенерировать, если создается дубликат. Но я не буду писать для вас, так как вы программист в вашей ситуации ...


в зависимости от того, сколько случайности требуется, a линейный конгруэнтный генератор с соответствующими параметрами может быть то, что вы ищете. Например, следуя рекомендациям, приведенным в Википедии о длине периода, набор параметров, которые могут работать для вас: M=1000000000, a=21, c=3, затем используйте любое начальное семя X0 в [0..999999999], и вычислить Xn+1=(a * Xn+c)%M. Это генерирует последовательность Xn С периодом M, т. е. последовательность генерирует все числа в [0..999999999] ровно один раз, прежде чем начать повторять.