随机数,它不等于前一个数字

Random number, which is not equal to the previous number

本文关键字:一个 数字 不等于 随机数      更新时间:2023-09-26

我需要得到随机数,但它不应该等于前一个数字。这是我的一段代码。但这行不通。

function getNumber(){
  var min = 0;
  var max = 4;
  var i;
  i = Math.floor(Math.random() * (max - min)) + min;
  if (i=== i) {
    i = Math.floor(Math.random() * (max - min)) + min;
  }
  return i;
};
console.log(getNumber());

这个答案有三次尝试

  1. 一个简单的版本,具有函数getNumber, last的属性,用于存储最后一个随机值

  2. minmax值使用闭包,当max小于min时引发异常。

  3. 一个结合了闭包和保留所有随机值的想法,并在合适的时候使用它的版本。


您可以使用getNumber的属性来存储最后一个数字,并使用do ... while循环。

function getNumber() {
    var min = 0,
        max = 4,
        random;
    do {
        random = Math.floor(Math.random() * (max - min)) + min;
    } while (random === getNumber.last);
    getNumber.last = random;
    return random;
};
var i;
for (i = 0; i < 100; i++) {
    console.log(getNumber());
}
.as-console-wrapper { max-height: 100% !important; top: 0; }


两个

另一个在区间和最后一个随机值上有闭包的建议。

function setRandomInterval(min, max) {
    var last;
    if (min >= max) {
        throw 'Selected interval [' + min + ', ' + max + ') does not work for random numbers.';
    }
    return function () {
        var random;
        do {
            random = Math.floor(Math.random() * (max - min)) + min;
        } while (random === last);
        last = random;
        return random;
    };
}
var i,
    getRandom = setRandomInterval(0, 4);
for (i = 0; i < 100; i++) {
    console.log(getRandom());
}
setRandomInterval(4, 4); // throw error
.as-console-wrapper { max-height: 100% !important; top: 0; }


3

这个提议使用了最小化新随机数调用的想法。它使用两个变量,value用于连续相同的随机值,count用于保存相同值的计数。

如果给定了保存的计数并且该值与最后一个值不相等,则函数首先查看。如果发生这种情况,则返回保存的值并减少计数。

否则将生成一个新的随机数,并按上述(第一个建议)进行检查。如果数字等于最后一个值,则计数增加,并继续生成一个新的随机值。

结果,几乎所有以前生成的随机值都被使用。

function setRandomInterval(min, max) {
    var last,      // keeping the last random value
        value,     // value which is repeated selected
        count = 0, // count of repeated value
        getR = function () { return Math.floor(Math.random() * (max - min)) + min; };
    if (min >= max) {
        throw 'Selected interval [' + min + ', ' + max + ') does not work for random numbers.';
    }
    return function () {
        var random;
        if (count && value !== last) {
            --count;
            return last = value;
        }
        random = getR();
        while (random === last) {
            value = random;
            ++count;
            random = getR();
        }
        return last = random;
    };
}
var i,
    getRandom = setRandomInterval(0, 4);
for (i = 0; i < 100; i++) {
    console.log(getRandom());
}
.as-console-wrapper { max-height: 100% !important; top: 0; }

以下方法在[min, max]范围内生成一个新的随机数,并确保该随机数与前一个随机数不同,不循环也不递归调用(Math.random()只调用一次):

  • 如果存在先前的数字,则将max减小1
  • 范围内生成一个新的随机数
  • 如果新号码等于或大于原号码,则增加一个
    (另一种选择:如果新号码等于旧号码,则设置为max + 1)

为了在闭包中保留前一个数字,可以在IIFE中创建getNumber:

// getNumber generates a different random number in the inclusive range [0, 4]
var getNumber = (function() {
  var previous = NaN;
  return function() {
    var min = 0;
    var max = 4 + (!isNaN(previous) ? -1 : 0);
    var value = Math.floor(Math.random() * (max - min + 1)) + min;
    if (value >= previous) {
      value += 1;
    }
    previous = value;
    return value;
  };
})();
// Test: generate 100 numbers
for (var i = 0; i < 100; i++) {
  console.log(getNumber());
}
.as-console-wrapper {
  max-height: 100% !important;
  top: 0;
}

[min, max]范围通过在以下语句中给max - min加1来包含:

var value = Math.floor(Math.random() * (max - min + 1)) + min;

这不是问题中的要求,但我觉得使用包含范围更自然。

首先函数应该与之前的值进行比较,现在我们只有i变量与自身进行比较。为了确保我们没有先前的值,我们需要在内部循环(在我的解决方案中递归),因为单个if statement不能给我们确定第二个随机值不相同(存在这种可能性)。你的数字设置非常小,所以碰撞的可能性很高,循环可能需要很少的执行。

function getNumber(prev){
  var min = 0;
  var max = 4;
  var next;
  
  next = Math.floor(Math.random() * (max - min)) + min;
  
  if (next===prev) {
    console.log("--run recursion. Our next is ="+next); //log only for test case
    next = getNumber(prev); //recursive
  }
  
  return next;
};
//test 100 times
var num=0;
for ( var i=0; i<100; i++){
  num=getNumber(num);
  console.log(num);
}

正如您在测试中看到的,我们从来没有两个相同的值相邻。我还添加了一些console.log来显示递归需要运行多少次才能找到与前一个不同的下一个数字。

通解

记录最后生成的号码。生成新号码时,请检查它是否与上一个号码不同。如果不一致,继续生成新数字,直到不一致,然后输出。

工作演示

var getNumber = (function(){
  var min = 0;
  var max = 4;
  var last = -1;
  return function(){
    var current;
    do{
      // draw a random number from the range [min, max]
      current = Math.floor(Math.random() * (max + 1 - min)) + min;
    } while(current === last)
    return (last = current);
  }
})();
// generate a sequence of 100 numbers,
// see that they all differ from the last
for(var test = [], i = 0; i < 100; i++){
  test[i] = getNumber();
}
console.log(test);

关于计算效率的评论

正如在评论和其他回答中讨论的那样,上述方法的一个潜在缺点是,如果生成的数字等于前一个随机数,则可能需要多次尝试来生成随机数。注意,需要多次尝试的概率非常低(它遵循快速下降的几何分布)。对于实际目的,这可能不会有任何明显的影响。

然而,可以通过直接从[min, max]范围内的数字集合中抽取一个不包括先前抽取的随机数的随机数来避免多次尝试生成新的随机数:这在@ConnorsFan的答案中得到了很好的证明,其中每次函数调用只生成一个随机数,而随机性仍然保留。

您需要一个比getNumber函数的局部变量范围更大的变量。试一试:

var j;
function getNumber(){
  var min = 0;
  var max = 4;
  var i = Math.floor(Math.random() * (max - min)) + min;
  if (j === i) {
    i = getNumber();
  }
  j = i;
  return i;
};

从一开始就从可能的值集中删除上一个值

function getNumber(previous) {
  var numbers = [0, 1, 2, 3, 4];
  if (previous !== undefined) {
      numbers.splice(numbers.indexOf(previous), 1);
  }
  var min = 0;
  var max = numbers.length;
  var i;
  i = Math.floor(Math.random() * (max - min)) + min;
  return numbers[i];
};
//demonstration. No 2 in  a row the same
var random;
for (var i = 0; i < 100; i++) {
  random = getNumber(random);
  console.log(random);
}

您可以使用@NinaScholz模式的实现,其中前一个值作为调用函数的属性存储,替换条件逻辑来增加或减少循环的当前返回值。

如果当前值等于先前的返回值,则在当前函数调用期间更改当前值,而不使用循环或递归,然后返回更改后的值。

var t = 0;
function getNumber() {
  var min = 0,
    max = 4,
    i = Math.floor(Math.random() * (max - min)) + min;
  console.log(`getNumber calls: ${++t}, i: ${i}, this.j: ${this.j}`);
  if (isNaN(this.j) || this.j != i) {
    this.j = i;
    return this.j
  } else {
    if (this.j === i) {
      if (i - 1 < min || i + 1 < max) {
        this.j = i + 1;
        return this.j
      }
      if (i + 1 >= max || i - 1 === min) {
        this.j = i - 1;
        return this.j
      }
      this.j = Math.random() < Math.random() ? --i : ++i;
      return this.j
    }
  }
};
for (var len = 0; len < 100; len++) {
  console.log("random number: ", getNumber());
}

此解决方案使用ES6生成器,并避免生成随机数,直到找到符合前提条件的随机数(两个相关数必须不同)。

主要思想是有一个数字数组和一个索引数组。然后,您将获得一个随机索引(为了符合前提条件,索引数组将是使用先前选择的索引过滤索引数组的结果)。返回值将是与数字数组中的索引相对应的数字。

function* genNumber(max = 4) {// Assuming non-repeating values from 0 to max
  let values = [...Array(max).keys()],
      indexes = [...Array(max).keys()],
      lastIndex,
      validIndexes;
  do {
    validIndexes = indexes.filter((x) => x !== lastIndex);
    lastIndex = validIndexes[Math.floor(Math.random() * validIndexes.length)];
    yield values[lastIndex];
  } while(true);
}
var gen = genNumber();
for(var i = 0; i < 100; i++) {
  console.log(gen.next().value);
}

如果你想检查结果,这里是小提琴。

将以前生成的随机数保存在数组中,检查新生成的随机数与现有的随机数是否一致,可以防止重复随机数的生成

// global variables
 tot_num = 10; // How many number want to generate?
 minimum = 0; // Lower limit
 maximum = 4; // upper limit
 gen_rand_numbers = []; // Store generated random number to prevent duplicate.
/*********** **This Function check duplicate number** ****************/
 function in_array(array, el) {
	for (var i = 0; i < array.length; i++) {
		if (array[i] == el) {
			return true;
		}
	}
	return false;
}
/*--- This Function generate Random Number ---*/
function getNumber(minimum, maximum) {
	var rand = Math.floor(Math.random() * (maximum - minimum + 1)) + minimum;
	if (gen_rand_numbers.length <= (maximum - minimum)) {
		if (!in_array(gen_rand_numbers, rand)) {
			gen_rand_numbers.push(rand);
			//alert(rand)
      console.log(rand);
			return rand;
		} else {
			return getNumber(minimum, maximum);
		}
	} else {
		alert('Final Random Number: ' + gen_rand_numbers);
	}
}
/*--- This Function call random number generator to get more than one random number ---*/
function how_many(tot_num) {
	for (var j = 0; j < tot_num; j++) {
		getNumber(minimum, maximum);
	}
	
}
<script src = "https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js" > </script>
	<input type = "button"	onclick = "how_many(4)" value = "Random Number" >

您可以使用线性同余生成器的增强实现。

线性同余发生器(LCG)是一种用不连续分段线性方程计算得到伪随机数序列的算法。

下面的函数返回一个种子随机数以及最小值和最大值:

Math.seededRandom = function(seed, min, max) {
  max = max || 1;
  min = min || 0;
  // remove this for normal seeded randomization
  seed *= Math.random() * max;
  seed = (seed * 9301 + 49297) % 233280;
  let rnd = seed / 233280.0;
  return min + rnd * (max - min);
};

在您的例子中,因为您永远不希望新数字与以前的数字相同,所以您可以将以前生成的数字作为种子传递。

下面是一个生成100个随机数的例子:

Math.seededRandom = function(seed, min, max) {
  max = max || 1;
  min = min || 0;
  
  // remove this for normal seeded randomization
  seed *= Math.random() * max;
  
  seed = (seed * 9301 + 49297) % 233280;
  let rnd = seed / 233280.0;
  return min + rnd * (max - min);
};
let count = 0;
let randomNumbers = [];
let max = 10;
do {
  let seed = (randomNumbers[randomNumbers.length -1] || Math.random() * max);
  
  randomNumbers.push(Math.seededRandom(seed, 0, max));
  count++;
} while (count < 100)
console.log(randomNumbers);

一个有趣的答案,在一行中生成从0到4的数字:

console.log(Math.random().toString(5).substring(2).replace(/(.)'1+/g, '$1').split('').map(Number));

解释:

Math.random() //generate a random number
    .toString(5) //change the number to string, use only 5 characters for it (0, 1, 2, 3, 4)
    .substring(2) //get rid of '0.'
    .replace(/(.)'1+/g, '$1') //remove duplicates
    .split('') //change string to array
    .map(Number) //cast chars into numbers

和一个更长的版本,使用generator:

let Gen = function* () {
  const generateString = (str) => str.concat(Math.random().toString(5).substring(2)).replace(/(.)'1+/g, '$1');
  let str = generateString('');
  let set = str.split('').map(Number);
  
  while (true) {
    if (set.length === 0) {
      str = generateString(str).substring(str.length);
      set = str.split('').map(Number);
    }
    yield set.pop();
  }
}
let gen = Gen();
console.log(gen.next().value);
console.log(gen.next().value);
console.log(gen.next().value);
console.log(gen.next().value);
console.log(gen.next().value);
console.log(gen.next().value);
console.log(gen.next().value);
console.log(gen.next().value);
console.log(gen.next().value);
console.log(gen.next().value);
console.log(gen.next().value);

function getNumber(){
    var min = 0;
    var max = 4;
    var i;
    i = Math.floor(Math.random() * (max - min)) + min;
    while(i==getNumber.last)
        i = Math.floor(Math.random() * (max - min)) + min;
    getNumber.last=i;
    return i;
};
console.log(getNumber());

尝试这些
var prev_no = -10;
function getNumber(){
    var min = 0;
    var max = 4;
    var i;
    i = Math.floor(Math.random() * (max - min)) + min;
    while (i == prev_no) {
        i = Math.floor(Math.random() * (max - min)) + min;
        prev_no = i;
    }
    return i;
};
console.log(getNumber());

可以使用Promise, Array.prototype.forEach(), setTimeout。创建并迭代一个将.length设置为max的数组;在.forEach()回调中使用setTimeout,将duration设置为随机值,将数组的索引推入新数组,以解决新数组内索引不均匀分布的问题。从getNumber函数返回解析的Promise,其中.then()Promise值将是.length max的数组,.forEach()回调中的随机index作为没有重复条目的值。

function getNumber(max) {
  this.max = max;
  this.keys = [];
  this.arr = Array.from(Array(this.max));
  this.resolver = function getRandom(resolve) {
    this.arr.forEach(function each(_, index) {
      setTimeout(function timeout(g) {
        g.keys.push(index);
        if (g.keys.length === g.max) {
          resolve(g.keys)
        };
      }, Math.random() * Math.PI * 100, this);
    }, this)
  };
  this.promise = new Promise(this.resolver.bind(this));
}
var pre = document.querySelector("pre");
var random1 = new getNumber(4);
random1.promise.then(function(keys) {
  pre.textContent += keys.length + ":'n";
  keys.forEach(function(key) {
    pre.textContent += key + " ";
  })
});
var random2 = new getNumber(1000);
random2.promise.then(function(keys) {
  pre.textContent += "'n'n";
  pre.textContent += keys.length + ":'n";
  keys.forEach(function(key) {
    pre.textContent += key + " ";
  })
});
pre {
  white-space: pre-wrap;
  width: 75vw;
}
<pre></pre>

我很惊讶没有人提出这样一个简单的解决方案:

function getRandomNum(min, max, exclude) {
    if (Number.isNaN(exclude)) exclude = null;
    let randomNum = null;
    do {
        randomNum = Math.floor(min + Math.random() * (max + 1 - min));
    } while (randomNum === exclude);
    return randomNum;
}

注意"exclude";是可选的。你可以这样使用它:

// Pick 2 unique random numbers between 1 and 10
let firstNum = getRandomNum(1, 10);
let secondNum = getRandomNum(1, 10, firstNum);

你可以在这里试一试:

function getRandomNum(min, max, exclude) {
  if (Number.isNaN(exclude)) exclude = null;
  let randomNum = null;
  do {
    randomNum = Math.floor(min + Math.random() * (max + 1 - min));
  } while (randomNum === exclude);
  return randomNum;
}

// Pick 2 unique random numbers between 1 and 10
let firstNum = getRandomNum(1, 10);
let secondNum = getRandomNum(1, 10, firstNum);

// Output the numbers
document.write(firstNum + ' and ' + secondNum);

除非您执行数据库查询以检查新号码是否存在,否则无法实现此操作。如果存在,则重复此过程。

生成唯一随机数的架构可能性是生成两个随机数并将字符串组合。

例如:

rand_num1 = rand(5);
rand_num2 = rand(4);

然后组合rand_num1和rand_num2,这更像是唯一的

实际的例子:

(23456)(2345)
(23458)(1290)
(12345)(2345)

还可以增加数字的数目以减少重复。