【问题标题】:How to Generate Unique Number of 8 digits?如何生成唯一的 8 位数字?
【发布时间】:2011-05-23 22:09:39
【问题描述】:

我正在使用此代码生成一个 8 位唯一编号。

byte[] buffer = Guid.NewGuid().ToByteArray();
return BitConverter.ToUInt32(buffer, 8).ToString();

这段代码真的会生成一个唯一的数字,还是会再次重复相同的数字?

【问题讨论】:

  • 您可能需要进一步讨论您的要求;为什么static counter=0; counter++; sprintf(buffer,"%8d", counter); 这样的代码不能完成这项工作?是否必须从 GUID 生成?应该有任何类型的数字分布吗?
  • 没有必要使用 GUID。我使用 GUID 的唯一原因是因为我找到了一些关于 GUID 的好文章,这就是我使用 GUID 的原因。但是如果您有任何其他方式来生成唯一编号,请告诉我。
  • GUID 不是唯一的,它只是不太可能产生两个相同的序列。通过将 GUID 减少到 8 位数字,您可以显着增加重复的机会。请进一步说明您需要唯一编号;如果你想要一个随机数,你不应该使用 GUID,如果你想要一个主键值,你可能应该只增加一个计数器,以确保在重复自己之前使用可用的最大数字范围(你会知道,因为它会抛出一个溢出异常)。
  • 到目前为止,似乎没有一个答案能够解决问题的唯一性要求。
  • 使用jeff.aaron.ca/cgi-bin/birthday,在 8 位数字空间中只有 10,000 个条目发生冲突的几率为 40%。但是,我们仍然不知道这些数字的意图是什么。

标签: c# numbers unique


【解决方案1】:

GUID 不仅仅是一个随机数;它由段组成。如果在同一台计算机上生成 guid,则某些段根本不会更改。通过仅使用原始 128 位中的 64 位,您正在破坏 guid 的结构,并且很可能会破坏生成数字的唯一性。

question 包含有关 guid 唯一性的更多信息,请查看 this link 以及更多信息,了解为什么在需要唯一编号时仅使用 guid 的一部分是不好的。

如果您需要将重复限制在绝对最小值,增量计数器将为您提供所需的内容。如果您的应用程序使用多个线程或进程,则可能很难(甚至不可能)正确实现计数器。

这是 guid 设计的领域,在多台机器上是唯一的。因此,如果需要跨机器的唯一性,则应使用 guid。整个指南。

【讨论】:

  • 你会比从 0 数到 99999999 更快地再次命中相同的随机数。
  • 这不完全正确。第 4 版 GUID(由 Guid.NewGuid() 生成)have exactly 6 bits fixed,其余的是伪随机的。旧版本的 GUID 的行为方式与您描述的一样。
  • 我不会对此投反对票,但随机数生成器仍会产生重复项。要么你需要一个保证所有值不重复枚举的函数,要么你需要内存。
【解决方案2】:

任何随机序列都必然会有一些冲突。这只是时间问题。使用生日悖论公式,有 100,000,000 个可能的值(8 位),只有 10,000 个元素发生碰撞的可能性约为 40%,而 30,000 个元素发生碰撞的可能性约为 99%。 (see here for a calculator)。

如果您确实需要随机序列,则不应为此使用 GUID。 GUID 具有非常具体的结构,只能作为一个整体来考虑。创建一个随机的 8 位序列发生器很容易。这应该会给你一个 8 位的序列:

 public string Get8Digits()
 {
   var bytes = new byte[4];
   var rng = RandomNumberGenerator.Create();
   rng.GetBytes(bytes);
   uint random = BitConverter.ToUInt32(bytes, 0) % 100000000;
   return String.Format("{0:D8}", random);
 }

您也可以将 RandomNumberGenerator 放在某个地方以避免每次都创建一个新的。

【讨论】:

  • 真的,这绝不是重复的。或者它在 2 或 4 年后重复......请告诉......
【解决方案3】:

这是另一个版本

public static string GetFormNumber()
    {
        byte[] buffer = Guid.NewGuid().ToByteArray();
        var FormNumber = BitConverter.ToUInt32(buffer, 0) ^ BitConverter.ToUInt32(buffer, 4) ^ BitConverter.ToUInt32(buffer, 8) ^ BitConverter.ToUInt32(buffer, 12);
        return FormNumber.ToString("X");

    }

保证独一无二!

【讨论】:

  • 使用 linqpad 为 1000000 条记录生成了它,并得到了 119 个重复项。为相同数量的记录再生成几次,重复计数通常为 110 和 200。但仍然令人印象深刻。如果只有一种方法可以删除重复项而无需与先前保存的值进行比较。使用的代码是Enumerable.Range(1,1000000).Select(x=>GetFormNumber()).GroupBy (x => x).Where(x=>x.Count()>1).Dump();
【解决方案4】:

我的第一个答案没有解决唯一性问题。我的第二个:

static int counter;
public static int GetUniqueNumber()
{ 
    return counter++; 
}

如果您希望在应用重新启动时拥有唯一编号,则需要在每次调用 GetUniqueNumber 后将计数器的值保存到数据库或其他地方。

【讨论】:

  • 为了进一步解释 Hans 的评论,在多线程环境中互锁是必要的,因为 2 个线程可能以相同的唯一编号结束,因为加法不是原子操作。声明 counter volatile 也可以解决这个问题。
  • @WilliamMorrison if i lock 会阻止线程的计数器,不是吗?
  • @Sid 你是对的,这也可以防止问题发生。
【解决方案5】:

值的范围太小。递增计数器是最好的解决方案,就像在 ERP 系统中一样 - 您将第一个客户编号设置为 1000,下一个是 1001、1002、...、99999999。否则,如果您从中获得一个随机数(或 GUID 的一部分),您将再次遇到相同的数字。根据您的应用,迟早会发生,但肯定会比仅仅迭代它们更快。

【讨论】:

    【解决方案6】:

    这个方法会生成一个随机字符串,它不依赖于Random方法,也比guid方法好很多:

    public static string gen_Digits(int length)
    {
        var rndDigits = new System.Text.StringBuilder().Insert(0, "0123456789", length).ToString().ToCharArray();
        return string.Join("", rndDigits.OrderBy(o => Guid.NewGuid()).Take(length));
    }
    

    您可以增加长度以减少碰撞机会,但要获得 100% 唯一的序列,您必须保留旧生成的值并检查新创建的值的唯一性。

    【讨论】:

      【解决方案7】:

      如果您想要一个介于 10000000 和 99999999 之间的唯一数字,请从 10000000 开始一个整数,然后开始递增它。生成按顺序排列的数字的随机性不亚于任何其他生成的序列,而且更容易生成。

      【讨论】:

        【解决方案8】:

        如果您将编号表示为日(2 位)、小时(2 位)、分钟(2 位)、秒(2 位)和年(4 位)的组合,那么它将是 12 位但始终是唯一的编号。

         DateTime _now = DateTime.Now;
         string _dd = _now.ToString("dd"); //
         string _mm = _now.ToString("MM");
         string _yy = _now.ToString("yyyy");
         string _hh = _now.Hour.ToString();
         string _min = _now.Minute.ToString();
         string _ss = _now.Second.ToString();
        
         string _uniqueId= _dd+ _hh+ _mm+_min+_ss + _yy;
        

        【讨论】:

        • 年份可以由2位数字组成,使总编号。 10 位数,因为有机会匹配 2 号。 100 年后。
        • 好极了,但 OP 说 8 位数
        • 在同一秒内多次调用它不会给你一个唯一的 id。即使添加微秒也不能保证这一点。
        • 是的,那是真的,那么你必须根据概率来考虑微米或纳米,但如果是唯一的没有。是由用户点击生成的,然后我认为一秒钟就可以了。
        【解决方案9】:
        System.Threading.Thread.Sleep(1);
        long code = (long)DateTime.UtcNow.Subtract(new DateTime(2018, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalMilliseconds;
        

        System.Threading.Thread.Sleep(1000);
        long code = (long)DateTime.UtcNow.Subtract(new DateTime(2018, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalSeconds;
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2015-07-29
          • 2011-01-28
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多