-1

Okey ive got to make a program that counts togheter 2 random values.

In the program there is supposed to be a list (1-9) in a function. From this list im supposed to get 2 random values (im recommended to use array.splice()).

After the 2 random values have been choosen the program is supposed to calculate them (addition) into a total value randomvalue1 + randomvalue2 = totalvalue;

THE CATCH! While executing the 2 randomvalues cant be of the same value (5+5, 3+3, 2+2, and so on is invalid)

THE SECOND CATCH! the random values are not allowed to be executed 2 times in a row. what i mean is that the program should not allow for randomvalue1 to be equal to the same value two times (or more) in a row (this also applies for randomvalue2)

So far i got suggested this code but it doesnt check if the same values appear x amount of times in a row

function makeRandom(list) {
 function getRandomIndex() {
     return Math.floor(Math.random() * list.length);
 }

let index1 = getRandomIndex(),
    index2 = getRandomIndex();

while (index1 === index2) index2 = getRandomIndex();

return list[index1] + '+' + list[index2];
}
console.log(makeRandom([1, 2, 3, 4, 5, 6, 7, 8, 9]));
  • 3
    what does not work (beside the fact that `splice` returns an array)? – Nina Scholz Nov 02 '20 at 15:23
  • @NinaScholz yeah i dont really know how to write the code to fullfill the needs. – Olaf Gorzkowski Nov 02 '20 at 15:29
  • If splice is your problem, then you could do it from first principles. Randomly move one of the 9 items to the front of the list (i.e.: swap it with whatever is at the first position), then randomly move one of the last 8 elements to the second position. The first two numbers in the array will be your two chosen numbers. – Wyck Nov 02 '20 at 15:39
  • It's not clear if your "first execution of the code" and "second time" simply refer to the two numbers you need to choose, or if we need to select two, and then two more, and *none* of these four numbers can match. – Scott Sauyet Nov 02 '20 at 16:15

3 Answers3

0

You could take the indeces and loop until you get a different index for getting the value from the array.

function makeRandom(list) {
    function getRandomIndex() {
        return Math.floor(Math.random() * list.length);
    }
    
    let index1 = getRandomIndex(),
        index2 = getRandomIndex();

    while (index1 === index2) index2 = getRandomIndex();

    return list[index1] + '+' + list[index2];
}

console.log(makeRandom([1, 2, 3, 4, 5, 6, 7, 8, 9]));

Approach with excluding some indices

function makeRandom(list, exclude = []) {
    function getRandomIndex() {
        return Math.floor(Math.random() * list.length);
    }
    function getFreeIndex() {
        let index;

        if (exclude.length >= list.length) return;

        do index = getRandomIndex();
        while (exclude.includes(index))

        exclude.push(index);
        return index;
    }
    
    return getFreeIndex() + '+' + getFreeIndex();
}

console.log(makeRandom([1, 2, 3, 4, 5, 6, 7, 8, 9], [2, 3]));
Nina Scholz
  • 376,160
  • 25
  • 347
  • 392
  • This should be fine for 2 out of 9. But I wouldn't want to do it in the general case. It's going to take longer and longer to find unused values. – Scott Sauyet Nov 02 '20 at 15:40
  • I always enjoy your brilliance. I _think_ part of the requirement was to avoid dup operators. You'll want to remove the used elements from the original list while operating. – Randy Casburn Nov 02 '20 at 15:40
  • @RandyCasburn: I couldn't tell if that was a requirement or not. The phrasing was ambiguous and might have meant only to reiterate that we don't duplicate them in the pair chosen. My answer, like this one makes the opposite assumption. – Scott Sauyet Nov 02 '20 at 15:42
  • @ScottSauyet - agreed about ambiguity - oh, wait - then I saw your answer! LOL! – Randy Casburn Nov 02 '20 at 15:43
  • what does the + operator before each return value mean? – Olaf Gorzkowski Nov 02 '20 at 15:57
  • it concats the string. – Nina Scholz Nov 02 '20 at 15:58
  • @NinaScholz but how would i go about to check if index1 or index2 have been selected before hand? like if index1 = 5 (first execution) and index1 = 5 (second execution) how can i prevent that from happening? – Olaf Gorzkowski Nov 02 '20 at 16:07
0

Updated Answer

An edit to the questions made clear some additional requirements, and that means the the original solution (below) doesn't do what's requested. This version seems to be useful.

We introduce a function that will randomly choose from a fixed array, but at no time pick any of the n-most recently used items. So if we call this with n of 4 and arr of [1, 2, 3, 4, 5, 6, 7, 8, 9], we will get back a function that will randomly select one of the values, and on the next call will randomly select another, and on the third call still another, and the fourth yet one more. There will be no duplicates among these four. The fifth one might be the same as the first or could be any of the other values not in that list. And the sixth call might be the same as the second (or if it's not been reused in the fifth round, the same value as the first), and so on.

const noRepeatRandom = (n, arr) => {
  // TODO: error if n >= arr.length
  let available = [...arr]
  let used = []
  return () => {
    const nextIdx = Math .floor (Math .random() * available .length)
    const next = available [nextIdx]
    used .push (next)
    available .splice (nextIdx, 1)
    if (used .length >= n) {
      available .push (used .shift())
    }
    return next
  }  
}
const myRandom = noRepeatRandom (4, [1, 2, 3,4, 5, 6, 7, 8, 9])

// display the results of a bunch of calls to `myRandom()`
console .log (...Array.from({length: 30}, myRandom))

We keep track of two lists: the ones not recently used and the ones we can't reuse right now (available and used.) Initially we just fill up used, but once it reaches n, we start taking the oldest element off the used list and adding it to available.

Note that there should be error checking for n and the length of the array. If n >= arr.length, this should just fail. I'll leave that as an exercise. Also note that if n == arr.length - 1, you will get an endlessly repeating cycle of the same arr.length items, as each time there will only be one option to choose from.


Original Answer

This version does not match the clarified requirements. It's still a useful technique, but it doesn't solve the problem.

I would do a partial shuffling of the array. The code below is a recursive version of the Fisher-Yates shuffle, modified to stop after n elements are chosen. It does not modify your input array.

We then build pickTwo on top of this by partially applying the 2 to our (curried) function.

const partialShuffle = (n) => (xs, i = Math.floor (Math .random () * xs .length)) =>
  n <= 0 || n > xs .length || xs .length == 0
    ? []
    : [xs[i], ... partialShuffle (n - 1) ([... xs .slice (0, i), ... xs .slice (i + 1)])]

const pickTwo = partialShuffle (2)

const arr = [1, 2, 3, 4, 5, 6, 7, 8, 9]

for (let i = 0; i < 5; i ++) {
  console.log(...pickTwo(arr))
}

console.log('Original not modified:', ...arr)
.as-console-wrapper {max-height: 100% !important; top: 0}
Scott Sauyet
  • 49,207
  • 4
  • 49
  • 103
0

Assumptions

  1. You have an array of numbers as input

  2. Every call to makeRandom should randomly select two numbers from your array and output the sum

  3. Every subsequent call to makeRandom should not use any number (id) already used

     list = [1,1,2,3,4,5];
     // The number 1 could be used twice (but only in different calls to makeRandom)
     // The numbers 2,3,4,5 can only be used once
    
     list = [1,1];
     // Shouldn't work because 1 == 1
    

JS Code

var list    = [1,2,3,4,5,6,7,8,9];    // Your seed list
var usable  = list.slice(0);          // Copy of your seed list {which we will alter with makeRandom}
function makeRandom(){
    // Check it's possible...
    let counts = {};                                       // Create object to count unique numbers
    for (var i = 0; i < usable.length; i++) {
        counts[usable[i]] = 1 + (counts[usable[i]] || 0);  // Iterate array and fill counts
    }
    if(Object.keys(counts).length < 2){                    // Check there are at least two unique numbers
        console.log("List is too short!");                 // Log error if <2
        return false;                                      // Exit function if <2
    }


    // Get random numbers and output sum...
    let id = Math.floor(Math.random() * usable.length)   // Randomly select an id from usable numbers
    let a  = usable[id];                                 // Set first number
    usable.splice(id, 1);                                // Remove 1st number from usable numbers
    
    let b;
    while(true){                                         // Loop until numbers are different
        id = Math.floor(Math.random() * usable.length);  // Randomly select an id from usable numbers
        b  = usable[id];                                 // Set second number
        if(a !== b)break;                                // Check 1st number isn't the same as the second number
    }
    usable.splice(id, 1);                                // Remove 2nd number from usable numbers
//  console.log(a + " + " + b + " = " + (a+b));          // Log for debugging if required
    return a+b;                                          // Return sum of 1st and 2nd numbers
}

Note: I've typed out the while loop in full for ease of understanding; it could be shortened using do...while(...):

    let b;
    do b = list[Math.floor(Math.random() * list.length)];  // Set second number
    while(a==b)                                            // Re-set second number if a == b OR if b was used in the last call to makeRandom

Example

var list    = [1,2,3,4,5,6,7,8,9];
var usable  = list.slice(0);
function makeRandom(){
    let counts = {};
    for (var i = 0; i < usable.length; i++) {
        counts[usable[i]] = 1 + (counts[usable[i]] || 0);
    }
    if(Object.keys(counts).length < 2){
        console.log("List is too short!");
        return false;
    }
    let id = Math.floor(Math.random() * usable.length)
    let a  = usable[id];
    usable.splice(id, 1);
    let b;
    while(true){
        id = Math.floor(Math.random() * usable.length);
        b  = usable[id];
        if(a !== b)break;
    }
    usable.splice(id, 1);
    console.log(a + " + " + b + " = " + (a+b));
    return a+b;
}
// Make several calls to function
makeRandom();
makeRandom();
makeRandom();
makeRandom();
makeRandom();
makeRandom();

// Change the seed lists
var list    = [1,1,1,1,1,9];
var usable  = list.slice(0);

// Make several calls to function
makeRandom();
makeRandom();
makeRandom();

Example Output

__Example 1__
list = [1,2,3,4,5,6,7,8,9]; // Example list used

                // console.log           return
> makeRandom(); // 4 + 2 = 6             6
> makeRandom(); // 1 + 8 = 9             9
> makeRandom(); // 9 + 3 = 12            12
> makeRandom(); // 6 + 7 = 13            13
> makeRandom(); // List is too short!    false
> makeRandom(); // List is too short!    false

__Example 2__
list = [1,1,1,1,1,9]; // Example list used

                // console.log           return
> makeRandom(); // 1 + 9 = 10            10
> makeRandom(); // List is too short!    false
> makeRandom(); // List is too short!    false

Alternative

Having had a look at the updates to your question...

I'm not clear on whether the numbers can only be used once or if they just can't be used back to back.

list = [1,2,3,4,5];
                 // Choose from      // Chosen
> makeRandom();  // 1, 2, 3, 4, 5    // 1, 4
> makeRandom();  // 2, 3, 5          // 2, 3
> makeRandom();  // 1, 4, 5          // 1, 5

If that's the case then the following is likely more useful

var list = [1,2,3,4,5,6,7,8,9];    // Your seed list
var used = [];           // Last used pair of numbers
function makeRandom(){
    // Check it's possible...
    let counts = {};                                       // Create object to count unique numbers
    for (var i = 0; i < list.length; i++) {
        counts[list[i]] = 1 + (counts[list[i]] || 0);  // Iterate array and fill counts
    }
    if(Object.keys(counts).length < 4){                    // Check there are at least four unique numbers: any less and we'll end up in an infinite loop on the second call of makeRandom
        console.log("List is too short!");                 // Log error if <2
        return false;                                      // Exit function if <2
    }


    // Get random numbers and output sum...
    let a;
    do a = list[Math.floor(Math.random() * list.length)];  // Set first number
    while(used.includes(a))                                // Reset first number if a was used in the last call to makeRandom
    let b;
    do b = list[Math.floor(Math.random() * list.length)];  // Set second number
    while(a==b || used.includes(b))                        // Re-set second number if a == b OR if b was used in the last call to makeRandom

    used = [a, b];                                         // Set last used numbers
    console.log(a + " + " + b + " = " + (a+b));            // Log for debugging if required
    return a+b;                                            // Return sum of 1st and 2nd numbers
}

// Make several calls to function
makeRandom();
makeRandom();
makeRandom();
makeRandom();
makeRandom();
makeRandom();

// Change the seed lists
var list    = [1,2,3,4];

// Make several calls to function
// Notice with only 4 numbers once the first pair is selected (e.g. 1 & 3) the pattern cycles 1,3 -> 2,4 -> 1,3 -> 2,4
makeRandom();
makeRandom();
makeRandom();
Steven
  • 6,053
  • 2
  • 16
  • 28