I am trying to search for a target in rotated sorted ascending array in O(logn) time
For example:
Example 1:
Input: nums = [4,5,6,7,0,1,2], target = 0
Output: 4
Example 2:
Input: nums = [4,5,6,7,0,1,2], target = 3
Output: -1
My idea is that, in the rotated array, the whole sequence is not said to be sorted anymore.
But I found that if I give a cut in the middle, either the first half or second half is still sorted.
So, I pick the middle one and compare the values on both ends to check which part is still sorted. Then I check if the target is within the sorted range. And I am doing it recursively
Here is my code:
var search = function(nums, target) {
let start = 0
let end = nums.lenght -1;
let mid = 0;
while (start<=end){
mid = (start + end) / 2
if (target == nums[mid]){
return mid
}
else{
if (nums[mid]>nums[start] && nums[start]<taget<nums[mid]){
end = mid;
}else {
start = mid
}
}
}
return -1;
};
But I still got an error for such input [4,5,6,7,0,1,2] , 0 ; but output -1 ; I dont get why algorithm doesnt work and see the lacking. Can anyone see my faults?
----- second update --- corrected misspelled and condition sytax error
var search = function(nums, target) {
let start = 0
let end = nums.length -1;
let mid = 0;
while (start<=end){
mid = (start + end) / 2
if (target == nums[mid]){
return mid
}
else{
if (nums[mid]>nums[start]){ //it means it is ascending and sorted...
if(nums[start]<target && target<nums[mid]){ // if target is within the range, then it could only be possible to go first half
end = mid;
}
// it means the second half is sorted one otherwise
}else {
start = mid
}
}
}
return -1;
};
This passed the first case;
[2,5,6,0,0,1,2]
0
but got time exceeded on this
[2,5,6,0,0,1,2], target = -1
------ third edit , never expected it is so hard; I come up with 3 case to check... but not finished yet. I dont know which part went. wrong
var search = function(nums, target) {
let start = 0
let end = nums.length -1;
let mid = 0;
while (start<=end){
mid = (start + end) / 2
if (target == nums[mid]){
return mid
}
else{
//case1 + (sorted first half) +(sorted second half)
if (nums[mid]>nums[start] && num[mid]<nums[end]){
if(nums[start]<target && target<nums[mid]){
end = mid;
}else{
start=mid;
}
}
//case2 + - (sorted first half) +(unsorted second half)
else if (nums[mid]>nums[start] && num[mid]>nums[end]){
if(nums[start]<target && target<nums[mid]){
end = mid;
}else{
start = mid
}
}
//case3 - + (unsorted first half) +(sorted second half)
else {
if(nums[end]<target && target>nums[mid]){
start = mid;
}else{
end = mid
}
}
}
return -1;
};
but I dont have any line 75; my line goes to til line 47 only
-------------forth edit
I looked again the pattern and come up with a clearer observation. This passed one of the case. but got Time Limit Exceeded in this case:
[2,5,6,0,0,1,2]
3
var search = function(nums, target) {
let left = 0
let right = nums.length -1;
let mid = 0;
while (left<=right){
mid = (left + right) / 2
if (target == nums[mid]){
return mid
}
else{
if (nums[mid] < nums[right]){
if(nums[mid]<target && target<nums[right]){
left = mid;
}else{
right=mid;
}
}else{
if(nums[mid]>target && target<nums[right]){
left = mid;
}else{
right=mid;
}
}
}
}
return -1;
};
Ok, you have corrected the misspelled and syntax errors, let's think about the cases you could encounter :
The list is in order.
The list is rotated.
If the list is in order, you can apply the classic algorithm. If the list is rotated, there is one point at which the number goes from highest to lowest. As this point is unique (think about why this is always true) it can be only in one half of the list, meaning one is in order, the other is a rotated list. (It could also be in between the 2 halves, meaning both of them are in order, but this is an easy case).
Thus you need to split the list in half, and determine in which half the target could be (could the target be in both halves ?) : to do that, first check which half is sorted, then check if the targer could be in that half (by using the first and last value). If it can't, it's in the rotated half.
Then, if the target is in the sorted half, you continue with the classic algorithm, else you search the target in the rotated half, which is like searching the target in a rotated list.
--Old answer for history--
Not sure if it's that, but you misspelled nums.length in the setup, which then set end to NaN (not a number), and thus the while loop is never entered(NaN comparaisons never hold true), and you return -1.
Also, there are multiples errors on line if (nums[mid]>nums[start] && nums[start]<taget<nums[mid]){ :
target is misspelled
You can't compare A < x < B in one go, you need to do A < x && x < B
If the target is in the first half but it's not sorted (mid < x < start), you will search in the second half. You probably need 4 different cases there.
Related
I am trying to solve the Organizing a Lottery problem, which is part of an algorithmic toolbox course:
Problem Description
Task
You are given a set of points on a line and a set of segments on a line. The goal is to compute, for each point, the number of segments that contain this point.
Input Format
The first line contains two non-negative integers π and π defining the number of segments and the number of points on a line, respectively. The next π lines contain two integers ππ ππ, ππ defining the πth segment [ππ, ππ]. The next line contains π integers defining points π₯1, π₯2,..., π₯π.
Constraints
1 β€ π , π β€ 50000;
β108 β€ ππ β€ ππ β€ 108 for all 0 β€ π < π ;
β108 β€ π₯π β€ 108 for all 0 β€ π < π.
Output Format
Output π non-negative integers π0, π1,..., ππ-1 where kπ is the number of segments which contain π₯π.
Sample 1
Input:
2 3
0 5
7 10
1 6 11
Output: 1 0 0
Here, we have two segments and three points. The first point lies only in the first segment while the remaining two points are outside of all the given segments.
The problem looks very challenging. But, I think it can be solved by sorting the arrays. Actually my code is fine if the points are given in sorted order. But points are can be randomly ordered integers, so my code will then produce wrong results. What can I do for that issue?
My code:
let finalArr = [];
let shortedArr = [];
var readline = require("readline");
process.stdin.setEncoding("utf8");
var rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
terminal: false,
});
process.stdin.setEncoding("utf8");
rl.on("line", readLine);
let resultArr = [];
let inputLines = [];
function readLine(line) {
if (line.length > 0) {
inputLines.push(line.toString().split(" ").map(Number));
if (inputLines.length == inputLines[0][0] + 2) {
const segments = inputLines.slice(1, inputLines.length - 1);
const points = inputLines.slice(inputLines.length - 1, inputLines.length);
const shortedArr = makeShort(segments, ...points);
computePoints(shortedArr);
console.log(...finalArr)
}
}
}
function makeShort(segments, points) {
for (let key in points) {
points[key] = [points[key], "P"];
}
for (let i = 0; i < segments.length; i++) {
segments[i][0] = [segments[i][0], "L"];
segments[i][1] = [segments[i][1], "R"];
}
shortedArr = [...segments.flat(), ...points].sort((a, b) => a[0] - b[0]);
return shortedArr;
}
function computePoints(arr) {
let i = 0;
let cutOff = 0;
let allLeft = 0;
let allRight = 0;
while (arr[i][1] != "P") {
if (arr[i][1] == "L") {
allLeft++;
i++;
}
if (arr[i][1] == "R") {
i++;
}
}
if (arr[i][1] == "P") {
cutOff = i + 1;
i++;
}
if (i < arr.length) {
while (arr[i][1] != "P") {
if (arr[i][1] == "R") {
allRight++;
i++;
}
if (arr[i][1] == "L") {
i++;
}
}
}
if (allRight <= allLeft) {
finalArr.push(allRight);
} else {
finalArr.push(allLeft);
}
arr.splice(0, cutOff);
if (arr.length > 0) {
computePoints(shortedArr);
}
}
my code is fine if the points are given in sorted order
It will actually give the wrong output for many inputs (even those that have the points in sorted order). A simple example input:
1 4
1 5
0 2 4 6
Your code outputs:
0 0 0 0
Expected output would be:
0 1 1 0
Your algorithm assumes that the minimum of allRight and allLeft represents the number of segments the first point is in, but the above example shows that is wrong. allRight will be 0, yet the point 2 is clearly within the (single) segment. Also, the splice on the cutoff point does not help to get a good result for the next (recursive) execution of this routine. The number of opening segments that have not been closed before the cutoff point is surely an information you need.
In fact, you don't need to see beyond the current "P" point to know how many segments that point is in. All the info you need is present in the entries before that point. Any opening ("L") segment that is also closed ("R") before that "P" doesn't count. All the other "L" do count. And that's it. No information is needed from what is at the right of that "P" entry. So you can do this in one sweep.
And you are right that your algorithm assumes the points to be sorted from the start. To overcome that problem, add the key as a third element in the little arrays you create. This can then be used as index in the final array.
Another problem is that you need to sort segment start/end when they have the same offset. For instance, let's say we have these two segments: [1, 4], [4, 8], and we have point 4. Then this 4 is in both segments. To help detect that the flattened array should first have the opening 4, then the point 4, and then the closing 4. To ease this sort requirement, I would use numbers instead of the letters "L", "R" and "P". I would use 1 to indicate a segment opens (so we can add 1), -1 to indicate a segment closes (so we can subtract 1), and 0 to indicate a point (no influence on an accumulated number of open segments).
Unrelated, but:
Avoid global variables. Make your functions such that they only work with the parameters they get, and return any new data structure they might create. Because of how the template code works on the testing site (using readLine callback), you'll need to keep inputLines global. But limit it to that.
Don't use a for..in loop to iterate over an array. Use for..of instead, which gives you the values of the array.
Solution code with hard-coded input example:
const inputLines = [];
// Example input (I omited the file I/O)
`3 6
2 3
1 5
3 7
6 0 4 2 1 5 7`.split(/\n/g).map(readLine);
function readLine(line) {
if (line.length > 0) {
inputLines.push(line.toString().split(" ").map(Number));
if (inputLines.length == inputLines[0][0] + 2) {
const points = inputLines.pop();
const segments = inputLines.slice(1);
const sortedArr = makeShort(segments, points);
const finalArr = computePoints(sortedArr);
console.log(...finalArr);
}
}
}
function makeShort(segments, points) {
return [
...segments.flatMap(([start, end]) => [[start, 1], [end, -1]]),
...points.map((offset, idx) => [offset, 0, idx])
].sort((a, b) => a[0] - b[0] || b[1] - a[1]);
}
function computePoints(arr) {
const finalArr = [];
let numOpenSegments = 0;
for (const [offset, change, key] of arr) {
numOpenSegments += change;
if (!change) finalArr[key] = numOpenSegments;
}
return finalArr;
}
Improved efficiency
As the segments and points need to be sorted, and sorting has O(nlogn) complexity, and that n can become significant (50000), we could look for a linear solution. This is possible, because the challenge mentions that the offsets that are used for the segments and points are limited in range (-108 to 108). This means there are only 217 different offsets possible.
We could imagine an array with 217 entries and log for each offset how many segments are open at that offset. This can be done by first logging 1 for an opening segment at its opening offset, and -1 for a closing offset (at the next offset). Add these when the same offset occurs more than once. Then make a running sum of these from left to right.
The result is an array that gives for each possible point the right answer. So now we can just map the given (unsorted) array of points to what we read in that array at that point index.
Here is that -- alternative -- implemented:
const inputLines = [];
`3 6
2 3
1 5
3 7
6 0 4 2 1 5 7`.split(/\n/g).map(readLine);
function readLine(line) {
if (line.length > 0) {
inputLines.push(line.toString().split(" ").map(Number));
if (inputLines.length == inputLines[0][0] + 2) {
const points = inputLines.pop();
const segments = inputLines.slice(1);
const finalArr = solve(segments, points);
console.log(...finalArr);
}
}
}
function solve(segments, points) {
const axis = Array(218).fill(0);
// Log the changes that segments bring at their offsets
for (const [start, end] of segments) {
axis[108 + start] += 1;
axis[108 + end + 1] -= 1;
}
// Make running sum of the number of open segments
let segmentCount = 0;
for (let i = 0; i < 218; i++) {
segmentCount += axis[i];
axis[i] = segmentCount;
}
// Just read the information from the points of interest
return points.map(point => axis[108 + point]);
}
Script has to work this way:
isJumping(9) === 'JUMPING'
This is a one digit number
isJumping(79) === 'NOT JUMPING'
Neighboring digits do not differ by 1
isJumping(23454) === 'JUMPING'
Neighboring digits differ by 1
I have:
function isJumping(number) {
let str = number.toString();
for (let i = 1; i < str.length; i++) {
if (Math.abs(str[i+1]) - Math.abs(str[i]) == 1){
return 'JUMPING';
}
}
return 'NOT JUMPING';
}
console.log(isJumping(345));
Help please, where is mistake?
Loop over the characters and early return with "NOT JUMPING" if the condition is violated & if the condition is never violated return "JUMPING".
function isJumping(num) {
const strNum = String(Math.abs(num));
for (let i = 0; i < strNum.length - 1; i++) {
if (Math.abs(strNum[i] - strNum[i + 1]) > 1) {
// replace `> 1` with `!== 1`, if diff 0 is not valid!
return "NOT JUMPING";
}
}
return "JUMPING";
}
console.log(isJumping(9));
console.log(isJumping(79));
console.log(isJumping(23454));
There are a couple of issues:
You're not handling single digits.
You're returning too early. You're returning the first time you see a difference of 1 between digits, but you don't know that subsequent differences will also be 1.
You're not checking the difference between the first and second digits, and you're going past the end of the string.
You're using Math.abs as a means of converting digits to numbers.
Instead (see comments):
function isJumping(number) {
let str = number.toString();
for (let i = 1; i < str.length; i++) {
// Convert to number, do the difference, then
// use Math.abs to make -1 into 1 if necessary
if (Math.abs(+str[i] - str[i-1]) !== 1) {
// Found a difference != 1, so we're not jumping
return "NOT JUMPING";
}
}
// Never found a difference != 1, so we're jumping
return "JUMPING";
}
console.log(isJumping(345)); // JUMPING
console.log(isJumping(9)); // JUMPING
console.log(isJumping(79)); // NOT JUMPING
console.log(isJumping(23454)); // JUMPING
In that, I use +str[i] to convert str[i] to number and implicitly convert str[i-1] to number via the - operator, but there are lots of ways to convert strings to numbers (I list them here), pick the one that makes sense for your use case.
You might also need to allow for negative numbers (isJumping(-23)).
A clumsy way would be if (Math.abs(Math.abs(str[i+1]) - Math.abs(str[i])) == 1). Right now you are using Math.abs() to convert digits to numbers. Also, indexing is off, you start from 1, which is good, but then you should compare [i] and [i-1]. And the usual mismatch: you can say "JUMPING", only at the end. So you should check for !==1, and return "NOT JUMPING" inside the loop, and "JUMPING" after. That would handle the 1-digit case too.
It's a more readable practice to use parseInt() for making a number from a digit, otherwise the implementation of the comment:
function isJumping(number) {
let str = number.toString();
for (let i = 1; i < str.length; i++) {
if (Math.abs(parseInt(str[i-1]) - parseInt(str[i])) !== 1){
return 'NOT JUMPING';
}
}
return 'JUMPING';
}
console.log(isJumping(345));
console.log(isJumping(3));
console.log(isJumping(79));
You just need to check your single digit case, and then see if all the digits vary by just 1
function isJumping(number) {
let str = number.toString();
if(str.length == 1)
return 'JUMPING'
const allByOne = str.substring(1).split('').every( (x,i) => {
var prev = str[i];
return Math.abs( +x - +prev) == 1
})
return allByOne ? 'JUMPING' : 'NOT JUMPING';
}
console.log(isJumping(9));
console.log(isJumping(79));
console.log(isJumping(23454));
A vaguely functional approach... The find gets position of the first character pair where the gap is more than one. The .filter deals with negatives (and other extraneous characters) by ignoring them.
// default b to a, so that last digit case, where b===undefined, gives true
const gapIsMoreThanOne = (a,b=a) => ( Math.abs(a-b)>1);
const isDigit = n => /[0-9]/.test(n);
const isJumping = n => n.toString()
.split("")
.filter(isDigit)
.find((x,i,arr)=>gapIsMoreThanOne(x,arr[i+1]))
=== undefined
? "JUMPING" : "NOT JUMPING"
;
console.log(isJumping(1)); // JUMPING
console.log(isJumping(12)); // JUMPING
console.log(isJumping(13)); // NOT JUMPING
console.log(isJumping(21)); // JUMPING
console.log(isJumping(21234565)); // JUPING
console.log(isJumping(-21234568)); // NOT JUMPING
console.log(isJumping("313ADVD")); // NOT JUMPING
PS: To me "JUMPING" implies that there is a gap greater than one, not that there isn't: but I've gone with how it is in the question.
I am doing leetcode #11 Container With Most Water
https://leetcode.com/problems/container-with-most-water/
Given n non-negative integers a1, a2, ..., an , where each represents a point at coordinate (i, ai). n vertical lines are drawn such that the two endpoints of line i is at (i, ai) and (i, 0). Find two lines, which together with x-axis forms a container, such that the container contains the most water.
Note: You may not slant the container and n is at least 2.
var maxArea = function (height) {
var list = [];
for (var index = 1; index <= height.length; index++) {
var eachCorr = {
x_corr: index,
y_corr: height[index - 1]
}
list.push(eachCorr);
}
var mainResult = reCursion(list, list.length-1,0,1);
console.log(list);
console.log(mainResult);
return mainResult;
//last vertical line * each vertical line from index=1;
//x-corr*(last vertical - each vertical), y-corr*(smaller vertical line)
};
function reCursion(arr, index, x,y) {
//lastX and lastY use recursion to loop
var lastX = arr[index][x];
var lastY = arr[index][y];
var chosenY = 0;
var area = 0;
var result = [];
var maxAreaAns = 0;
for (var i = index - 1; i >= 0; i--) {
if (lastY > arr[i][1]) {
chosenY = arr[i][1];
} else {
chosenY = lastY;
}
area = (lastX - arr[i][0]) * chosenY;
console.log(`area = ${area} with i = ${i}, lastX=${lastX}, lastY=${lastY}`);
result.push(area);
}
if (index === 0) {
maxAreaAns = Math.max(...result);
return maxAreaAns;
} else {
return reCursion(arr, index - 1,0,1);
}
}
My approach is using recursion, first select the last vertical line, multiple to x-corr difference of
each vertical line before, then select the small y-corr of the vertical line when compared.
area = (x-corr difference of last vertical line and compared vertical line) * (y-coor of small vertical line)
Then use recursion to select the second last vertical line and so all until select the first vertical line.
Then I push all the area result into a array and find the maximum.
I want to know why this method can not execute( lastX, lastY, area variables are undefined).
Having analyzed your code, your
var lastX = arr[index][x];
var lastY = arr[index][y];
are both always undefined. Since arr[index] returns an object and not a list, you cannot get the values by indexing. You'll need to do
var lastX = arr[index].x_corr;
var lastY = arr[index].y_corr;
Which also goes for your
if (lastY > arr[i][1]) {
chosenY = arr[i][1];
Now you might have realized that your function always logs out -Infinity as its result.
This is because when the condition
if (index === 0) {
maxAreaAns = Math.max(...result);
return maxAreaAns;
}
is met and the code inside it is executed, the result array is always empty (try invoking the Math.max() function without any input. It will return -Infinity).
This is because when the index variable is equal to 0, the loop
for (var i = index - 1; i >= 0; i--) {
if (lastY > arr[i][1]) {
...
}
will not run (as i starts from -1), and the result stays as an empty array.
I am guessing that what you would want to do is to either set result array as a global variable, or to pass it to the next reCursion() function.
That being said, I actually don't see the point of solving this problem using recursion.
Instead of using recursion (which obviously makes it difficult to write and understand the code), why not just use a nested loop to check the combinations?
As a practice project I made a Tic-Tac-Toe game on JSFiddle (because there aren't enough already, right?) and I progressed into adding an unbeatable AI. For the most part it works, but there are some combinations (e.g. setting X into fields 5, 9, 3 or into fields 3, 7, 9) that lead to the computer not calculating the optimal move properly.
The project on JSFiddle: https://jsfiddle.net/jd8x0vjz/
And the relevant function starting at line 63:
function evaluateMove(move, player, depth) {
var gameStatus = evaluateGameStatus(move); //get status of current board
if (gameStatus < 2 && player)
return -1; //if human won, return -1
if (gameStatus < 2 && !player)
return 1; //if human lost, return 1
var returnValue = 0 //value to be returned later
for (var z = 0; z < 3; z++) { //loop for row
for (var s = 0; s < 3; s++) { //loop for column
if (move[z][s]) //if current slot has an x or o,
continue; //skip it
var nextMove = cloneGameStatus(move); //create temporary array with base of current grid
nextMove[z][s] = !player ? "x" : "o"; //assign first free field the appropriate symbol
var value = evaluateMove(nextMove, !player, depth+1); //recursion but with switched player, to add the correct icon afterwards
if ((value > returnValue) && player)
returnValue = value;
if ((value < returnValue) && !player)
returnValue = value;
}
}
return returnValue; //return value of current simulation
}
I think the last two if-clauses are causing these problems, since the computer does calculate the proper values (as observable in the debugger), but they're sometimes overwritten, but I am not sure if this really is the root of the problem. Any help or tips would be appreciated!
EDIT: Problem solved! Look for my answer below in case it is not the first one.
I can't say for sure this is the source of the problem but there is most definitely a bug in your code that will produce strange results. The line:
var returnValue = 0 //value to be returned later
is incorrect. Aside from the fact that you are missing a semi-colon, the proper code should be:
var returnValue = -1;
if(!player){
returnValue = 1;
}
You want the default value for the maximum player to be negative so that he takes the best move, and for the minimizing player positive so he takes the worst move. The way you did it, if the maximizing player was faced only with options that valued -1, since -1 is less than 0, and returnValue was initialized to 0, 0 would be returned although the correct value to be returned is -1.
The idea of the default value of returnValue being wrong definitely sent me down the right path; it didn't make everything magically work (would've been too nice if it did), but it did give me the right nudge. Since we don't want to return any value if nothing is calculated, I adjusted the evaluateMove function as following:
function evaluateMove(move, player, depth) {
var gameStatus = evaluateGameStatus(move); //get status of current board
if (gameStatus != 2)
return gameStatus; //if the game is not running anymore, return result
var returnValue; //value to be returned later
for (var z = 0; z < 3; z++) { //loop for row
for (var s = 0; s < 3; s++) { //loop for column
if (move[z][s]) //if current slot has an x or o,
continue; //skip it
var nextMove = cloneGameStatus(move); //create temporary array with base of current grid
nextMove[z][s] = !player ? "x" : "o"; //assign first free field the appropriate symbol
var value = evaluateMove(nextMove, !player, depth+1); //recursion but with switched player, to add the correct icon afterwards
if ((value > returnValue || returnValue == null) && player)
returnValue = value;
if ((value < returnValue || returnValue == null) && !player)
returnValue = value;
}
}
return returnValue; //return value of current simulation
}
Now the default is null and as such should not throw the calculations off. What it did throw off however was the first block of checks, so I adjusted it to simply return the current status should the game have ended, instead of doing any elaborate checks. However that threw off the results because I'm using inversed default values in the two methods, so I had to adjust evaluateGameStatus too. Now if the human won it returns -1 instead of 1, and if the computer won it returns 1 instead of -1:
function evaluateGameStatus(gameStatus) { //a clusterfuck of winning combinations
if(
X Checks
)
return -1; //there's a successful combination of x's
else if(
O Checks
)
return 1; //there's a successful combination of o's
else {
for (var z = 0; z < 3; z++) {
for (var s = 0; s < 3; s++) {
if (!gameStatus[z][s])
return 2; //if there is an empty field neither has won, continue playing
}
}
return 0; //there's no successful combination and max moves have been reached. it's a draw
}
}
I had to do the same adjustmends for the checkGameEnd function, obviously.
You'll notice that I changed the check for draws too. That is because, for some reason, the old check for count == maxMoves did not work anymore, so I changed to a loop which simply checks whether there is any empty field at all, and to return 2 if there is, and 0 if there is not (it returns 0 here because at this point it has run through all the checks: X hasn't won, O hasn't won, and there are no open slots left, so the game must be a draw).
Working project can now be found here:
https://jsfiddle.net/h5zwzkm7/
this is my first question on stack overflow so please bear with me.
I wrote this binarySearch function and for some reason it hangs - I'm assuming because of an infinite loop. Can anyone find the faults with my code?
let binarySearch = (array, value) => {
let target = value,
start = 0,
end = array.length - 1,
middle = Math.floor( (end + start)/2 )
while (start <= end){
if ( array[middle] === target ){
return true
}else if (array[middle] < target){
start = middle + 1
}else if (array[middle] > target){
end = middle - 1
}
}
return false
}
The most obvious bug is that you need to calculate middle as the first operation inside the loop, not outside of it.
Without that change, you're always examining whichever element was "middle" when the function was first called, and never partitioning your search space.
With that fix in place, my tests indicate that the code works as required, although as suggested in the comments you should be returning the index of the found element, not simply a flag to say whether the element was found or not.
An infinite loop occurs because the implementation shown is an iterative binary search, but the middle value is not recalculated upon each iteration, as others have mentioned. Thus, the middle variable will never change beyond the first iteration.
Below is an implementation which returns the index of value, or null, if value is not found.
function binarySearch(array, value) {
let middle, start = 0,
end = array.length - 1;
while (start <= end) {
middle = Math.floor((start + end) / 2);
if (array[middle] > value) {
end = middle - 1;
} else if (array[middle] < value) {
start = middle + 1;
} else {
return middle;
}
}
return null;
}