随机分布的双射映射生成器函数用于字符串生成

2024-09-28 20:39:31 发布

您现在位置:Python中文网/ 问答频道 /正文

我正在尝试创建一个python generator函数来生成1到385之间的双射(内射&满射)映射,以及长度为5的字母数字(28个小写字母+10个数字)的随机分布乘积 例如如下:

1 -> 4fde6
2 -> grt74
3 -> g7w33
...

有这个模块吗?
对算法有什么想法吗?在


编辑:我希望映射为:

  • 可使用生成器实现(不是内存密集型)
  • 在不同的代码运行期间保持不变
  • 尽可能均匀分布

所以在一句话里我想要一个Uniformly distributed constant bijective mapping between indexes and products of n alpha-numerics

谢谢


Tags: 模块函数内存算法编辑字母数字generator
1条回答
网友
1楼 · 发布于 2024-09-28 20:39:31

好的,这里是从索引到5bytes字符串的双向映射,基于Linear Congruential Generator。 我对drand48使用了常量,对于40位的LCG来说似乎很好,种子1我测试了所有的 240值,全周期。在

它依赖于带掩码溢出的无符号64位数学,因此大量使用NumPy。 包装和拆箱的方式有些粗糙,还有很多事情需要改进。问问题不要犹豫。在

import numpy as np

# unsigned constants
ZERO  = np.uint64(0)
ONE   = np.uint64(1)

# signed constants
SZERO = np.int64(0)
SONE  = np.int64(1)
MONE  = np.int64(-1)

# LCG parameters
bits = np.uint64(40)
mult = np.uint64(25214903917)
incr = np.uint64(11)

mod  = np.uint64( np.left_shift(ONE, bits) )
mask = np.uint64(mod - ONE)

def rang(seed: np.uint64) -> np.uint64:
    """
    LCG mapping from one 40bit integer to another
    """
    return np.uint64(np.bitwise_and(mult*np.uint64(seed) + incr, mask))

def compute_nskip(nskp: np.int64) -> np.int64:
    nskip: np.int64 = nskp

    while nskip < SZERO:
        t: np.uint64 = np.uint64(nskip) + mod
        nskip = np.int64(t)

    return np.int64( np.bitwise_and(np.uint64(nskip), mask) )

def skip(nskp: np.int64, seed: np.uint64) -> np.uint64: # inverse mapping
    """
    Jump from given seed by number of skips
    """
    nskip: np.int64 = compute_nskip(nskp)

    m: np.uint64 = mult # original multiplicative constant
    c: np.uint64 = incr # original additive constant

    m_next: np.uint64 = ONE  #  new effective multiplicative constant
    c_next: np.uint64 = ZERO # new effective additive constant

    while nskip > SZERO:
        if np.bitwise_and(nskip, SONE) != SZERO: # check least significant bit for being 1
            m_next = np.bitwise_and(m_next * m, mask)
            c_next = np.bitwise_and(c_next * m + c, mask)

        c = np.bitwise_and((m + ONE) * c, mask)
        m = np.bitwise_and(m * m, mask)

        nskip = np.right_shift(nskip, SONE) # shift right, dropping least significant bit

    # with G and C, we can now find the new seed
    return np.bitwise_and(m_next * seed + c_next, mask)

def index2bytes(i: np.uint64) -> bytes:
    bbb: np.uint64 = rang(i)

    rc = bytearray()
    rc.append( np.uint8( np.bitwise_and(bbb, np.uint64(0xFF)) ) )
    bbb = np.right_shift(bbb, np.uint64(8))
    rc.append( np.uint8( np.bitwise_and(bbb, np.uint64(0xFF)) ) )
    bbb = np.right_shift(bbb, np.uint64(8))
    rc.append( np.uint8( np.bitwise_and(bbb, np.uint64(0xFF)) ) )
    bbb = np.right_shift(bbb, np.uint64(8))
    rc.append( np.uint8( np.bitwise_and(bbb, np.uint64(0xFF)) ) )
    bbb = np.right_shift(bbb, np.uint64(8))
    rc.append( np.uint8( np.bitwise_and(bbb, np.uint64(0xFF)) ) )

    return rc

def bytes2index(a: bytes) -> np.uint64:
    seed: np.uint64 = ZERO
    seed += np.left_shift( np.uint64(a[0]), np.uint64(0))
    seed += np.left_shift( np.uint64(a[1]), np.uint64(8))
    seed += np.left_shift( np.uint64(a[2]), np.uint64(16))
    seed += np.left_shift( np.uint64(a[3]), np.uint64(24))
    seed += np.left_shift( np.uint64(a[4]), np.uint64(32))

    return skip(MONE, seed)

# main part, silence overflow warnings first
np.warnings.filterwarnings('ignore')

bbb = index2bytes(ONE)
print(bbb)
idx = bytes2index(bbb)
print(idx)

bbb = index2bytes(999999)
print(bbb)
idx = bytes2index(bbb)
print(idx)

bbb = b'\xa4\x3c\xb1\xfc\x79'
idx = bytes2index(bbb)
print(idx)
bbb = index2bytes(idx)
print(bbb)
print(bbb == b'\xa4\x3c\xb1\xfc\x79')

相关问题 更多 >