可以采用递增计数器并使其看起来唯一随机的算法或公式

Algorithm or formula that can take an incrementing counter and make it appear uniquely random

我想知道是否有某种通用公式可以采用单个递增整数,并且 运行 通过模数之类的东西将其移动到随机位置,以便随着您递增计数器,它的输出值跳来跳去并随机出现,但没有任何值被击中两次。假设对一组数字有一些限制,例如 16 位整数(65536 个整数)或 32 位整数等。也许有办法以某种方式使数字螺旋下降,我不知道。序列是可以预测的,但对于外行人来说,它会显得随机而不考虑太多。

例如,你可以将一个数乘以2,使其不显得直接递增。但这不是很复杂。您也许可以从集合的中间开始数字(例如 16 位整数的 30103),然后乘以 2 并使用模数旋转数字,这看起来会增加得更少。但是你仍然可以看到一个模式。

我想知道您可以 运行 递增的数字(在一组有界整数中)可以使用哪种模式或方程式,以便输出看起来尽可能不可预测,同时它永远不会两次击中相同的数字。通过这种方式,您可以让外行人看到 ID 是随机生成的,而不必事先以随机顺序将所有 ID 存储在数据库中。该公式将从单个存储的整数生成它们。在这方面有什么可能,方程式是什么?理论上能走多远?

也许你可以把这个集合设为奇数,并跳过每 20 个数字,并以某种方式证明它最终会在整个集合中循环而不会重复。不过我想不通。

更新:这似乎在 pseudorandom number generation, like this 领域,但我不确定它们是否符合从不重复数字的附加限制。

Here 是我发现并实现的,但它提供了一些重复项:/.

const fetch = (x, o) => {
  if (x >= o) {
    return x
  } else {
    const v = (x * x) % o
    return (x <= o / 2) ? v : o - v
  }
}

const fetch32 = (x) => fetch(x, 4294967291)
const fetch16 = (x) => fetch(x, 65519)
const fetch8 = (x) => fetch(x, 251)

// the last number can be anything.
const build32 = (x, o) => fetch32((fetch32(x) + o) ^ 1542469173)
const build16 = (x, o) => fetch16((fetch16(x) + o) ^ 42703)
const build8 = (x, o) => fetch8((fetch8(x) + o) ^ 101)

let i = 0
let n = Math.pow(2, 32)
while (i < n) {
  let j = 0
  let r = {}
  while (j < n) {
    let x = build32(j, i)
    if (r[x]) throw `${i}:${j}:${x}`
    r[x] = true
    j++
  }
  i++
}

评论中的另一个链接问题没有显示 JavaScript 遵守唯一性约束的实现。

var bottomLimit = 1
var topLimit = 10
var arr = []
for (var i = bottomLimit; i < topLimit; i++) {
  arr.push(i)
}
arr = shuffle(arr);
console.log(arr);

//
function shuffle(array) {
  var currentIndex = array.length,
    temporaryValue, randomIndex;

  while (0 !== currentIndex) {

    randomIndex = Math.floor(Math.random() * currentIndex);
    currentIndex -= 1;

    temporaryValue = array[currentIndex];
    array[currentIndex] = array[randomIndex];
    array[randomIndex] = temporaryValue;
  }

  return array;
}

好吧,你可以生成随机号码。在二号范围内

 public static int getRandomVal(int min, int max) {
    Random random = new Random();

    return random.nextInt((max - min) + 1) + min;
}

public static void getRandomNumbers(int size, int min,
        int max) {
    ArrayList<Integer> numbers = new ArrayList<Integer>();

    while (numbers.size() < size) {
        int random = getRandomVal(min, max);

        if (!numbers.contains(random)) {
            numbers.add(random);
            System.out.println(" "+random);
        }
    }
}

现在生成 10 个不同的编号。在 50 到 100 之间你可以使用

getRandomNumbers(10, 50,100);

这种方法非常简单我正在创建一个数组并检查随机值是否已经存在。如果它不存在,我将它推送到数组并输出它。

如果您正在寻找一个序列,其中一个值是通过知道先前的值产生的,那么您正在寻找的可能是 Linear congruential generator,其模数为 2 的幂。涉及到几个参数:

  • m:模数,在你的例子中是 28, 216 , 或 232.
  • a:乘数。为确保在生成第一个副本之前生成所有值,这必须是 4 加 1 的倍数(假设 m 是 2 的幂)。
  • c:增量。一定是奇数。

你可以把玩这些数字,得出一个你对“随机性”感到满意的数列。

上面引用的维基百科文章列出了一些伪随机生成器中使用的参数选择。我刚刚选择了 a=97c 范围一半的奇数。

下面是证明唯一性的代码:

/*
    Assuming that m is a power of 2:
    - c must be odd
    - a % 4 must be 1
*/
function createFetch(m, a, c) { // Returns a function
     return x => (a * x + c) % m;
}
const m = 2**16;
const fetch16 = createFetch(m, 97, (m>>1)-1);

const r = new Set;
let x = 1;
for (let i = 0; i < m; i++) {
    x = fetch16(x);
    if (i < 10) console.log(x);
    if (r.has(x)) throw `${i}:${x}`
    r.add(x);
}
console.log("...");
console.log(`generated ${r.size} unique numbers`);

注意/这是生成器的一个很好的用例,在 JavaScript 中看起来像这样:

function * fetch(m, a, c, x=1) {
     while (true) {
         x = (a * x + c) % m;
         yield x;
     }
}
const m = 2**16;
const fetch16 = fetch(m, 97, (m>>1)-1);

const r = new Set;
for (let i = 0; i < m; i++) {
    x = fetch16.next().value;
    if (i < 10) console.log(x);
    if (r.has(x)) throw `${i}:${x}`
    r.add(x);
}
console.log("...");
console.log(`generated ${r.size} unique numbers`);

给自己一个种子随机数生成器。

种子为 1,return 下一个随机数。种子为 2,return 下一个随机数。种子为 3,return 下一个随机数... 如果您使用整数作为种子,那么下一个随机数将是可重复的伪随机数。

块大小为 n 位的任何块密码都是 {0,1,2, ..., 2n-1} 的排列。因此,如果 E 是这样的分组密码并且 k 是 E 的有效密钥,则 Ek(0), Ek(1) , ..., Ek(2n-1) 都是不同的。如果块密码是好的,那么这些值在肉眼看来是“随机的”。如果您更改密钥 k,您会得到不同的排列。

您提供的 link 中实际上提到了这一点。

同时考虑 this 答案。