将整数转换为随机但可确定的可重复选择

问题描述 投票:7回答:4

如何将无符号整数(表示用户ID)转换为随机查找但实际上是确定性可重复的选择?必须以相同的概率选择选择(不管输入整数的分布如何)。例如,如果我有3个选择,即[0, 1, 2],则用户ID 123可以总是随机分配选项2,而用户ID 234可以总是被分配选择1。

需要跨语言和跨平台的算法再现性。除非有更好的方法,否则我倾向于使用哈希函数和模数。这是我有的:

>>> num_choices = 3
>>> id_num = 123
>>> int(hashlib.sha256(str(id_num).encode()).hexdigest(), 16) % num_choices
2

我正在使用最新的稳定Python 3.请注意,这个问题与convert a string to random but deterministically repeatable uniform probability的相关问题相似但不完全相同。

python random hashlib
4个回答
7
投票

Using hash and modulo

import hashlib

def id_to_choice(id_num, num_choices):
    id_bytes = id_num.to_bytes((id_num.bit_length() + 7) // 8, 'big')
    id_hash = hashlib.sha512(id_bytes)
    id_hash_int = int.from_bytes(id_hash.digest(), 'big')  # Uses explicit byteorder for system-agnostic reproducibility
    choice = id_hash_int % num_choices  # Use with small num_choices only
    return choice

>>> id_to_choice(123, 3)
0
>>> id_to_choice(456, 3)
1

笔记:

  • 不得使用内置的hash方法,因为它可以保留输入的分布,例如,与hash(123)。或者,它可以返回重新启动Python时不同的值,例如与hash('123')
  • 为了将int转换为字节,bytes(id_num)可以正常工作,但由于它返回一个空字节数组,因此效率极低,因此不能使用它。使用int.to_bytes更好。使用str(id_num).encode()工作,但浪费几个字节。
  • 不可否认,使用modulo并不能提供完全一致的概率,但是这不应该对这个应用有很大的偏差,因为[1]预计会非常大,而[2]被认为是小的。

Using random

id_hash_int模块可以与num_choices一起使用,同时解决围绕random和连续性的问题。以这种方式使用id_num与对种子进行散列并采用模数相比并且更简单。

使用这种方法,不仅需要考虑跨语言的可重复性,而且跨多个未来版本的Python的可重复性也可能是一个问题。因此不建议这样做。

thread safety

0
投票

另一种方法是加密用户ID。如果保持加密密钥相同,则每个输入编号将加密到不同的输出编号,直到您使用的密码的块大小。 DES使用64位块,其覆盖ID 000000到18446744073709551615.这将为用户ID提供随机出现的替换,保证不会给两个不同的用户ID提供相同的“随机”数字,因为加密是一对一的排列块值。


0
投票

我道歉我没有Python实现,但我确实在Java中有非常清晰,可读和自我实现的实现,应该很容易以最小的努力转换为Python。以下产生长期可预测的均匀分布的序列,覆盖除零之外的所有范围

XorShift(randrange

import random

def id_to_choice(id_num, num_choices):
    localrandom = random.Random(id_num)
    choice = localrandom.randrange(num_choices)
    return choice

>>> id_to_choice(123, 3)
0
>>> id_to_choice(456, 3)
2

或XorShift128Plus(需要在使用之前将state0和state1重新种子化为非零值,http://www.arklyffe.com/main/2010/08/29/xorshift-pseudorandom-number-generator

public int nextQuickInt(int number) {
    number ^= number << 11;
    number ^= number >>> 7;
    number ^= number << 16;
    return number;
}

public short nextQuickShort(short number) {
    number ^= number << 11;
    number ^= number >>> 5;
    number ^= number << 3;
    return number;
}

public long nextQuickLong(long number) {
    number ^= number << 21;
    number ^= number >>> 35;
    number ^= number << 4;
    return number;
}

或ChorOshiro128Plus(engxswpoi)

http://xoroshiro.di.unimi.it/xorshift128plus.c

或者SplitMix64(public class XorShift128Plus { private long state0, state1; // One of these shouldn't be zero public long nextLong() { long state1 = this.state0; long state0 = this.state0 = this.state1; state1 ^= state1 << 23; return (this.state1 = state1 ^ state0 ^ (state1 >> 18) ^ (state0 >> 5)) + state0; } public void reseed(...) { this.state0 = ...; this.state1 = ...; } }

http://xoroshiro.di.unimi.it/

或XorShift1024Mult(public class XorOshiro128Plus { private long state0, state1; // One of these shouldn't be zero public long nextLong() { long state0 = this.state0; long state1 = this.state1; long result = state0 + state1; state1 ^= state0; this.state0 = Long.rotateLeft(state0, 55) ^ state1 ^ (state1 << 14); this.state1 = Long.rotateLeft(state1, 36); return result; } public void reseed() { } } )或Pcg64_32(http://xoroshiro.di.unimi.it/splitmix64.cpublic class SplitMix64 { private long state; public long nextLong() { long result = (state += 0x9E3779B97F4A7C15L); result = (result ^ (result >> 30)) * 0xBF58476D1CE4E5B9L; result = (result ^ (result >> 27)) * 0x94D049BB133111EBL; return result ^ (result >> 31); } public void reseed() { this.state = ...; } }


-1
投票

最简单的方法是通过多个选项来模拟http://xoroshiro.di.unimi.it/xorshift1024star.c

http://www.pcg-random.org/

它非常简单快捷。但是,如果您知道user_id,则可以猜测算法。

此外,伪随机序列可以从用户常数(例如http://www.pcg-random.org/download.html)播种的user_id获得:

choice = user_id % number_of_options
© www.soinside.com 2019 - 2024. All rights reserved.