Случайно распределенная функция генератора биективного отображения для генерации строки - PullRequest
0 голосов
/ 27 июня 2018

Я пытаюсь создать функцию Python generator, чтобы получить биективное (Injective & Surjective) отображение между последовательными числами от 1 до 38 5 и случайно распределенными произведениями буквенно-цифровых символов (28 строчных букв + 10 номеров) длиной 5
например что-то вроде этого:

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

есть ли модуль для этого?
Есть идеи по алгоритму?


Редактировать: Я хочу, чтобы отображение было:

  • Реализуемо с использованием генераторов (не требует много памяти)
  • Константа при разных запусках кода
  • Равномерно распределено в максимально возможной степени

Итак, в одном предложении я хочу Uniformly distributed constant bijective mapping between indexes and products of n alpha-numerics

Спасибо

1 Ответ

0 голосов
/ 17 октября 2018

Хорошо, вот биективное отображение от индекса до 5-байтовой строки и обратно на основе Линейного конгруэнтного генератора . Я использовал константы для drand48, кажется, работает нормально для 40 бит LCG, с семян 1, я проверил все 2 40 значений и полный период.

Он основан на 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')
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...