Javascript rounding issue when summing values [duplicate] - javascript

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Is JavaScript's Math broken?
The user enters values in the first two text boxes and, as they type, Javascript (sorry, no jQuery, I'm not up to it yet) is used to calculate the precise sum and the sum rounded to 2 digits.
Why am I getting rounding error and what can I do to correct it?
Many thanks.
Hmmm....ParseFloat? Wrong data type?
What I would like to see if the precise answer as if it were added on a calculator. Is there a parseDecimal or other data type that I can use?
![enter image description here][1]
function SumValues() {
//debugger;
var txtSubsContrRbtAmt = document.getElementById("<%=txtSubsContrRbtAmt.ClientID%>");
var txtDeMinAmt = document.getElementById("<%=txtDeMinAmt.ClientID%>");
var txtTotRbtAmt = document.getElementById("<%=txtTotRbtAmt.ClientID%>");
var txtRndRbtAmt = document.getElementById("<%=txtRndRbtAmt.ClientID%>");
var total = Add(txtSubsContrRbtAmt.value, txtDeMinAmt.value);
txtTotRbtAmt.value = total;
txtRndRbtAmt.value = RoundToTwoDecimalPlaces(total);
}
function Add() {
var sum = 0;
for (var i = 0, j = arguments.length; i < j; i++) {
var currentValue;
if (isNumber(arguments[i])) {
currentValue = parseFloat(arguments[i]);
}
else {
currentValue = 0;
}
sum += currentValue;
}
return sum;
}
function RoundToTwoDecimalPlaces(input) {
return Math.round(input * 100) / 100
}
function IsNumeric(input) {
return (input - 0) == input && input.length > 0;
}
function isNumber(n) {
return !isNaN(parseFloat(n)) && isFinite(n);
}
[1]: http://i.stack.imgur.com/5Otrm.png
Update. I am evaluating something like this:
function AddWithPrecision(a, b, precision) {
var x = Math.pow(10, precision || 2);
return (Math.round(a * x) + Math.round(b * x)) / x;
}

There is a golden rule for anyone writing software in the financial sector (or any software dealing with money): never use floats. Therefore most software dealing with money use only integers and represent decimal numbers as a data structure.
Here's one way of doing it:
(Note: this function adds two strings that looks like numbers)
(Additional note: No error checking is done to aid clarity. Also does not handle negative numbers)
function addNumberStrings (a,b) {
a = a.split('.');
b = b.split('.');
var a_decimal = a[1] || '0';
var b_decimal = b[1] || '0';
diff = a_decimal.length - b_decimal.length;
while (diff > 0) {
b_decimal += '0';
diff --;
}
while (diff < 0) {
a_decimal += '0';
diff ++;
}
var decimal_position = a_decimal.length;
a = a[0] + a_decimal;
b = b[0] + b_decimal;
var result = (parseInt(a,10)+parseInt(b,10)) + '';
if (result.length < decimal_position) {
for (var x=result.length;x<decimal_position;x++) {
result = '0'+result;
}
result = '0.'+result
}
else {
p = result.length-decimal_position;
result = result.substring(0,p)+'.'+result.substring(p);
}
return result;
}
*note: code is simplified, additional features left out as homework.

To fix your addition the way you want, I'd suggest counting the decimal places in each number somehow This method, for instance Then passing the max value to toFixed, and trimming any leftover zeroes.
function AddTwo(n1, n2) {
var n3 = parseFloat(n1) + parseFloat(n2);
var count1 = Decimals(n1, '.');
var count2 = Decimals(n2, '.');
var decimals = Math.max(count1, count2);
var result = n3.toFixed(decimals)
var resultDecimals = Decimals(result, '.');
if (resultDecimals > 0) {
return result.replace(/\.?0*$/,'');
}
else {
return result;
}
}
// Included for reference - I didn't write this
function Decimals(x, dec_sep)
{
var tmp=new String();
tmp=x;
if (tmp.indexOf(dec_sep)>-1)
return tmp.length-tmp.indexOf(dec_sep)-1;
else
return 0;
}
Here's a JSFiddle of that

Related

Number generation between 1 - 100 in JavaScript using JQuery [duplicate]

experts
I wish to generate unique random numbers between two numbers (from text box of web page).
I am using array to store numbers. when user click button it gives first random number, store it in array, and when user clicks button again, it generate random number, compares it with array numbers, and if different, stores it and displays it.
If maximum possible different numbers are show it, clears array and informs user.
I have written code, but it gives error : stack overflow, or sometimes I get duplicate results shown.
Can anyone shed a light on code:
var allnums = new Array();
var num1= new Number;
var num2= new Number;
function funClick()
{
var num1 = Number(document.getElementById('lnum').value);
var num2 = Number(document.getElementById('hnum').value);
if (allnums.length==num2)
{
alert("Maximum non-duplicate numbers served. Now resetting the counter.");
allnums = [];
return;
}
if (num1<num2)
{
x = Math.floor(Math.random() * (num2 - num1 + 1)) + num1;
funShow(x);
}
else
{
alert("You entered wrong number criteria!");
}
}
function funShow(x)
{
var bolFound = false;
for (var i=0;i<allnums.length;i++)
{
if((allnums[i])==x)
{
funClick();
}
}
if (bolFound == false)
{
document.getElementById('rgen').innerText = x;
allnums.push(x);
}
}
I fail to see how that code is generating a stack overflow (even though funShow has a call to funClick and funClick has a call to funShow, funShow's call to funClick should never happen because of a logic error -- fix the error and you'll get a stack overflow, though), but it has several issues. See the comments:
// style: Use [], not new Array()
var allnums = new Array();
// `new Number` doesn't do anything useful here
var num1 = new Number;
var num2 = new Number;
function funClick() {
// For user-entered values, use parseInt(value, 10) to parse them into numbers
var num1 = Number(document.getElementById('lnum').value);
var num2 = Number(document.getElementById('hnum').value);
if (allnums.length == num2) {
alert("Maximum non-duplicate numbers served. Now resetting the counter.");
allnums = [];
return;
}
// & is a bitwise AND operation, not a logical one. If your goal is to see
// if both numbers are !0, though, it works but is obtuse.
// Also, there is no ltnum2 variable anywhere, so trying to read its value
// like this should be throwing a ReferenceError.
if (num1 & ltnum2) {
// You're falling prey to The Horror of Implicit Globals, x has not
// been declared.
x = Math.floor(Math.random() * (num2 - num1 + 1)) + num1;
funShow(x);
} else {
alert("You entered wrong number criteria!");
}
}
function funShow(x) {
var bolFound = false;
// Again, & is a bitwise AND operation. This loop will never run, because
// you start with 0 and 0 & anything = 0
// But it should be throwing a ReferenceError, as there is no ltallnums
// anywhere.
for (var i = 0; i & ltallnums.length; i++) {
if ((allnums[i]) == x) {
funClick();
}
}
// This condition will always be true, as you've done nothing to change
// bolFound since you set it to false
if (bolFound == false) {
document.getElementById('rgen').innerText = x;
allnums.push(x);
}
}
There are two ways to approach this. Here's one that's basically what you were trying to do, but without recursion:
function funClick() {
var num1 = parseInt(document.getElementById('lnum').value, 10);
var num2 = parseInt(document.getElementById('hnum').value, 10);
var nums = [];
var targetCount;
var x;
// Check the inputs
if (isNaN(num1) || isNaN(num2) || num2 <= num1) {
alert("Please ensure that hnum is higher than lnum and both are really numbers.");
return;
}
// Find out how many integers there are in the range num1..num2 inclusive
targetCount = num2 - num1 + 1;
// Produce that many random numbers
while (nums.length < targetCount) {
x = Math.floor(Math.random() * (num2 - num1 + 1)) + num1;
if (nums.indexOf(x) < 0) {
nums.push(x);
}
}
// Show the result
document.getElementById('rgen').innerText = nums.join(", ");
}
Live Example | Source
The problem with that is that it can take a long time to fill the last few slots, since we have to hit them randomly.
The other way is to produce the array with the numbers in order, then mess it up. It can be dramatically more efficient for large ranges. Something like this:
function funClick() {
var num1 = parseInt(document.getElementById('lnum').value, 10);
var num2 = parseInt(document.getElementById('hnum').value, 10);
var nums = [];
var x;
// Check the inputs
if (isNaN(num1) || isNaN(num2) || num2 <= num1) {
alert("Please ensure that hnum is higher than lnum and both are really numbers.");
return;
}
// Create an array with those numbers in order
for (x = num1; x <= num2; ++x) {
nums.push(x);
}
// Sort it with a random comparison function
nums.sort(function(a, b) {
return 0.5 - Math.random();
});
// Show the result
document.getElementById('rgen').innerText = nums.join(", ");
}
Live Example | Source
But, just doing the nums.sort(...) randomly once may well not be as successful at producing random results; see this article for more. (Thanks to eBusiness for that link and for his input on the below.)
So you may want to go further and throw in further random operations. Here's another example:
function funClick() {
var num1 = parseInt(document.getElementById('lnum').value, 10);
var num2 = parseInt(document.getElementById('hnum').value, 10);
var nums = [];
var n, x, y;
var num;
// Check the inputs
if (isNaN(num1) || isNaN(num2) || num2 <= num1) {
alert("Please ensure that hnum is higher than lnum and both are really numbers.");
return;
}
// Create an array with those numbers in order
for (n = num1; n <= num2; ++n) {
nums.push(n);
}
// We only need to shuffle it if it's more than one element long
if (nums.length > 1) {
// Sort it "randomly"
nums.sort(function(a, b) {
return 0.5 - Math.random();
});
// Throw a bunch of random swaps in there
for (n = 0; n < nums.length; ++n) {
do {
x = Math.floor(Math.random() * nums.length);
}
while (x === n);
num = nums[x];
nums[x] = nums[n];
nums[n] = num;
}
}
// Show the result
document.getElementById('rgen').innerText = nums.join(", ");
}
Live Example | Source
That does the array sort thing as a starting point, but then does a bunch of random swaps between elements as well. It still runs in constant time, but should have a better result than using the array sort alone. Naturally, you'll want to test the distribution.
use an array:
var uniqueRandomNumbers = new Array();
var totalNumbers = 100;
for (var i=0; i<totalNumbers; i++){
uniqueRandomNumbers.push(i);
}
uniqueRandomNumbers.sort(function() {return 0.5 - Math.random();});
var uniqueNumber;
for(var i=0; i<uniqueRandomNumbers.length; i++){
uniqueNumber = uniqueRandomNumbers[i];
//do something with the number
}
Since I can't edit Crowder's answer, here is the simple unbiased way of scrambling an array:
function scramble(nums){
for (var n = nums.length; n; n--) {
var x = Math.floor(Math.random() * n);
var num = nums[n-1];
nums[n-1] = nums[x];
nums[x] = num;
}
}

Optimal verification if a number is Palindrom in JS

I have a problem I am sitting on for the past few days.
I want to write an optimal (in JS) program for verifying if a number is a Palindrome.
My current approach:
function isPalidrom2(pali){
//MOST time consuming call - I am splitting the digit into char array.
var digits = (""+pali).split("");
//To get the length of it.
var size = digits.length;
var isPali = true;
for(var i = 0; i<Math.floor(size/2); i++){
//I am comparing digits (first vs last, second vs last-1, etc.) one by one, if ANY of the pars is not correct I am breaking the loop.
if(parseInt(digits[i]) != parseInt(digits[size-i-1])){
isPali = false;
break;
}
}
return isPali;
}
It's not optimal. The biggest amount of time I am waisting is the change from INT to STRING.
And I am out of ideas
- I tried to understand the BIT operators but I can't.
- I tried to google and look for alternative approaches - but I can't find anything.
- I tried to play with different algorithms - but this one is the fastest I was able to apply.
So in short - my question is:
"how can I make it faster?"
EDIT:
So the task I want to solve:
Find all of the prime numbers within the range of all five digit numbers.
Among all of the multiplies (i*j) they are between them, find the most significant palindrome.
My current approach:
function isPrime(number){
var prime = true;
var i
for(i = 2; i<= number/2; i++){
if((number%i)==0){
prime = false;
break;
}
}
return prime;
}
function get5DigitPrimeNr(){
var a5DigitsPrime = [];
var i;
for(i = 10000; i<100000; i++){
if(isPrime(i)){
a5DigitsPrime.push(i)
}
}
return a5DigitsPrime;
}
function isPalidrom(pali){
var digits = (""+pali).split("");
//we check if first and last are the same - if true, we can progress
size = digits.length;
return
(digits[0]==digits[size-1]) &&
(parseInt(digits.slice(1, Math.floor(size/2)).join("")) ==
parseInt(digits.reverse().slice(1, Math.floor(size/2)).join("")))
}
function isPalidrom2_48s(str) {
var str = str.toString();
const lower = str.substr(0, Math.floor(str.length / 2));
const upper = str.substr(Math.ceil(str.length / 2));
return lower.split("").reverse().join("") === upper;
}
function isPalidrom_22s(pali){
var digits = (""+pali).split("");
var size = digits.length;
for(var i = 0; i<Math.floor(size/2); i++){
//console.log("I am comparing: "+i+", and "+(size-i-1)+" elements in array")
//console.log("I am comparing digit: "+digits[i]+", and "+digits[(size-i-1)]+"")
if(digits[i] !== digits[size-i-1]){
//console.log('nie sa rowne, koniec')
return false;
}
}
return true;
}
function isPalidrom2_80s(pali){
return parseInt(pali) == parseInt((""+pali).split("").reverse().join(""))
}
function runme(){
var prime5digits = get5DigitPrimeNr();
var size = prime5digits.length;
var max = 0;
var message = "";
for(var i = 0; i<size; i++){
for(var j = 0; j<size; j++){
var nr = prime5digits[i]*prime5digits[j];
if(nr>max && isPalidrom2(nr)){
max = nr;
message = 'biggest palidrome nr: '+nr+', made from numbers: '+prime5digits[i]+' x '+prime5digits[j];
}
}
}
console.log(message)
}
function timeMe(){
var t0 = performance.now();
runme();
var t1 = performance.now();
console.log("Function took " + millisToMinutesAndSeconds(t1 - t0) + " s to find the perfect palindrom.")
}
//helper functons:
function millisToMinutesAndSeconds(millis) {
var minutes = Math.floor(millis / 60000);
var seconds = ((millis % 60000) / 1000).toFixed(0);
return minutes + ":" + (seconds < 10 ? '0' : '') + seconds;
}
To keep the spirit of you code, you could exit the loop with return instead of break and use the string directly without converting to an array. Strings have, as arrays, the possibility of an access single character with an index.
function isPalidrom2(value) {
var digits = value.toString(),
length = digits.length,
i, l;
for (i = 0, l = length >> 1; i < l; i++) {
if (digits[i] !== digits[length - i - 1]) {
return false;
}
}
return true;
}
console.log(isPalidrom2(1));
console.log(isPalidrom2(12));
console.log(isPalidrom2(1221));
console.log(isPalidrom2(123));
The fastest is probably to rely on javascripts native methods:
function isPalindrome(str) {
const lower = str.substr(0, Math.floor(str.length / 2));
const upper = str.substr(Math.ceil(str.length / 2));
return lower.split("").reverse().join("") === upper;
}
Or leave away all unneccessary conversions from your code:
function isPlaindrome(str) {
const half = str.length / 2;
for(var i = 0; i < half; i++)
if(str[i] !== str[str.length - i - 1])
return false;
return true;
}
If you're trying to speed things up, you could shave a few more seconds off by optimising your isPrime(n) function.
You don't need to check every factor, only the prime factors less than sqrt(n)
If you check every number from 2 to 99999 in ascending order, you can store the results as you go, so you don't need to keep re-calculating the list of previous primes
Something like this:
var savedPrimes = [2]
function isPrime(n){
// call this method with increasing values of n (starting at 2), saving primes as we go,
// so we can safely assume that savedPrimes contains all primes less than n
for(var i=0; i<savedPrimes.length; i++)
{
var f = savedPrimes[i];
if ((n % f) == 0)
return false; // found a factor
if (f*f>=n)
break; // stop checking after f >= sqrt(n)
}
// no prime factors - we found a new prime
savedPrimes.push(n);
return true;
}
function get5DigitPrimeNr(){
var a5DigitsPrime = [];
var i;
// first find all the primes less than 10000
for(i = 3; i<10000; i++){
isPrime(i);
}
// now find (and keep) the rest of the primes up to 99999
for(i = 10000; i<100000; i++){
if(isPrime(i)){
a5DigitsPrime.push(i)
}
}
return a5DigitsPrime;
}
EDIT - when I run your code with this method, I get a time of 10 sec
Code :
There are multiple methods that you can use (dunno if they are optimal) :
Palindrom = _ => (_=''+_) === [..._].sort(_=>1).join``
Some more :
let isPalindrome = __ => (_=(__=__+'').length)==0||_==1?!0:__[0]==__.slice(-1)?isPalindrome(__.slice(1,-1)):!1
let isPalindrome = (s,i) => (i=i||0)<0||i>=(s=''+s).length>>1||s[i]==s[s.length-1-i]&&isPalindrome(s,++i);
let isPalindrome = (str) => {
var len = ~~((str=str+'').length / 2);
for (var i = 0; i < len; i++)
if (str[i] !== str[str.length - i - 1])
return false;
return true;
}
Updated now takes numbers as input

javascript, function, mathrandom, array

I'm supposed to do a math exercise with subtractions for a basic Javascript course. The exercise has to contain 5 exercises with 5 prompt-windows (e.g "10-7=", "5-1=" etc). The first number has to be bigger than the second one.
Therefore, I need to create a function that generates two random numbers and then returns them in an array. Before it returns, it has to make sure that the number in position 0 is bigger than the number in position 1, the random numbers has to be between 1-10. If the person answers the question right, an alert window should pop up and say "Right!" and the same if it's wrong.
My code so far is this, and I'm aware that it's not completely right. What's wrong? How can I make it right?
function number ()
{
var array = [a, b];
var numbers = Math.floor(Math.random()*10)+1;
array[0] = a ;
array[1] = b ;
if (a <= b)
a = a+b;
b = a-b;
a = a-b;
return a + b;
}
var mathQuestion = a + " - " + b +" = ";
var answer = a - b;
for(var i =0; i<6; i++) {
var yourAnswer = parseInt(prompt(mathQuestion));
if (yourAnswer === answer) {
alert("Right!");
}
else {
alert("Wrong!");
}
}
I'm not really sure what you are trying to achieve but I considered to have a look at your code to kind of improve it.
This code is working fine, so please have a look :)
function getTask() {
var array = [];
var numberA = getRandomNumber()
var numberB = getRandomNumber()
if (numberA > numberB) {
array[0] = numberA;
array[1] = numberB;
} else {
array[0] = numberB;
array[1] = numberA;
}
return array;
}
function getRandomNumber() {
return Math.floor(Math.random() * 10) + 1;
}
let questionCount = 1;
for (var i = 0; i < questionCount; i++) {
let numbers = getTask()
var mathQuestion = numbers[0] + " - " + numbers[1] + " = ";
var answear = numbers[0] - numbers[1];
var yourAnswear = parseInt(prompt(mathQuestion));
if (yourAnswear === answear)
alert("Right!");
else
alert("Wrong!");
}
Note: The problems was that you were missing the declaration of a and b. Put the keyword var before the values.
You can impress your teacher with ternary operators, join and sort array methods and arrow functions
Codepen demo
const getRandom = () => Math.floor(Math.random() * 10) + 1
const ask = () => {
const numbers = [getRandom(), getRandom()].sort((a, b) => b - a)
alert(
prompt(numbers.join(' - ') + ' = ') == numbers[0] - numbers[1] ?
'Right!' : 'Wrong!'
)
}
for (var i = 0; i < 6; i++) {
ask();
}

Generating random number in Array without repeatition - JavaScript

so I know this questing has been asked, but all the answers that were given are already known to me. I don't want to make a variable of all the posible numbers (that was always the answer). So to go to the question, I want to make a random number generator, that will generate me 7 numbers, that must not be the same. For example, I get random numbers:
"5,16,12,5,21,37,2" ... But I don't want the number 5 to be used again, so I want different numbers. I made a code for the generation, but I can not think of any good method/way to do this. I was thinking that maybe check if the number is already in array and if it is, then generate another number, but as I'm amateur in JavaScript, I don't know how to do this. So here is my JavaScript code:
// JavaScript Document
function TableOn()
{
document.write("<table border='1'>");
}
function TableOff()
{
document.write("</table>");
}
function RandNum()
{
var n = new Array();
for(var i=0;i<7;i++)
{
n[i] = Math.round((1+(Math.random()*40)));
}
TableOn();
for(var c=0;c<7;c=c+1)
{
document.write("<tr><td>"+n[c]+"</td></tr>");
}
TableOff();
}
In HTML I just have a button, that is onclick="RandNum()" ... Pardon for my English.
I would do it like this:
var nums = [], numsLen = 5, maxNum = 100, num;
while (nums.length < numsLen) {
num = Math.round(Math.random() * maxNum);
if (nums.indexOf(num) === -1) {
nums.push(num);
}
}
This generates an array with 5 random numbers in the range 0..100.
(numsLen cannot be greater than maxNum.)
These commands can be used to check if a value is/is not in your array:
if ( !!~n.indexOf(someVal) ) {
// someVal is in array "n"
}
if ( !~n.indexOf(someVal) ) {
// someVal is not in array "n"
}
I'd use a string, storing the generated random numbers with a divider. Then check if the newly generated number is in that string.
Something like this
generated = "";
for(var i=0;i<7;i++)
{
generate = Math.round((1+(Math.random()*40))); //generate = 5
while (generated.indexOf("[" + generate + "]") != -1) { //checking if the [5] is already in the generated string, and loop until it's a different number
generate = Math.round((1+(Math.random()*40))); //get a new random number
}
generated += "[" + generate + "]";
n[i] = generate;
}
or you can take another longer approach
for(var i=0;i<7;i++)
{
repeated = true;
while (repeated) {
repeated = false;
generate = Math.round((1+(Math.random()*40)));
for (var a=0; a < i, a++) {
if (generate == n[a]) { repeated = true; }
}
}
n[i] = generate;
}
Here's a function to generate an array of n unrepeated random numbers in [min, max):
function rands(n, min, max) {
var range = max - min;
if (range < n)
throw new RangeError("Specified number range smaller than count requested");
function shuffle() {
var deck = [], p, t;
for (var i = 0; i < range; ++i)
deck[i] = i + min;
for (i = range - 1; i > 0; --i) {
p = Math.floor(Math.random() * i);
t = deck[i];
deck[i] = deck[p];
deck[p] = t;
}
return deck.slice(0, n);
}
function find() {
var used = {}, rv = [], r;
while (rv.length < n) {
r = Math.floor(Math.random() * range + min);
if (!used[r]) {
used[r] = true;
rv.push(r);
}
}
return rv;
}
return range < 3 * n ? shuffle() : find();
}
This code checks the range of possible values and compares it to the number of random values requested. If the range is less than three times the number of values requested, the code uses the shuffle to avoid the terrible performance of the lookup approach. If the range is large, however, the lookup approach is used instead.
Not sure if OP's requirement of non-repeating is really needed, but here's a fiddle of something that could work if your number range isn't too big:
http://jsfiddle.net/6rEDV/1/
function range(start, end, step) {
if (typeof step === 'undefined') {
step = 1;
}
if (typeof start === 'undefined' || typeof end === 'undefined') {
throw TypeError('Must have start and end');
}
var ret = [];
for (var i = start; i <= end; i += step) {
ret.push(i);
}
return ret;
};
// source: http://stackoverflow.com/a/6274381/520857
function shuffle(o) { //v1.0
for(var j, x, i = o.length; i; j = Math.floor(Math.random() * i), x = o[--i], o[i] = o[j], o[j] = x);
return o;
};
function getNonRepeatingRand(min, max, num) {
var arr = shuffle(range(min, max));
return arr.slice(0, num-1);
}
// Get 7 random numbers between and including 1 and 1000 that will *not* repeat
console.log(getNonRepeatingRand(1,1000,7));
A possibly slower, but less memory intensive method:
http://jsfiddle.net/Qnd8Q/
function rand(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
function getNonRepeatingRand(min, max, num) {
var ret = [];
for (var i = 0; i < num; i++) {
var n = rand(min, max);
if (ret.indexOf(n) == -1) {
ret.push(n);
} else {
i--;
}
}
return ret;
}
console.log(getNonRepeatingRand(1,5,5));
var n = new Array(),num;
function TableOn()
{
document.write("<table border='1'>");
}
function TableOff()
{
document.write("</table>");
}
function check_repition()
{
num=Math.round((1+(Math.random()*40)))
if(n.indexOf(num)==-1)
return true;
else
return false;
}
function RandNum()
{
for(var i=0;i<7;i++)
{
if(check_repition())
n[i] =num;
else // keep checking
{
check_repition()
n[i] =num;
}
}
TableOn();
for(var c=0;c<7;c=c+1)
{
document.write("<tr><td>"+n[c]+"</td></tr>");
}
TableOff();
}
RandNum()

javascript unique random numbers

experts
I wish to generate unique random numbers between two numbers (from text box of web page).
I am using array to store numbers. when user click button it gives first random number, store it in array, and when user clicks button again, it generate random number, compares it with array numbers, and if different, stores it and displays it.
If maximum possible different numbers are show it, clears array and informs user.
I have written code, but it gives error : stack overflow, or sometimes I get duplicate results shown.
Can anyone shed a light on code:
var allnums = new Array();
var num1= new Number;
var num2= new Number;
function funClick()
{
var num1 = Number(document.getElementById('lnum').value);
var num2 = Number(document.getElementById('hnum').value);
if (allnums.length==num2)
{
alert("Maximum non-duplicate numbers served. Now resetting the counter.");
allnums = [];
return;
}
if (num1<num2)
{
x = Math.floor(Math.random() * (num2 - num1 + 1)) + num1;
funShow(x);
}
else
{
alert("You entered wrong number criteria!");
}
}
function funShow(x)
{
var bolFound = false;
for (var i=0;i<allnums.length;i++)
{
if((allnums[i])==x)
{
funClick();
}
}
if (bolFound == false)
{
document.getElementById('rgen').innerText = x;
allnums.push(x);
}
}
I fail to see how that code is generating a stack overflow (even though funShow has a call to funClick and funClick has a call to funShow, funShow's call to funClick should never happen because of a logic error -- fix the error and you'll get a stack overflow, though), but it has several issues. See the comments:
// style: Use [], not new Array()
var allnums = new Array();
// `new Number` doesn't do anything useful here
var num1 = new Number;
var num2 = new Number;
function funClick() {
// For user-entered values, use parseInt(value, 10) to parse them into numbers
var num1 = Number(document.getElementById('lnum').value);
var num2 = Number(document.getElementById('hnum').value);
if (allnums.length == num2) {
alert("Maximum non-duplicate numbers served. Now resetting the counter.");
allnums = [];
return;
}
// & is a bitwise AND operation, not a logical one. If your goal is to see
// if both numbers are !0, though, it works but is obtuse.
// Also, there is no ltnum2 variable anywhere, so trying to read its value
// like this should be throwing a ReferenceError.
if (num1 & ltnum2) {
// You're falling prey to The Horror of Implicit Globals, x has not
// been declared.
x = Math.floor(Math.random() * (num2 - num1 + 1)) + num1;
funShow(x);
} else {
alert("You entered wrong number criteria!");
}
}
function funShow(x) {
var bolFound = false;
// Again, & is a bitwise AND operation. This loop will never run, because
// you start with 0 and 0 & anything = 0
// But it should be throwing a ReferenceError, as there is no ltallnums
// anywhere.
for (var i = 0; i & ltallnums.length; i++) {
if ((allnums[i]) == x) {
funClick();
}
}
// This condition will always be true, as you've done nothing to change
// bolFound since you set it to false
if (bolFound == false) {
document.getElementById('rgen').innerText = x;
allnums.push(x);
}
}
There are two ways to approach this. Here's one that's basically what you were trying to do, but without recursion:
function funClick() {
var num1 = parseInt(document.getElementById('lnum').value, 10);
var num2 = parseInt(document.getElementById('hnum').value, 10);
var nums = [];
var targetCount;
var x;
// Check the inputs
if (isNaN(num1) || isNaN(num2) || num2 <= num1) {
alert("Please ensure that hnum is higher than lnum and both are really numbers.");
return;
}
// Find out how many integers there are in the range num1..num2 inclusive
targetCount = num2 - num1 + 1;
// Produce that many random numbers
while (nums.length < targetCount) {
x = Math.floor(Math.random() * (num2 - num1 + 1)) + num1;
if (nums.indexOf(x) < 0) {
nums.push(x);
}
}
// Show the result
document.getElementById('rgen').innerText = nums.join(", ");
}
Live Example | Source
The problem with that is that it can take a long time to fill the last few slots, since we have to hit them randomly.
The other way is to produce the array with the numbers in order, then mess it up. It can be dramatically more efficient for large ranges. Something like this:
function funClick() {
var num1 = parseInt(document.getElementById('lnum').value, 10);
var num2 = parseInt(document.getElementById('hnum').value, 10);
var nums = [];
var x;
// Check the inputs
if (isNaN(num1) || isNaN(num2) || num2 <= num1) {
alert("Please ensure that hnum is higher than lnum and both are really numbers.");
return;
}
// Create an array with those numbers in order
for (x = num1; x <= num2; ++x) {
nums.push(x);
}
// Sort it with a random comparison function
nums.sort(function(a, b) {
return 0.5 - Math.random();
});
// Show the result
document.getElementById('rgen').innerText = nums.join(", ");
}
Live Example | Source
But, just doing the nums.sort(...) randomly once may well not be as successful at producing random results; see this article for more. (Thanks to eBusiness for that link and for his input on the below.)
So you may want to go further and throw in further random operations. Here's another example:
function funClick() {
var num1 = parseInt(document.getElementById('lnum').value, 10);
var num2 = parseInt(document.getElementById('hnum').value, 10);
var nums = [];
var n, x, y;
var num;
// Check the inputs
if (isNaN(num1) || isNaN(num2) || num2 <= num1) {
alert("Please ensure that hnum is higher than lnum and both are really numbers.");
return;
}
// Create an array with those numbers in order
for (n = num1; n <= num2; ++n) {
nums.push(n);
}
// We only need to shuffle it if it's more than one element long
if (nums.length > 1) {
// Sort it "randomly"
nums.sort(function(a, b) {
return 0.5 - Math.random();
});
// Throw a bunch of random swaps in there
for (n = 0; n < nums.length; ++n) {
do {
x = Math.floor(Math.random() * nums.length);
}
while (x === n);
num = nums[x];
nums[x] = nums[n];
nums[n] = num;
}
}
// Show the result
document.getElementById('rgen').innerText = nums.join(", ");
}
Live Example | Source
That does the array sort thing as a starting point, but then does a bunch of random swaps between elements as well. It still runs in constant time, but should have a better result than using the array sort alone. Naturally, you'll want to test the distribution.
use an array:
var uniqueRandomNumbers = new Array();
var totalNumbers = 100;
for (var i=0; i<totalNumbers; i++){
uniqueRandomNumbers.push(i);
}
uniqueRandomNumbers.sort(function() {return 0.5 - Math.random();});
var uniqueNumber;
for(var i=0; i<uniqueRandomNumbers.length; i++){
uniqueNumber = uniqueRandomNumbers[i];
//do something with the number
}
Since I can't edit Crowder's answer, here is the simple unbiased way of scrambling an array:
function scramble(nums){
for (var n = nums.length; n; n--) {
var x = Math.floor(Math.random() * n);
var num = nums[n-1];
nums[n-1] = nums[x];
nums[x] = num;
}
}

Categories