如何随机生成一个整数排列的函数< 10000

时间:2017-04-03 16:41:04

标签: java random permutation

我想允许用户使用相同的用户名,但是在#XXXX形式中有一些额外的id,其中X是一个数字(例如,BestUserName#3421),类似于在Battle.net上完成的方式。

没有两个用户具有相同的用户名和#id组合。

另外,我不希望我的用户能够轻松预测其他用户的额外ID。所以,我不能从BestUserName#0000开始,然后是BestUserName#0001,BestUserName#0002,...)。

因此,我想为每个用户名生成从0000到9999之间的所有数字到0000到9999之间的每个数字的双射f(n).f必须使得很难猜出f(n-1)是什么当你知道f(n)。此外,对于所有用户名,f(n)必须不相同。

然后第一个用户将是BestUserName#f(0000),第二个用户将是BestUserName#f(0001),依此类推,我的用户将无法猜到彼此的#id #id

我怎样才能用Java做到这一点?

3 个答案:

答案 0 :(得分:5)

在构造函数中,创建一个使用值ArrayList初始化的0000 - 9999,将其随机播放,并将计数器初始化为-1(或10000)。每次添加用户时,递增(或递减)计数器并使用它来索引ArrayList的下一个元素。或者,如果您要将额外ID添加为用户属性,则可以放弃计数器,只需删除并指定ArrayList的最后一个(或第一个)元素。

如果分配应该是永久性的,那么您必须存储先前的分配,随机分配ArrayList和计数器值,以便您可以在上次离开的地方继续分配。

答案 1 :(得分:2)

你想要一个双射。加密是一种双射,因此只需加密数字0,1,2,3 ...并且只要您使用相同的密钥就不会重复。加密可确保没有明显的数字出现顺序。

你想要一个不寻常的范围,0000..9999所以你可能需要Hasty Pudding cipher,它可以处理许多不同的范围,包括你的范围。

答案 2 :(得分:1)

所以,我碰巧在这个区域进行了参数搜索,发现了以下14位散列函数:

int h(int x) {
    x &= 0x3fff;
    x ^= x >> 8;
    x *= 0x68ab;
    x &= 0x3fff;
    x ^= x >> 8;
    x *= 0x594b;
    x &= 0x3fff;
    x ^= x >> 8;
    return x;
}

此哈希遵循murmur3 mix函数构造。 &=操作是将算术运算保持在14位溢出域中,但除此之外,每一步都是双射,因此整体散列是双射。

每个乘法都是奇数(使其与模2 ** 14共同构成,这保证所有结果都是唯一的),并且可以通过一次一个地解开操作来反转移位异或操作

但上述功能只保证将一个小于16384的数字映射到另一个小于16384的数字。我们需要将其限制为小于10000.

如果你将它包装在下面的循环中,它将被限制在小于10000的值(不用担心,平均迭代次数可以证明是1.6384,所以它非常安全): / p>

int f(int x) {
  do {
    x = h(x);
  } while (x >= 10000);
  return x;
}

因为循环内的散列函数是双射的,所以只有一个输入x可以映射到任何给定的结果。如果该结果超出范围,但x在范围内,则哈希的下一次迭代必须映射到新值。即使它指向一个新的超出范围的价值,该链最终也必须被强制退回到范围内。

完全超出范围的循环可以存在,但是它不能从范围内的值中访问,因为暗示该循环中的一个链接有两个值映射对它(一个来自外部,一个来自内部),这是双射函数无法实现的。

这意味着f()的输入必须已经在范围内才能避免无限循环。

现在,为了使每个用户名不同,请尝试以下方法:

int f(int x, int username_hash) {
  int m = (username_hash * 2 + 1) & 0x3fff;
  int c = (username_hash >> 13) & 0x3fff;
  do {
    x = (x * m + c) & 0x3fff;
    x = h(x);
  } while (x >= 10000);
  return x;
}

同样,乘以2的幂的模数的奇数是双射的,并且以2的幂为模的加法也是双射的。在那里抛出这两个额外的操作有效地扩展了散列并根据username_hash扰乱了它的行为。外循环将结果保持在10000以下。

我不确定您是否要将用户总数限制为10000,或者您是否要为每个竞争用户名保留单独的计数,但是如果您转到f()该号码(保证小于10000)以及用于配置操作的用户名的一些整数哈希值,然后您将得到一个与您传入的号码一样唯一的号码。