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

时间:2016-10-15 07:24:23

标签: javascript

我需要获得随机数,但它不应该等于之前的数字。这是我的代码片段。但它不起作用。

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());

16 个答案:

答案 0 :(得分:19)

这个答案提出了三次尝试

  1. 一个简单版本,其属性为getNumberlast,用于存储最后一个随机值。

  2. 如果min小于max,则对maxmin值使用闭包的版本会引发异常。

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

  4. <强>一

    您可以使用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; }

    <强>三

    此提案使用该想法来最小化新随机数的调用。它适用于两个变量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; }

答案 1 :(得分:15)

以下方法在[min,max]范围内生成一个新的随机数,并确保此数字与前一个不同,没有循环且没有递归调用(Math.random()只调用一次):< / p>

  • 如果存在以前的号码,请将最多减少一个
  • 范围内生成新的随机数
  • 如果新号码等于或大于前一个号码,请添加一个号码 (另一种方法:如果新数字等于前一个数字,则将其设置为max + 1)

为了将先前的数字保留在闭包中,可以在IIFE中创建getNumber

&#13;
&#13;
// 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());
}
&#13;
.as-console-wrapper {
  max-height: 100% !important;
  top: 0;
}
&#13;
&#13;
&#13;

通过在以下语句中向max - min添加1,[最小,最大]范围为inclusive

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

这不是问题的要求,但使用包容性范围对我来说更自然。

答案 2 :(得分:9)

此主题中的大部分答案都过于复杂。

以下是我将如何做到这一点的简明例子:

function getNumber(){
    return (getNumber.number = Math.floor(Math.random() * (4 + 1))) === getNumber.lastNumber ? getNumber() : getNumber.lastNumber = getNumber.number;
}

console.log(getNumber()); // Generates a random number between 0 and 4

实例:https://jsfiddle.net/menv0tur/3/

答案 3 :(得分:5)

首先,函数应该与之前的值进行比较,现在我们只有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来显示递归需要运行多少次才能找到与前一个不同的下一个数字。

答案 4 :(得分:4)

您需要一个范围大于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;
};

答案 5 :(得分:4)

从一开始就从可能值集中删除先前的值。

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);
}

答案 6 :(得分:4)

一般解决方案

跟踪上次生成的号码。生成新号码时,请检查它是否与上一个号码不同。如果没有,继续生成新数字直到它不同,然后输出它。

工作演示

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);

关于计算效率的评论

正如评论和其他答案中所讨论的,上述方法的一个潜在缺点是,如果生成的数字等于前一个,则可能需要多次尝试生成随机数。请注意,需要多次尝试的概率非常低(它遵循快速下降的geometric distribution)。出于实际目的,这不太可能产生任何明显的影响。

但是,可以避免多次尝试生成新的随机数,方法是从[min,max]范围内的数字组中直接绘制一个随机数,不包括先前绘制的数字:这在通过@ConnorsFan回答,其中每个函数调用只生成一个随机数,而随机性仍然保留。

答案 7 :(得分:3)

您可以使用@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());
}

答案 8 :(得分:2)

此解决方案使用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);
}

如果你想检查结果,这是fiddle

答案 9 :(得分:2)

将先前生成的随机数保存在数组中,使用现有数字检查新数字,可以防止重复随机数生成。

// 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" >

答案 10 :(得分:1)

您可以使用linear congruential generator的增强实现。

  

线性同余生成器(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个随机数的示例:

&#13;
&#13;
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);
&#13;
&#13;
&#13;

答案 11 :(得分:1)

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

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

说明:

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

使用生成器的更长版本:

&#13;
&#13;
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);
&#13;
&#13;
&#13;

答案 12 :(得分:1)

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());

答案 13 :(得分:0)

试试这个

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());

答案 14 :(得分:0)

您可以使用PromiseArray.prototype.forEach()setTimeout。创建并迭代.length设置为max的数组;在setTimeout回调中使用.forEach()并将duration设置为随机值,以将数组的索引推送到新数组,以便在新数组中非均匀分布索引。已从Promise函数返回已解决的getNumber Promise .then()值为.length的{​​{1}} max数组,其中index来自.forEach() 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 + " "; }) });回调为没有重复条目的值。

&#13;
&#13;
pre {
  white-space: pre-wrap;
  width: 75vw;
}
&#13;
<pre></pre>
&#13;
timer
&#13;
&#13;
&#13;

答案 15 :(得分:-1)

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

有一种架构可能性,使得唯一的随机数是生成两个随机数并组合字符串。

例如:

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

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

实际例子:

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

同时增加位数以减少重复。