【问题标题】:Generate 5 length unique code from natural number从自然数生成 5 个长度的唯一代码
【发布时间】:2020-12-10 07:57:26
【问题描述】:

我需要用给定的数字生成一个长度为 5 的唯一代码。换句话说,我需要将自然数编码为 5 长度的唯一代码

我想给客户一个固定长度的可记忆代码,并将序列号保存在数据库中,并在需要时进行编码或解码。

给定的数字可以在 1 到 9999999 的范围内。 但结果总是必须是 5 个长度。

例如

1 => a56er 或者 2 => c7gh4

独特性很重要 我google了很多,我找不到解决办法。

【问题讨论】:

  • 范围问题:数字可以有多大? (例如,如果他们只去 99999,那么您可以只返回数字本身,如果需要,可以填充...)
  • 感谢关注 1 到 9999999 范围内
  • 生成的代码应该是“随机的”吗?即如果 1 是 aaaaa,那么 2 可以是 aaaab 吗?
  • 随机比较好,不过没关系。

标签: c# unique decode encode generated-code


【解决方案1】:

给定的数字可以在 1 到 9999999 的范围内

没错。因此,您需要对 24 位信息进行编码,并且您有 5 个字符可以执行此操作——因此每个字符需要 5 位。这在“仅数字和小写 ASCII 字符”范围内令人愉悦,您甚至可以消除诸如“o/0”和“i/1”之类的混淆点。

请注意,这绝不是“安全的”——它是完全可预测和可逆的。如果您不希望客户能够从编码形式对他们的序列号进行逆向工程,那将是行不通的。但这是一种将数字编码为固定长度字符串的简单方法。

显示编码和解码的示例代码:

using System;
using System.Globalization;

public class Test
{
    static void Main()
    {
        EncodeDecode(10);
        EncodeDecode(100);
        EncodeDecode(1000);
        EncodeDecode(10000);
        EncodeDecode(100000);
        EncodeDecode(1000000);
        EncodeDecode(9999999);
        
        void EncodeDecode(int number)
        {
            string encoded = EncodeBase32(number);
            int decoded = DecodeBase32(encoded);
            Console.WriteLine($"{number} => {encoded} => {decoded}");
        }
   }
    
    private const string Base32Alphabet = 
        "23456789abcdefghjklmnpqrstuvwxyz";
    private static string EncodeBase32(int number)
    {
        // TODO: Range validation
        char[] chars = new char[5];
        for (int i = 0; i < 5; i++)
        {
            chars[i] = Base32Alphabet[number & 0x1f];
            number = number >> 5;
        }
        return new string(chars);
    }
    
    private static int DecodeBase32(string text)
    {
        if (text.Length != 5)
        {
            throw new ArgumentException("Invalid input: wrong length");
        }
        int number = 0;
        for (int i = 4; i >= 0; i--)
        {
            number = number << 5;
            int index = Base32Alphabet.IndexOf(text[i]);
            if (index == -1)
            {
                throw new ArgumentException("Invalid input: invalid character");
            }
            number |= index;
        }
        return number;
    }
}

输出:

10 => c2222 => 10
100 => 65222 => 100
1000 => az222 => 1000
10000 => jsb22 => 10000
100000 => 2p352 => 100000
1000000 => 2ljy2 => 1000000
9999999 => zm7kb => 9999999

【讨论】:

  • 谢谢你,这很好,解决了我的问题。但是有没有办法让它随机?
  • @AliasgharAhmadpour:使其随机并从输入数字生成是矛盾的。它可能是 pseudo-random (因此仍然可以从输入中预测),但您需要提供更多关于您的要求的详细信息,以便为此提供答案。另一种方法是生成存储在数据库中的 truly 随机字符串(尽可能随机,不基于任何输入),并采取适当的保护措施防止重复,但这不是你问的问题。
  • 再次感谢您的关注。给定的解决方案解决了我的问题。但我想知道有没有办法让它不连续?生成的代码是顺序的:0 => 22222 1 => 32222 2 => 42222
  • 我在stackoverflow.com/q/65263378/9221609提出了新问题
  • 不幸的是,它很快被关闭为重复(这个问题)。你能做什么?
【解决方案2】:

根据你的要求,我会先回答你的问题,然后给你我认为最好的解决方案。

因此,根据您的数字 1 到 9999999,您可以使用 SHA256MD5 或任何其他散列函数来生成字符串,然后在字符串的随机部分使用 Substring,以获取您要求的代码。

我个人使用的更简单的方法是忽略用户输入,并使用Guid.NewGuid()函数,它将生成一个16个字符的随机字符串,您可以在该字符串上删除“-”并取5个随机字符用子字符串获取你想要的代码。

Guid.NewGuid() 

以“a869ee3e-13b2-46ce-8c09-ff8998ab9393”之类的方式为您提供代码。然后你申请一个

string.Replace("-")

你得到“a869ee3e13b246ce8c09ff8998ab9393”然后你在字符串中随机取5个字符(只需做一个子字符串并作为起点传递一个介于1和字符串长度-5之间的随机数(如果你想要一个5长字符)。

或者说得更简单

Guid.NewGuid().ToString().Replace("-", "").Substring(x.Next(1,27),5)

这将提供您要求的代码

【讨论】:

  • @MichaelMao 不。这会给你一个字母数字代码,就像问题作者所问的那样。如果你想要一个数字代码,你应该只使用 Random.Next() 并可能应用一个种子来应用更多的“随机化”。我用来获得更多随机性的一种方法是使用 Random.Next 生成大量随机数,每个随机数都有不同的种子,然后使用另一个 Random.Next,从中选择一个。它们仍然是伪随机数,但我从来没有得到相等的数字,这是一个非常困难的可能性。
  • 这里的方法不能保证唯一性。如果您提供示例代码,我很乐意根据它编写一个显示重复的示例。 (您可以通过保留生成的字符串并继续执行直到获得新值来添加唯一性,但此时它实际上并不能回答所提出的问题。)
  • @JonSkeet 将您的示例应用到我在 NewGuid 示例中的最后一个代码中。
  • @LiquidCore:这很容易证明它必须生成重复 - 因为它将是 5 个十六进制字符,导致总共 16^5 = 1,048,576 个可能的结果。当数字在 1 - 9,999,999 范围内时,必须重复。
  • 换句话说,您提供的解决方案将提供超过一百万个不同的代码,而问题将有 1000 万个不同的代码。因此,幸运的是,每当 OP 的数据库中有 200 万个数字时,每个数字都会重复两次。因此,不尊重唯一性。
猜你喜欢
  • 2016-01-16
  • 1970-01-01
  • 2019-10-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-01-14
  • 1970-01-01
  • 2011-08-19
相关资源
最近更新 更多