Reputation: 4480
Given an array with a minimum length of 3 and a maximum length of 5, which always contains uniquely occurring integers from 0 to 4 in ascending order, I need to pick out two non-consecutive numbers from it. Non-consecutive refers to their numeric value, not their position in the array.
To clarify, here are examples of valid arrays:
[ 1, 2, 3 ]
[ 0, 1, 2, 4 ]
[ 0, 3, 4 ]
For the arrays above, valid answers could be, respectively:
[ 1, 3 ]
[ 0, 2 ]
, [ 0, 4 ]
or [ 1, 4 ]
[ 0, 3 ]
or [ 0, 4 ]
Furthermore, in those cases where there is more than one valid answer, I need it to be selected at random, if at all possible (for instance I don't want to favor sequences that begin with the lowest number, which is what would occur if I always began checking from left to right and stopped checking as soon as I found one valid solution).
What would be the most efficient way of tackling this problem in Javascript?
Upvotes: 1
Views: 1149
Reputation: 576
since you state that the array ellemnts are all unique, and that they are sorted.
It should suffice to take an random element
var index1=Math.floor(Math.random()*arr.length)
now any other element (except maybe the elemnts on position (index1 +/- 1) are not consecutive So a new random element can be chosen excluding the first index.
var index2=Math.floor(Math.random()*arr.length);
if(index2==index1){
index2+=((index2<arr.length-1)?1:-1);
}
if(Math.abs(arr[index1]-arr[index2])<=1){
if(index2==0 && arr.length<4){
//set index2 to arr.length-1 and do check again, if not ok=> no result
if(!(arr[index1]-arr[arr.length-1]>=-1)){
return [arr[arr.length-1],arr[index1]];
}
}
else if(index2==arr.length-1 && arr.length<4){
//set index2 to 0 and do check again, if not ok=> no result
if(!(arr[index1]-arr[0]<=1)){
return [arr[0],arr[index1]];
}
}
else{
//if index2>index1 index2++
//else index2--
//always OK so no further check needed
index2+=(index2>index1?1:-1);
return [arr[index1],arr[index2]];
}
}
else{
//ok
return [arr[index1,arr[index2]];
}
return false;
if speed is not important, you can use a filter on the array to calculate a new array with all elements differing more then 1 unit of arr[index1]. and randomly select a new number from this new array.
Other attempt
function getNonConsecutive(arr){
var index1,index2,arr2;
index1=Math.floor(Math.random()*arr.length);
arr2=[].concat(arr);
arr2.splice((index1!==0?index1-1:index1),(index!==0?3:2));
if(arr2.length){
index2=Math.floor(Math.random()*arr2.length);
return [arr[index1],arr2[index2]];
}
else{
//original array has length 3 or less
arr2=[].concat(arr);
arr2.splice(index1),1);
for (var j=0,len=arr.length;j<len;j++){
if(Math.abs(arr1[index1]-arr2[j])>1){
return [arr[index1],arr2[j]];
}
}
}
return false
}
Upvotes: 0
Reputation: 386578
You could use two nested iterations and build an new array for choosing as random result.
function getNonConsecutives(array) {
return array.reduce((r, a, i, aa) => r.concat(aa.slice(i + 2).map(b => [a, b])), []);
}
console.log(getNonConsecutives([ 0, 1, 2, 4 ]));
.as-console-wrapper { max-height: 100% !important; top: 0; }
According to Bee157's answer, you could use a random choice with a constraint, like length for the first index and add the needed space for the second index.
The problem is, due to the nature of choosing the first number first, the distribution of the result is not equal.
function getNonConsecutives(array) {
var i = Math.floor(Math.random() * (array.length - 2));
return [
array[i],
array[Math.floor(Math.random() * (array.length - 2 - i)) + 2 + i]
];
}
console.log(getNonConsecutives([ 0, 1, 2, 4 ]));
Upvotes: 1
Reputation: 30082
Something like this should do it:
const pick = nums => {
// Pick a random number
const val = nums[Math.floor(Math.random() * nums.length) + 0];
// Filter out any numbers that are numerically consecutive
const pool = nums.filter(n => Math.abs(n - val) > 1);
// Pick another random number from the remainer
const other = pool[Math.floor(Math.random() * pool.length) + 0];
// Sort + return them
return [val, other].sort();
};
console.log(pick([0, 1, 2, 4]));
Upvotes: 0
Reputation: 122047
You can create a function using recursion that will pick random number in each iteration and loop all other elements and if condition is met add to array.
function findN(data) {
data = data.slice();
var r = []
function repeat(data) {
if (data.length < 2) return r;
var n = parseInt(Math.random() * data.length);
data.forEach(function(e, i) {
if (i != n) {
var a = data[n];
if (Math.abs(a - e) != 1 && r.length < 2) r.push(n < i ? [a, e] : [e, a])
}
})
data.splice(n, 1);
repeat(data)
return r;
}
return repeat(data)
}
console.log(findN([1, 2, 3]))
console.log(findN([0, 1, 2, 4]))
console.log(findN([0, 3, 4]))
Upvotes: 0
Reputation: 142
demoFn(array) {
var i,j, y =[];
for (i=0; i<=array.length;i++) {
for (j = i + 1; j <= array.length; j++) {
if (array[j] && array[i]) {
if (array[j] !== array[i] + 1) {
y.push([array[i], array[j]]);
}
}
}
}
}
Take a random array and check it.
Upvotes: 0