Python 生成长度为n的唯一字符串,而无需预先填充字典

Python 生成长度为n的唯一字符串,而无需预先填充字典,python,algorithm,Python,Algorithm,我有一个类似URL缩短器的应用程序,需要在用户请求时生成唯一的URL 为此,我需要一个函数将索引/编号映射到长度为n的唯一字符串,并满足两个要求: 两个不同的数字不能生成相同的字符串。 换句话说,只要i,j你就可以尝试基于sha1散列的东西 #!/usr/bin/python3 import hashlib def generate_link(i): n = 7 a = "abcdefghijklmnopqrstuvwxyz01234567890"

我有一个类似URL缩短器的应用程序,需要在用户请求时生成唯一的URL

为此,我需要一个函数将索引/编号映射到长度为n的唯一字符串,并满足两个要求:

两个不同的数字不能生成相同的字符串。
换句话说,只要i,j你就可以尝试基于sha1散列的东西

#!/usr/bin/python3

import hashlib

def generate_link(i):
    n = 7
    a = "abcdefghijklmnopqrstuvwxyz01234567890"
    return "".join(a[x%36] for x in hashlib.sha1(str(i).encode('ascii')).digest()[-n:])

您可以尝试基于sha1散列的内容

#!/usr/bin/python3

import hashlib

def generate_link(i):
    n = 7
    a = "abcdefghijklmnopqrstuvwxyz01234567890"
    return "".join(a[x%36] for x in hashlib.sha1(str(i).encode('ascii')).digest()[-n:])
那么:

from random import Random

n = 7


def f(i):
    myrandom = Random()
    myrandom.seed(i)
    alphabet = "123456789"
    return "".join([myrandom.choice(alphabet) for _ in range(7)])


# same entry, same output
assert f(0) == "7715987"
assert f(0) == "7715987"
assert f(0) == "7715987"

# different entry, different output
assert f(1) == "3252888"
更改字母表以符合您的需要


这将模拟UUID,因为您说过您可以接受一个很小的冲突机会。如果要避免冲突,您真正需要的是一个完美的哈希函数。

如何:

from random import Random

n = 7


def f(i):
    myrandom = Random()
    myrandom.seed(i)
    alphabet = "123456789"
    return "".join([myrandom.choice(alphabet) for _ in range(7)])


# same entry, same output
assert f(0) == "7715987"
assert f(0) == "7715987"
assert f(0) == "7715987"

# different entry, different output
assert f(1) == "3252888"
更改字母表以符合您的需要


这将模拟UUID,因为您说过您可以接受一个很小的冲突机会。如果您想避免冲突,您真正需要的是一个完美的哈希函数。

这是我在中概述的一个非常简单的示例。它只是根据i来偏移数字。如果您想要不同的字符串,不要使用它,因为如果num是0,那么您将得到n=7的abcdefg


您只需要将num+i更改为一些复杂而模糊的数学公式。

这是我在中概述的一个非常简单的示例。它只是根据i来偏移数字。如果您想要不同的字符串,不要使用它,因为如果num是0,那么您将得到n=7的abcdefg


您只需要将num+i更改为一些复杂而模糊的数学公式。

我将为您概述一个解决方案,即使是知识渊博的人也不会随意检查,尽管它可能在加密方面不安全

首先,字符串和数字在一对一的映射中。下面是一些简单的代码

alphabet = 'abcdefghijklmnopqrstuvwxyz'
len_of_codes = 7
char_to_pos = {}
for i in range(len(alphabet)):
    char_to_pos[alphabet[i]] = i

def number_to_string(n):
    chars = []
    for _ in range(len_of_codes):
        chars.append(alphabet[n % len(alphabet)])
        n = n // len(alphabet)
    return "".join(reversed(chars))

def string_to_number(s):
    n = 0
    for c in s:
        n = len(alphabet) * n + char_to_pos[c]
    return n
所以现在你的问题是如何得到一个上升的数字流,并从中得到一个明显随机的数字流。因为你知道如何把它们变成弦。好的,素数有很多技巧,所以让我们找到一个合适大小的素数,适合你想要的范围

def is_prime (n):
    for i in range(2, n):
        if 0 == n%i:
            return False
        elif n < i*i:
            return True
    if n == 2:
        return True
    else:
        return False

def last_prime_before (n):
    for m in range(n-1, 1, -1):
        if is_prime(m):
            return m

print(last_prime_before(len(alphabet)**len_of_codes)
选择一个随机数进行置乱,intrandom.random*26**7碰巧给了我3661807866,我们得到了一个可以计算的序列:

for i in range(1, 5):
    print(number_to_string(scramble1(8031810103, 3661807866, i)))
这给了我们

lwfdjoc
xskgtce
jopkctb
vkunmhd
这看起来是随机检查。但对于任何有知识的人来说,只要他付出了适度的努力,这是可逆的。他们只需要猜测我们使用的素数和算法,看两个连续的值来得到隐藏的参数,然后看更多的值来验证它


在解决这个问题之前,让我们先了解如何获取字符串并返回数字。多亏了我们对p素数和1的了解,我将为您概述一个解决方案,即使是有知识的人也不会随意检查,尽管它可能不是加密安全的

首先,字符串和数字在一对一的映射中。下面是一些简单的代码

alphabet = 'abcdefghijklmnopqrstuvwxyz'
len_of_codes = 7
char_to_pos = {}
for i in range(len(alphabet)):
    char_to_pos[alphabet[i]] = i

def number_to_string(n):
    chars = []
    for _ in range(len_of_codes):
        chars.append(alphabet[n % len(alphabet)])
        n = n // len(alphabet)
    return "".join(reversed(chars))

def string_to_number(s):
    n = 0
    for c in s:
        n = len(alphabet) * n + char_to_pos[c]
    return n
所以现在你的问题是如何得到一个上升的数字流,并从中得到一个明显随机的数字流。因为你知道如何把它们变成弦。好的,素数有很多技巧,所以让我们找到一个合适大小的素数,适合你想要的范围

def is_prime (n):
    for i in range(2, n):
        if 0 == n%i:
            return False
        elif n < i*i:
            return True
    if n == 2:
        return True
    else:
        return False

def last_prime_before (n):
    for m in range(n-1, 1, -1):
        if is_prime(m):
            return m

print(last_prime_before(len(alphabet)**len_of_codes)
选择一个随机数进行置乱,intrandom.random*26**7碰巧给了我3661807866,我们得到了一个可以计算的序列:

for i in range(1, 5):
    print(number_to_string(scramble1(8031810103, 3661807866, i)))
这给了我们

lwfdjoc
xskgtce
jopkctb
vkunmhd
这看起来是随机检查。但对于任何有知识的人来说,只要他付出了适度的努力,这是可逆的。他们只需要猜测我们使用的素数和算法,看两个连续的值来得到隐藏的参数,然后看更多的值来验证它


在解决这个问题之前,让我们先了解如何获取字符串并返回数字。由于我们知道p素数和1,所以基本上,对于长度为n的每个数字,都需要一个唯一的字符串?如果我有两个相同的数字,我会有不同的字符串吗?对。相同的数字总是生成相同的字符串。这是为了确保每个请求增加的新数字不会生成与以前生成的字符串冲突的字符串。正确,只是我希望AAAA B看起来与AAAA更不同,这样用户就无法预测下一个生成字符串的模式。要求2您正在寻找唯一的哈希样式ID。通常的编码方向是将字符串转换为整数;你正朝相反的方向走。然而,原理是一样的:获取一些输入,以不容易反转的方式对其进行操作,然后生成一个输出。您可以将任何字符串编码为整数,将任何整数编码为以字符串为基数的26位十进制转换,因此这些算法将解决您的问题。请继续你的研究。@Prune措辞完美。为了以不容易反转的方式操纵它,可以使用一些模糊的数学公式

将一个数字作为输入,输出一个字符,并将其与一个微分数学方程一起使用n次的运算,每次使用一个微分数学方程或对原始方程的修改。这会使它看起来是随机的,但对相同的数字保留相同的字符串?如果我有两个相同的数字,我会有不同的字符串吗?对。相同的数字总是生成相同的字符串。这是为了确保每个请求增加的新数字不会生成与以前生成的字符串冲突的字符串。正确,只是我希望AAAA B看起来与AAAA更不同,这样用户就无法预测下一个生成字符串的模式。要求2您正在寻找唯一的哈希样式ID。通常的编码方向是将字符串转换为整数;你正朝相反的方向走。然而,原理是一样的:获取一些输入,以不容易反转的方式对其进行操作,然后生成一个输出。您可以将任何字符串编码为整数,将任何整数编码为以字符串为基数的26位十进制转换,因此这些算法将解决您的问题。请继续你的研究。@Prune措辞完美。为了以一种不容易反转的方式操作它,您可以使用一些模糊的数学方程,将数字作为输入并输出一个字符,并将其平铺为n次,每次使用一个微分数学方程或对原始方程的修改。这会使它看起来是随机的,但对相同的数字保留相同的字符串。请注意,如果您希望原始数字不能进行反向工程,那么现在sha1不够安全。有关更多信息,请参阅stackoverflow问题。如果您基于哈希进行某些操作,您将面临一个生日问题。也就是说,在您可能的搜索空间的sqrt附近,您将开始获得较高的碰撞概率。这个搜索空间大约有90k。在这之后,预期的碰撞次数以二次方的方式增加。@B我同意你可能会得到重复的碰撞,但这与使用随机模块没有什么不同。我运行了一个测试来生成第一个1m链接,使用sha1和n=7,有7个重复,使用n=8,有0个重复,它在2秒内运行。使用random和n=7时,我又得到了7个副本,但运行时花了90秒,n=8时有1个副本。@B我刚刚读了你的答案,这是一个很棒的算法,速度非常快,不会产生副本。请注意,如果你想让原始数字无法反向工程,sha1现在就不够安全。有关更多信息,请参阅stackoverflow问题。如果您基于哈希进行某些操作,您将面临一个生日问题。也就是说,在您可能的搜索空间的sqrt附近,您将开始获得较高的碰撞概率。这个搜索空间大约有90k。在这之后,预期的碰撞次数以二次方的方式增加。@B我同意你可能会得到重复的碰撞,但这与使用随机模块没有什么不同。我运行了一个测试来生成第一个1m链接,使用sha1和n=7,有7个重复,使用n=8,有0个重复,它在2秒内运行。在n=7的情况下使用random,我又得到了7个重复项,但运行起来花了90秒,在n=8的情况下,有1个重复项。@B我刚刚读了你的答案,这是一个很好的算法,速度很快,并且不会产生重复项。这模拟了UUID,因为你说你可以接受一个很小的碰撞机会。如果你想避免碰撞,您真正需要的是一个完美的哈希函数。请编辑您的答案以包含这些信息,而不是将其作为注释发布。我还是一个新手,感谢您纠正我:这模拟UUID,因为您说过您可以接受一个小的冲突机会。如果您想避免冲突,您真正需要的是一个完美的哈希函数。请编辑您的答案以包含这些信息,而不是将其作为评论发布。我还是一个新手,谢谢您纠正我:
ehidzxf
shsifyl
gicmmcm
ofaroeg
def decode (s):
    n = string_to_number(s)
    for p, k in [
            (8031810103, 3319920713)
          , (8031810097, 4707272543)
          , (8031810091, 5077139687)
          , (8031809963, 192273749)
          , (8031809917, 5986071506)
        ]:
        n = scramble1(p, k, n)

    return n