Подтвердить что ты не робот

Как генерировать уникальные целые числа на основе идентификаторов GUID

Можно ли генерировать (очень вероятный) уникальный Integer из GUID?

int i = Guid.NewGuid().GetHashCode();

int j = BitConverter.ToInt32(Guid.NewGuid().ToByteArray(), 0);

Какой из них лучше?

4b9b3361

Ответ 1

Эрик Липперт сделал очень интересный (как всегда) пост о вероятности вероятности хеш-коллизий.

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

Probability of hash collisions

В связи с вашим конкретным вопросом, я бы тоже пошел с GetHashCode, поскольку столкновения неизбежны в любом случае.

Ответ 2

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

Но, как я уверен, вы знаете, хеширование 128 бит информации в 32 бита информации отбрасывает много данных, поэтому почти наверняка будут столкновения, если у вас достаточно большое количество идентификаторов GUID.

Ответ 3

Вот простейший способ:

Guid guid = Guid.NewGuid();
Random random = new Random();
int i = random.Next();

Вы заметите, что guid на самом деле не используется здесь, главным образом потому, что не будет смысла использовать его. Алгоритм Microsoft GUID больше не использует MAC-адрес компьютера - GUID фактически генерируется с использованием псевдослучайного генератора (основанного на значениях времени), поэтому, если вы хотите случайное целое число, имеет смысл использовать класс Random для этого.

Обновление:, используя GUID для генерации int, вероятно, будет хуже, чем просто использовать Random ( "хуже" в том смысле, что это скорее сгенерирует коллизии), Это связано с тем, что не все 128 бит в GUID являются случайными. В идеале вы хотели бы исключить не изменяющиеся биты из функции хеширования, хотя было бы намного проще просто создать случайное число, как я уже говорил ранее.:)

Ответ 4

GUID - это 128-битное целое число (его только в шестнадцатеричном виде, а не в базе 10). С .NET 4 используйте http://msdn.microsoft.com/en-us/library/dd268285%28v=VS.100%29.aspx так:

// Turn a GUID into a string and strip out the '-' characters.
BigInteger huge = BigInteger.Parse(modifiedGuidString, NumberStyles.AllowHexSpecifier)

Если у вас нет .NET 4, вы можете посмотреть IntX или Solver Foundation.

Ответ 5

Поскольку пространство GUID больше, чем число 32-битных целых чисел, у вас гарантировано наличие конфликтов, если у вас достаточно GUID. Учитывая, что вы понимаете это и готовы справиться с столкновениями, как бы редко, GetHashCode() предназначен именно для этой цели и должен быть предпочтительным.

Ответ 6

В статическом классе сохраняйте статическую константу integer, затем добавьте 1 к ней перед каждым отдельным доступом (используя свойство public get). Это обеспечит вам цикл всего диапазона int, прежде чем вы получите не уникальное значение.

    /// <summary>
    /// The command id to use. This is a thread-safe id, that is unique over the lifetime of the process. It changes
    /// at each access.
    /// </summary>
    internal static int NextCommandId
    {
        get
        {
            return _nextCommandId++;
        }
    }       
    private static int _nextCommandId = 0;

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

Ответ 7

Если вы хотите пробить барьер 2 ^ 32, попробуйте этот метод:

/// <summary>
/// Generate a BigInteger given a Guid. Returns a number from 0 to 2^128
/// 0 to 340,282,366,920,938,463,463,374,607,431,768,211,456
/// </summary>
    public BigInteger GuidToBigInteger(Guid guid)
    {
        BigInteger l_retval = 0;
        byte[] ba = guid.ToByteArray();
        int i = ba.Count();
        foreach (byte b in ba)
        {
            l_retval += b * BigInteger.Pow(256, --i);
        }
        return l_retval;
    }

Вселенная распадается на холодное и темное пространство, прежде чем столкнется с конфликтом.

Ответ 9

У меня было требование, чтобы несколько экземпляров консольного приложения нуждались в получении уникального целочисленного идентификатора. Он используется для идентификации экземпляра и назначается при запуске. Поскольку .exe запускается руками, я решил использовать решение, используя отметки времени начала.

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

Я устанавливаю дату на 0001-01-01, добавляю текущее время и деля отметки на 10000 (потому что я не устанавливаю микросекунды), чтобы получить число, которое достаточно мало, чтобы вписаться в целое число.

 var now = DateTime.Now;
 var zeroDate = DateTime.MinValue.AddHours(now.Hour).AddMinutes(now.Minute).AddSeconds(now.Second).AddMilliseconds(now.Millisecond);
 int uniqueId = (int)(zeroDate.Ticks / 10000);

ИЗМЕНИТЬ: Есть некоторые оговорки. Чтобы сделать конфликты маловероятными, убедитесь, что:

  • Экземпляры запускаются вручную (более чем на миллисекунду)
  • Идентификатор генерируется один раз для каждого экземпляра при запуске
  • Идентификатор должен быть уникальным только для других экземпляров, которые в настоящее время запущены.
  • Только небольшое количество идентификаторов будет когда-либо понадобиться