Return random number excluding specific range - javascript - javascript
I'm trying to return a random number within a specific range while also excluding a specific range within it. I've seen similar questions posed, but I can't get it to work. Here's what I have so far:
var x = xFunction();
function xFunction() {
return parseFloat(Math.round(Math.random() * 2250) / 1000).toFixed(3);
}
if (x > 1.250 && x < 2.001 ) {
// this number is excluded so redo xFunction();
} else {
// this number is acceptable so do some code
}
Any help is appreciated. Thank you so much!
One way to handle this is to look for a random number in the range with the excluded part removed. For example if you were looking for a random number between 0 and 100 with 70-80 removed, you would find a random number between 0 and 90 (removing the 10 from the excluded range). Then if any value falls above 70 you add the excluded range back. This will preserve the appropriate ratio of randomness for each range and you should see results mostly from the lower range with a few from the upper range because that is a larger percentage of the distribution.
(I've moved the division and rounding out of the function just to make it clearer how it works.)
function xFunction(max, exclude) {
let excluded_range = exclude[1] - exclude[0]
let rand = Math.random() * (max - excluded_range)
if (rand > exclude[0]) rand += excluded_range
return rand
}
for (let x = 0; x<10; x++){
let r = xFunction(2250, [1250, 2000])
console.log ((r / 1000).toFixed(3));
}
If you pick a random 0 or 1 and use that to determine the range as recommended in the comments, you will end up with approximately half of the result in the much smaller top range. This will bias your results toward that top range rather than truly finding a random number within the whole range.
Related
Compress group of arrays into smallest possible string
This question can be answered using javascript, underscore or Jquery functions. given 4 arrays: [17,17,17,17,17,18,18,18,18,18,19,19,19,19,19,20,20,20,20,20] => x coordinate of unit [11,12,13,14,15,11,12,13,14,15,11,12,13,14,15,11,12,13,14,15] => y coordinate of unit [92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92] => x (unit moves to this direction x) [35,36,37,38,39,35,36,37,38,39,35,36,37,38,39,35,36,37,38,39] => y (unit moves to this direction y) They are very related to each other. For example first element of all arrays: [17,11,92,35] is unit x/y coordinates and also x/y coordinates of this units target. So here are totally 5*4 = 20 units. Every unit has slightly different stats. These 4 arrays of units x/y coordinates visually looks like an army of 20 units "x" (targeting "o"): xxxxx o xxxxx o xxxxx o xxxxx o There will always be 4 arrays. Even if 1 unit, there will be 4 arrays, but each size of 1. This is the simplest situation and most common. In real situation, every unit has totally 20 different stats(keys) and 14 keys are mostly exact to other group of units - all 14 keys. So they are grouped as an army with same stats. Difference is only coordinates of the unit and also coordinates of the units target. I need to compress all this data into as small as possible data, which later can be decompressed. There can also be more complex situations, when all these 14 keys are accidently same, but coordinates are totally different from pattern. Example: [17,17,17,17,17,18,18,18, 215, 18,18,19,19,19,19,19,20,20,20,20,20] => x coordinate of unit [11,12,13,14,15,11,12,13, 418, 14,15,11,12,13,14,15,11,12,13,14,15] => y coordinate of unit [92,92,92,92,92,92,92,92, -78, 92,92,92,92,92,92,92,92,92,92,92,92] => x (unit moves to this direction x) [35,36,37,38,39,35,36,37, -887, 38,39,35,36,37,38,39,35,36,37,38,39] => y (unit moves to this direction y) In this situation i need to extract this array as for 2 different armies. When there are less than 3 units in army, i just simply write these units without the pattern - as [215,418,-78,-887],[..] and if there are more than 2 units army, i need a compressed string with pattern, which can be decompressed later. In this example there are 21 units. It just has to be splitted into armies of 1 unit and (5x4 = 20) untis army.
In assumption that every n units has a pattern, encode units with n: sequence units count ssx: start of source x dsx: difference of source x ssy: start of source y dsy: difference of source y stx: start of target x dtx: difference of target x sty: start of target y dty: difference of target y by the array: [n,ssx,dsx,ssy,dsy,stx,dtx,sty,dty] so that the units: [17,17,17,17,17], [11,12,13,14,15], [92,92,92,92,92], [35,36,37,38,39] are encoded: [5,17,0,11,1,92,0,35,1] of course if you know in advance that, for example the y targets are always the same for such a sequence you can give up the difference parameter, to have: [n,ssx,dsx,ssy,---,stx,dtx,sty,---] => [n,ssx,dsx,ssy,stx,dtx,sty], and so on. For interruption of a pattern like you mentioned in your last example, you can use other 'extra' arrays, and then insert them in the sequence, with: exsx: extra unit starting x exsy: extra unit starting y extx: extra unit target x exty: extra unit target y m: insert extra unit at so that the special case is encoded: { patterns:[ [5,17,0,11,1,92,0,35,1], [5,18,0,11,1,92,0,35,1], [5,19,0,11,1,92,0,35,1], [5,17,0,11,1,92,0,35,1] ], extras: [ [215,418,-78,-887,8] // 8 - because you want to insert this unit at index 8 ] } Again, this is a general encoding. Any specific properties for the patterns may further reduce the encoded representation. Hope this helps.
High compression using bitstreams You can encode sets of values into a bit stream allowing you to remove unused bits. The numbers you have shown are not greater than -887 (ignoring the negative) and that means you can fit all the numbers into 10 bits saving 54 bits per number (Javascript uses 64 bit numbers). Run length compression You also have many repeated sets of numbers which you can use run length compression on. You set a flag in the bitstream that indicates that the following set of bits represents a repeated sequence of numbers, then you have the number of repeats and the value to repeat. For sequences of random numbers you just keep them as is. If you use run-length compression you create a block type structure in the bit stream, this makes it possible to embed further compression. As you have many numbers that are below 128 many of the numbers can be encoded into 7 bits, or even less. For a small overhead (in this case 2 bits per block) you can select the smallest bit size to pack all the numbers in that block in. Variable bit depth numbers I have created a number type value that represent the number of bits used to store numbers in a block. Each block has a number type and all numbers in the block use that type. There are 4 number types that can be encoded into 2 bits. 00 = 4 bit numbers. Range 0-15 01 = 5 bit numbers. Range 0-31 10 = 7 bit numbers. Range 0-127 11 = 10 bit numbers. Range 0-1023 The bitstream To make this easy you will need a bit stream read/write. It allows you to easily write and read bits from a stream of bits. // Simple unsigned bit stream // Read and write to and from a bit stream. // Numbers are stored as Big endian // Does not comprehend sign so wordlength should be less than 32 bits // methods // eof(); // returns true if read pos > buffer bit size // read(numberBits); // number of bits to read as one number. No sign so < 32 // write(value,numberBits); // value to write, number of bits to write < 32 // getBuffer(); // return object with buffer and array of numbers, and bitLength the total number of bits // setBuffer(buffer,bitLength); // the buffers as an array of numbers, and bitLength the total number of bits // Properties // wordLength; // read only length of a word. function BitStream(){ var buffer = []; var pos = 0; var numBits = 0; const wordLength = 16; this.wordLength = wordLength; // read a single bit var readBit = function(){ var b = buffer[Math.floor(pos / wordLength)]; // get word b = (b >> ((wordLength - 1) - (pos % wordLength))) & 1; pos += 1; return b; } // write a single bit. Will fill bits with 0 if wite pos is moved past buffer length var writeBit = function(bit){ var rP = Math.floor(pos / wordLength); if(rP >= buffer.length){ // check that the buffer has values at current pos. var end = buffer.length; // fill buffer up to pos with zero while(end <= rP){ buffer[end] = 0; end += 1; } } var b = buffer[rP]; bit &= 1; // mask out any unwanted bits bit <<= (wordLength - 1) - (pos % wordLength); b |= bit; buffer[rP] = b; pos += 1; } // returns true is past eof this.eof = function(){ return pos >= numBits; } // reads number of bits as a Number this.read = function(bits){ var v = 0; while(bits > 0){ v <<= 1; v |= readBit(); bits -= 1; } return v; } // writes value to bit stream this.write = function(value,bits){ var v; while(bits > 0){ bits -= 1; writeBit( (value >> bits) & 1 ); } } // returns the buffer and length this.getBuffer = function(){ return { buffer : buffer, bitLength : pos, }; } // set the buffer and length and returns read write pos to start this.setBuffer = function(_buffer,bitLength){ buffer = _buffer; numBits = bitLength; pos = 0; } } A format for your numbers Now to design the format. The first bit read from a stream is a sequence flag, if 0 then the following block will be a repeated value, if 1 the following block will be a sequence of random numbers. Block bits : description; repeat block holds a repeated number bit 0 : Val 0 = repeat bit 1 : Val 0 = 4bit repeat count or 1 = 5bit repeat count then either bits 2,3,4,5 : 4 bit number of repeats - 1 bits 6,7 : 2 bit Number type or bits 2,3,4,5,6 : 5 bit number of repeats - 1 bits 7,8 : 2 bit Number type Followed by Then a value that will be repeated depending on the number type End of block sequence block holds a sequence of random numbers bit 0 : Val 1 = sequence bit 1 : Val 0 = positive sequence Val 1 = negative sequence bits 2,3,4,5 : 4 bit number of numbers in sequence - 1 bits 6,7 : 2 bit Number type then the sequence of numbers in the number format End of block. Keep reading blocks until the end of file. Encoder and decoder The following object will encode and decode the a flat array of numbers. It will only handles numbers upto 10 bits long, So no values over 1023 or under -1023. If you want larger numbers you will have to change the number types that are used. To do this change the arrays const numberSize = [0,0,0,0,0,1,2,2,3,3,3]; // the number bit depth const numberBits = [4,5,7,10]; // the number bit depth lookup; If you want max number to be 12 bits -4095 to 4095 ( the sign bit is in the block encoding). I have also shown the 7 bit number type changed to 8. The first array is used to look up the bit depth, if I have a 3 bit number you get the number type with numberSize[bitcount] and the bits used to store the number numberBits[numberSize[bitCount]] const numberSize = [0,0,0,0,0,1,2,2,2,3,3,3,3]; // the number bit depth const numberBits = [4,5,8,12]; // the number bit depth lookup; function ArrayZip(){ var zipBuffer = 0; const numberSize = [0,0,0,0,0,1,2,2,3,3,3]; // the number bit depth lookup; const numberBits = [4,5,7,10]; // the number bit depth lookup; this.encode = function(data){ // encodes the data var pos = 0; function getRepeat(){ // returns the number of repeat values var p = pos + 1; if(data[pos] < 0){ return 1; // ignore negative numbers } while(p < data.length && data[p] === data[pos]){ p += 1; } return p - pos; } function getNoRepeat(){ // returns the number of non repeat values // if the sequence has negitive numbers then // the length is returned as a negative var p = pos + 1; if(data[pos] < 0){ // negative numbers while(p < data.length && data[p] !== data[p-1] && data[p] < 0){ p += 1; } return -(p - pos); } while(p < data.length && data[p] !== data[p-1] && data[p] >= 0){ p += 1; } return p - pos; } function getMax(count){ var max = 0; var p = pos; while(count > 0){ max = Math.max(Math.abs(data[p]),max); p += 1; count -= 1; } return max; } var out = new BitStream(); while(pos < data.length){ var reps = getRepeat(); if(reps > 1){ var bitCount = numberSize[Math.ceil(Math.log(getMax(reps) + 1) / Math.log(2))]; if(reps < 16){ out.write(0,1); // repeat header out.write(0,1); // use 4 bit repeat count; out.write(reps-1,4); // write 4 bit number of reps out.write(bitCount,2); // write 2 bit number size out.write(data[pos],numberBits[bitCount]); pos += reps; }else { if(reps > 32){ // if more than can fit in one repeat block split it reps = 32; } out.write(0,1); // repeat header out.write(1,1); // use 5 bit repeat count; out.write(reps-1,5); // write 5 bit number of reps out.write(bitCount,2); // write 2 bit number size out.write(data[pos],numberBits[bitCount]); pos += reps; } }else{ var seq = getNoRepeat(); // get number no repeats var neg = seq < 0 ? 1 : 0; // found negative numbers seq = Math.min(16,Math.abs(seq)); // check if last value is the start of a repeating block if(seq > 1){ var tempPos = pos; pos += seq; seq -= getRepeat() > 1 ? 1 : 0; pos = tempPos; } // ge the max bit count to hold numbers var bitCount = numberSize[Math.ceil(Math.log(getMax(seq) + 1) / Math.log(2))]; out.write(1,1); // sequence header out.write(neg,1); // write negative flag out.write(seq - 1,4); // write sequence length; out.write(bitCount,2); // write 2 bit number size while(seq > 0){ out.write(Math.abs(data[pos]),numberBits[bitCount]); pos += 1; seq -= 1; } } } // get the bit stream buffer var buf = out.getBuffer(); // start bit stream with number of trailing bits. There are 4 bits used of 16 so plenty // of room for aulturnative encoding flages. var str = String.fromCharCode(buf.bitLength % out.wordLength); // convert bit stream to charcters for(var i = 0; i < buf.buffer.length; i ++){ str += String.fromCharCode(buf.buffer[i]); } // return encoded string return str; } this.decode = function(zip){ var count,rSize,header,_in,i,data,endBits,numSize,val,neg; data = []; // holds character codes decompressed = []; // holds the decompressed array of numbers endBits = zip.charCodeAt(0); // get the trailing bits count for(i = 1; i < zip.length; i ++){ // convert string to numbers data[i-1] = zip.charCodeAt(i); } _in = new BitStream(); // create a bitstream to read the bits // set the buffer data and length _in.setBuffer(data,(data.length - 1) * _in.wordLength + endBits); while(!_in.eof()){ // do until eof header = _in.read(1); // read header bit if(header === 0){ // is repeat header rSize = _in.read(1); // get repeat count size if(rSize === 0){ count = _in.read(4); // get 4 bit repeat count }else{ count = _in.read(5); // get 5 bit repeat count } numSize = _in.read(2); // get 2 bit number size type val = _in.read(numberBits[numSize]); // get the repeated value while(count >= 0){ // add to the data count + 1 times decompressed.push(val); count -= 1; } }else{ neg = _in.read(1); // read neg flag count = _in.read(4); // get 4 bit seq count numSize = _in.read(2); // get 2 bit number size type while(count >= 0){ if(neg){ // if negative numbers convert to neg decompressed.push(-_in.read(numberBits[numSize])); }else{ decompressed.push(_in.read(numberBits[numSize])); } count -= 1; } } } return decompressed; } } The best way to store a bit stream is as a string. Javascript has Unicode strings so we can pack 16 bits into every character The results and how to use. You need to flatten the array. If you need to add extra info to reinstate the multi/dimensional arrays just add that to the array and let the compressor compress it along with the rest. // flatten the array var data = [17,17,17,17,17,18,18,18,18,18,19,19,19,19,19,20,20,20,20,20,11,12,13,14,15,11,12,13,14,15,11,12,13,14,15,11,12,13,14,15,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,35,36,37,38,39,35,36,37,38,39,35,36,37,38,39,35,36,37,38,39]; var zipper = new ArrayZip(); var encoded = zipper.encode(data); // packs the 80 numbers in data into 21 characters. // compression rate of the data array 5120 bits to 336 bits // 93% compression. // or as a flat 7bit ascii string as numbers 239 charcters (including ,) // 239 * 7 bits = 1673 bits to 336 bits 80% compression. var decoded = zipper.decode(encoded); I did not notice the negative numbers at first so the compression does not do well with the negative values. var data = [17,17,17,17,17,18,18,18, 215, 18,18,19,19,19,19,19,20,20,20,20,20, 11,12,13,14,15,11,12,13, 418, 14,15,11,12,13,14,15,11,12,13,14,15, 92,92,92,92,92,92,92,92, -78, 92,92,92,92,92,92,92,92,92,92,92,92, 35,36,37,38,39,35,36,37, -887, 38,39,35,36,37,38,39,35,36,37,38,39] var encoded = zipper.encode(data); // packs the 84 numbers in data into 33 characters. // compression rate of the data array 5376 bits to 528 bits var decoded = zipper.decode(encoded); Summary As you can see this results in a very high compression rate (almost twice as good as LZ compression). The code is far from optimal and you could easily implement a multi pass compressor with various settings ( there are 12 spare bits at the start of the encoded string that can be used to select many options to improve compression.) Also I did not see the negative numbers until I came back to post so the fix for negatives is not good, so you can some more out of it by modifying the bitStream to understand negatives (ie use the >>> operator)
Robust comparison of positive/negative numbers value by a threshold value
I would like to calculate whether a variable average differs from another variable trackingAmount by a certain threshold either positively(+) or negatively (-). These are the constraints: If the difference (+/-) between average and trackingAmount exceeds the threshold value then I would like to trigger a function calcMultiTrack() Threshold value in the example is called trackTolerance average can be positive or negative, same goes for trackingAmount What is the most robust, (maybe elegant is a better word here), way to handle such cases? This is what I do so far. average = average / (selItemsDimArray.length - 1); var trackingAmount = 3 var trackTolerance = 0.2 //If number is positive if (average >= 0) { if (average < (trackingAmount - trackTolerance) || average > (trackingAmount + trackTolerance)) { calcMultiTrack(); //This is the function I want to call if the numbers are not the same(threshold value applies) console.log("Positive average that differs with trackingAmount by more than +- tolerance"); } } //Else number is negative else { if (average < (-(trackingAmount - trackTolerance)) || average > (-(trackingAmount + trackTolerance))) { calcMultiTrack(); console.log("Negative average that differs with trackingAmount by more than +- tolerance"); } }
The absolute value function is the ideal way to compute the distance between two real numbers. Math.abs(average - trackingAmount) < threshold
Normally this kind of comparison is done with Math.abs(x - expected) < threshold if the values have an absolute meaning and the origin is arbitrary (e.g. a position). If otherwise the values are positive and zero based (e.g. a weight) then normally a relative distance is used Math.abs(x - expected) / expected < threshold where for example using threshold = 0.1 means a 10% error.
Random number between negative and positive value [duplicate]
This question already has answers here: Closed 10 years ago. Possible Duplicate: Generating random numbers in Javascript in a specific range? How can i get a random value between, for example, from -99 to 99, excluding 0?
var num = Math.floor(Math.random()*99) + 1; // this will get a number between 1 and 99; num *= Math.round(Math.random()) ? 1 : -1; // this will add minus sign in 50% of cases Altogether var ranNum = Math.ceil(Math.random() * 99) * (Math.round(Math.random()) ? 1 : -1)
This returns what you want function getNonZeroRandomNumber(){ var random = Math.floor(Math.random()*199) - 99; if(random==0) return getNonZeroRandomNumber(); return random; } Here's a functional fiddle EDIT To contribute for future readers with a little debate happened in the comments which the user #MarkDickinson made a indeed relevant contribution to my first code posted, I've decided to make another fiddle with a fast comparison between using Math.floor() and Math.round() functions to return the value the op wanted. First Scenario: Using var random = Math.round(Math.random()*198) - 99; (My first suggestion) function getNonZeroRandomNumberWithMathRound(){ var random = Math.round(Math.random()*198) - 99; if(random==0) return getNonZeroRandomNumber(); return random; } Second scenario: Using var random=Math.floor(Math.random()*199) - 99; (Mark suggestion) function getNonZeroRandomNumberWithMathFloor(){ var random = Math.floor(Math.random()*199) - 99; if(random==0) return getNonZeroRandomNumber(); return random; } Methodology Since it's a short debate I've chosen fiddle.net to do the comparison. The test consists of running the above functions 100.000 times and then retrieving how much times the extreme numbers 99 and -99 would appear against a other number, let's say 33 and -33. The test will then give a simple output consisting of the percentage of appearances from 99 and -99 and the percentage of appearances of 33 and -33. It'll be used the Webkit implementation from Safari 6.0.2 to the give the output from this answer but anyone can test with your favourite browser late on fiddle.net Result from first scenario: Percentage of normal ocurrences:0.97% Percentage of extreme ocurrences:0.52% Percentage of extreme ocurrences relative to normal ocurrences:53.4% // Half the chances indeed Result from second scenario: Percentage of normal ocurrences:1.052% Percentage of extreme ocurrences:0.974% Percentage of extreme ocurrences relative to normal ocurrences:92% //Closer of a fair result with a minimal standard deviation The result can be seen here: http://jsfiddle.net/brunovieira/LrXqh/
Here's a generalized solution that will let you set the boundaries, and opt in/out of including the 0. var pos = 99, neg = 99, includeZero = false, result; do result = Math.ceil(Math.random() * (pos + neg)) - neg; while (includeZero === false && result === 0); The pos and neg values are inclusive. This way there's no requirement that the positive and negative ranges be balanced. Or if you're worried about the rerun due to a single excluded value, you can just make the initial range less by one, and add 1 to any result greater than or equal to 0. var pos = 5, neg = 5, result; result = Math.floor(Math.random() * (pos + neg)) - neg; result = result < 0 ? result : result + 1; That last line could be shorter if you prefer: result += (result >= 0)
How to generate skewed random numbers in Javascript?
Using Javascript, how can I generate random numbers that are skewed towards one end or the other of the distribution? Or ideally an point within the range? For context: I'm creating a UI that has uses a grid of random grey squares. I'm generating the grey's RGB values using Math.random() but would like to be able to skew the greys to be on average darker or lighter while still having the full range from black to white represented. (I think this is a similar question to Skewing java random number generation toward a certain number but I'm working with Javascript...) Any help greatly appreciated.
Raise Math.random() to a power to get a gamma curve - this changes the distribution between 0 and 1, but 0 and 1 stay constant endpoints. var r= Math.pow(Math.random(), 2); var colour= 'rgb('+r*255+', '+r*255+', '+r*255+')'; For gamma>1, you will get darker output; for 0<gamma<1 you get lighter. (Here, '2' gives you the x-squared curve; the equidistant lightness would be '0.5' for the square-root curve.)
This seems a little crude and less graceful than #bobince's answer, but what the hell. //setup var colours = [], num_colours = 10, skew_to = 255, skew_chance = 20; //get as many RGB vals as required for (var i=0; i<num_colours; i++) { //generate random grey var this_grey = Math.floor(Math.random() * 256); //skew it towards the #skew_to endpoint, or leave as-is? if (Math.floor(Math.random() * 100) >= skew_chance && this_grey != skew_to) { //skew by random amount (0 - difference between curr val and endpoint) var skew_amount = Math.floor(Math.random() * Math.abs(this_grey - skew_to)); this_grey += ' (skewed to '+(skew_to < this_grey ? this_grey - skew_amount : this_grey + skew_amount)+')'; } colours.push(this_grey); } console.log(colours); Essentially it generates random greys then decides, based on probably specified (as a percentage) in skew_chance, whether to skew it or not. (In case you wanted to make this occasional, not constant). If it decides to skew, a random number is then added or subtracted from the grey value (depending on whether the skew endpoint is under or above the current value). This random number is a number between 0 and the absolute difference between the current value and the endpoint, e.g. if current value is 40, and the endpoint is 100, the number added would be between 0 and 60. Like I say, #bobince's answer is somewhat, er, more graceful!
[This might be a little different approach.] This approach deals with getting the number in the following fashion: random = numberToSkewTo + random(-1,1)*stdDeviation Where: numberToSkewTo is the number you want to skew towards. stdDeviation is the deviation from numberToSkewTo numberToSkewTo + abs(stdDeviation) <= MAX_NUMBER and numberToSkewTo - abs(stdDeviation) >= MIN_NUMBER What the following code does is, it pick a random number around the given number with constantly increasing standard deviations. It returns the average of results. function skew(skewTo,stdDev){ var rand = (Math.random()*2 - 1) + (Math.random()*2 - 1) + (Math.random()*2 - 1); return skewTo + rand*stdDev; } function getRandom(skewTo){ var difference = Math.min(skewTo-MIN_NUMBER, MAX_NUMBER-skewTo); var steps = 5; var total = 0.0; for(var i=1; i<=steps; i++) total += skew(skewTo, 1.0*i*difference/steps); return total/steps }
Javascript Brainteaser - Reverse Number Determining
Lets say I have a list of numbers in the following form(Ignore the | they are there for formating help). 00|00|xx 00|xx|00 xx|00|00 etc. Rules: XX can be any number between 1 and 50. No XX values can be identical. Now I select a random set of numbers(no duplicates) from a list qualifying the above format, and randomly add and subtract them. For example 000011 - 002400 - 230000 = -232389 How can I determine the original numbers and if they were added or subtracted solely from -232389? I'm stumped. Thanks! EDIT: I was looking for a function so I ended up having to make one. Its just a proof of concept function so variables names are ugly http://jsfiddle.net/jPW8A/. There are bugs in the following implementation, and it fails to work in a dozen of scenarios. Check the selected answer below. function reverse_add_subtract(num){ var nums = []; while(num != 0){ var str = num.toString(), L = Math.abs(num).toString().length, MA = str.match(/^(-?[0-9]?[0-9])([0-9][0-9])([0-9][0-9])*$/); if(MA){ var num1 = MA[1], num2 = MA[2]; }else{ var num1 = num, num2 = 0; } if(L%2)L++; if( num2 > 50){ if(num < 0) num1--; else num1++; } nums.push(num1); var add = parseInt(num1 + Array(--L).join(0),10); num = (num-add); } return nums; } reverse_add_subtract(-122436);
First note that each xx group is constrained from [1, 50). This implies that each associated pair in the number that is in the range [50, 99) is really 100 - xx and this means that it "borrowed from" the group to the left. (It also means that there is only one set of normalized numbers and one solution, if any.) So given the input 23|23|89 (the initial xx spots from -232389), normalize it -- that is, starting from the right, if the value is >= 50, get 100 - value and carry the 100 rightward (must balance). Example: (23 * 100) + 89 = 2300 * 89 = 2400 - 11 = 2389. And example that shows that it doesn't matter if it's negative as the only things that change is the signs: (-23 * 100) - 89 = -2300 - 89 = -2400 + 11 = -2389 (Notes: Remember, 1 is added to the 23 group to make it 24: the sign of the groups is not actually considered in this step, the math is just to show an example that it's okay to do! It may be possible to use this step to determine the sign and avoid extra math below, but this solution just tries to find the candidate numbers at this step. If there are any repeats of the number groups after this step then there is no solution; otherwise a solution exists.) The candidate numbers after the normalization are then 23|24|11 (let's say this is aa|bb|cc, for below). All the xx values are now known and it is just a matter of finding the combination such that e * (aa * 10000) + f * (bb * 100) + g * (cc * 1) = -232389. The values aa, bb, cc are known from above and e, f, and g will be either 1 or -1, respectively. Solution Warning: A method of finding the addition or subtraction given the determined numbers (determined above) is provided below the horizontal separator. Take a break and reflect on the above sections before deciding if the extra "hints" are required. This can then be solved by utilizing the fact that all the xx groups are not dependent after the normalization. (At each step, try to make the input number for the next step approach zero.) Example: -232389 + (23 * 10000) = -2389 (e is -1 because that undoes the + we just did) -2389 + (24 * 100) = 11 (likewise, f is -1) 11 - (11 * 1) = 0 (0 = win! g is 1 and solution is (-1 * 23 * 10000) + (-1 * 24 * 100) + (1 * 11 * 1) = -232389) Happy homeworking.
First, your math is wrong. Your leading zeros are converting the first two numbers to octal. If that is the intent, the rest of this post doesn't exactly apply but may be able to be adapted. 11-2400-230000 = -232389 Now the last number is easy, it's always the first two digits, 23 in this case. Remove that: -232389 + 230000 = -2389 Your 2nd number is the next 100 below this, -2400 in this case. And your final number is simply: -2389 + 2400 = 11
Aww! Someone posted an answer saying "brute force it" that I was about to respond to with: function find(num){for(var i=1;i<50;i++){for(var o1=0;o1<2;o1++){for(var j=1;j<50;j++){for(var o2=0;o2<2;o2++){for(var k=1;k<50;k++){var eq;if(eval(eq=(i+(o1?'+':'-')+j+'00'+(o2?'+':'-')+k+'0000'))==num){ return eq; }}}}}}} they deleted it... :( It was going to go in the comment, but here's a cleaner format: function find(num){ for(var i=1;i<50;i++){ for(var o1=0;o1<2;o1++){ for(var j=1;j<50;j++){ for(var o2=0;o2<2;o2++){ for(var k=1;k<50;k++){ var eq; if(eval(eq=(i+(o1?'+':'-')+j+'00'+(o2?'+':'-')+k+'0000'))==num){ return eq; } } } } } } }