How to use Timsort in javascript? [closed] - javascript
As it currently stands, this question is not a good fit for our Q&A format. We expect answers to be supported by facts, references, or expertise, but this question will likely solicit debate, arguments, polling, or extended discussion. If you feel that this question can be improved and possibly reopened, visit the help center for guidance.
Closed 9 years ago.
How can I use Timsort in Javascript format? there are a lot of documentations in Java, Python and C++, is it doable in JS also?
Timsort Javascript
Array.prototype.timsort = function(comp){
var global_a=this
var MIN_MERGE = 32;
var MIN_GALLOP = 7
var runBase=[];
var runLen=[];
var stackSize = 0;
var compare = comp;
sort(this,0,this.length,compare);
/*
* The next two methods (which are package private and static) constitute the entire API of this class. Each of these methods
* obeys the contract of the public method with the same signature in java.util.Arrays.
*/
function sort (a, lo, hi, compare) {
if (typeof compare != "function") {
throw new Error("Compare is not a function.");
return;
}
stackSize = 0;
runBase=[];
runLen=[];
rangeCheck(a.length, lo, hi);
var nRemaining = hi - lo;
if (nRemaining < 2) return; // Arrays of size 0 and 1 are always sorted
// If array is small, do a "mini-TimSort" with no merges
if (nRemaining < MIN_MERGE) {
var initRunLen = countRunAndMakeAscending(a, lo, hi, compare);
binarySort(a, lo, hi, lo + initRunLen, compare);
return;
}
/**
* March over the array once, left to right, finding natural runs, extending short natural runs to minRun elements, and
* merging runs to maintain stack invariant.
*/
var ts = [];
var minRun = minRunLength(nRemaining);
do {
// Identify next run
var runLenVar = countRunAndMakeAscending(a, lo, hi, compare);
// If run is short, extend to min(minRun, nRemaining)
if (runLenVar < minRun) {
var force = nRemaining <= minRun ? nRemaining : minRun;
binarySort(a, lo, lo + force, lo + runLenVar, compare);
runLenVar = force;
}
// Push run onto pending-run stack, and maybe merge
pushRun(lo, runLenVar);
mergeCollapse();
// Advance to find next run
lo += runLenVar;
nRemaining -= runLenVar;
} while (nRemaining != 0);
// Merge all remaining runs to complete sort
mergeForceCollapse();
}
/**
* Sorts the specified portion of the specified array using a binary insertion sort. This is the best method for sorting small
* numbers of elements. It requires O(n log n) compares, but O(n^2) data movement (worst case).
*
* If the initial part of the specified range is already sorted, this method can take advantage of it: the method assumes that
* the elements from index {#code lo}, inclusive, to {#code start}, exclusive are already sorted.
*
* #param a the array in which a range is to be sorted
* #param lo the index of the first element in the range to be sorted
* #param hi the index after the last element in the range to be sorted
* #param start the index of the first element in the range that is not already known to be sorted (#code lo <= start <= hi}
* #param c comparator to used for the sort
*/
function binarySort (a, lo, hi, start, compare) {
if (start == lo) start++;
for (; start < hi; start++) {
var pivot = a[start];
// Set left (and right) to the index where a[start] (pivot) belongs
var left = lo;
var right = start;
/*
* Invariants: pivot >= all in [lo, left). pivot < all in [right, start).
*/
while (left < right) {
var mid = (left + right) >>> 1;
if (compare(pivot, a[mid]) < 0)
right = mid;
else
left = mid + 1;
}
/*
* The invariants still hold: pivot >= all in [lo, left) and pivot < all in [left, start), so pivot belongs at left. Note
* that if there are elements equal to pivot, left points to the first slot after them -- that's why this sort is stable.
* Slide elements over to make room to make room for pivot.
*/
var n = start - left; // The number of elements to move
// Switch is just an optimization for arraycopy in default case
switch (n) {
case 2:
a[left + 2] = a[left + 1];
case 1:
a[left + 1] = a[left];
break;
default:
arraycopy(a, left, a, left + 1, n);
}
a[left] = pivot;
}
}
/**
* Returns the length of the run beginning at the specified position in the specified array and reverses the run if it is
* descending (ensuring that the run will always be ascending when the method returns).
*
* A run is the longest ascending sequence with:
*
* a[lo] <= a[lo + 1] <= a[lo + 2] <= ...
*
* or the longest descending sequence with:
*
* a[lo] > a[lo + 1] > a[lo + 2] > ...
*
* For its intended use in a stable mergesort, the strictness of the definition of "descending" is needed so that the call can
* safely reverse a descending sequence without violating stability.
*
* #param a the array in which a run is to be counted and possibly reversed
* #param lo index of the first element in the run
* #param hi index after the last element that may be contained in the run. It is required that #code{lo < hi}.
* #param c the comparator to used for the sort
* #return the length of the run beginning at the specified position in the specified array
*/
function countRunAndMakeAscending (a, lo, hi, compare) {
var runHi = lo + 1;
// Find end of run, and reverse range if descending
if (compare(a[runHi++], a[lo]) < 0) { // Descending
while (runHi < hi && compare(a[runHi], a[runHi - 1]) < 0){
runHi++;
}
reverseRange(a, lo, runHi);
} else { // Ascending
while (runHi < hi && compare(a[runHi], a[runHi - 1]) >= 0){
runHi++;
}
}
return runHi - lo;
}
/**
* Reverse the specified range of the specified array.
*
* #param a the array in which a range is to be reversed
* #param lo the index of the first element in the range to be reversed
* #param hi the index after the last element in the range to be reversed
*/
function /*private static void*/ reverseRange (/*Object[]*/ a, /*int*/ lo, /*int*/ hi) {
hi--;
while (lo < hi) {
var t = a[lo];
a[lo++] = a[hi];
a[hi--] = t;
}
}
/**
* Returns the minimum acceptable run length for an array of the specified length. Natural runs shorter than this will be
* extended with {#link #binarySort}.
*
* Roughly speaking, the computation is:
*
* If n < MIN_MERGE, return n (it's too small to bother with fancy stuff). Else if n is an exact power of 2, return
* MIN_MERGE/2. Else return an int k, MIN_MERGE/2 <= k <= MIN_MERGE, such that n/k is close to, but strictly less than, an
* exact power of 2.
*
* For the rationale, see listsort.txt.
*
* #param n the length of the array to be sorted
* #return the length of the minimum run to be merged
*/
function /*private static int*/ minRunLength (/*int*/ n) {
//var v=0;
var r = 0; // Becomes 1 if any 1 bits are shifted off
/*while (n >= MIN_MERGE) { v++;
r |= (n & 1);
n >>= 1;
}*/
//console.log("minRunLength("+n+") "+v+" vueltas, result="+(n+r));
//return n + r;
return n + 1;
}
/**
* Pushes the specified run onto the pending-run stack.
*
* #param runBase index of the first element in the run
* #param runLen the number of elements in the run
*/
function pushRun (runBaseArg, runLenArg) {
//console.log("pushRun("+runBaseArg+","+runLenArg+")");
//this.runBase[stackSize] = runBase;
//runBase.push(runBaseArg);
runBase[stackSize] = runBaseArg;
//this.runLen[stackSize] = runLen;
//runLen.push(runLenArg);
runLen[stackSize] = runLenArg;
stackSize++;
}
/**
* Examines the stack of runs waiting to be merged and merges adjacent runs until the stack invariants are reestablished:
*
* 1. runLen[i - 3] > runLen[i - 2] + runLen[i - 1] 2. runLen[i - 2] > runLen[i - 1]
*
* This method is called each time a new run is pushed onto the stack, so the invariants are guaranteed to hold for i <
* stackSize upon entry to the method.
*/
function mergeCollapse () {
while (stackSize > 1) {
var n = stackSize - 2;
if (n > 0 && runLen[n - 1] <= runLen[n] + runLen[n + 1]) {
if (runLen[n - 1] < runLen[n + 1]) n--;
mergeAt(n);
} else if (runLen[n] <= runLen[n + 1]) {
mergeAt(n);
} else {
break; // Invariant is established
}
}
}
/**
* Merges all runs on the stack until only one remains. This method is called once, to complete the sort.
*/
function mergeForceCollapse () {
while (stackSize > 1) {
var n = stackSize - 2;
if (n > 0 && runLen[n - 1] < runLen[n + 1]) n--;
mergeAt(n);
}
}
/**
* Merges the two runs at stack indices i and i+1. Run i must be the penultimate or antepenultimate run on the stack. In other
* words, i must be equal to stackSize-2 or stackSize-3.
*
* #param i stack index of the first of the two runs to merge
*/
function mergeAt (i) {
var base1 = runBase[i];
var len1 = runLen[i];
var base2 = runBase[i + 1];
var len2 = runLen[i + 1];
/*
* Record the length of the combined runs; if i is the 3rd-last run now, also slide over the last run (which isn't involved
* in this merge). The current run (i+1) goes away in any case.
*/
//var stackSize = runLen.length;
runLen[i] = len1 + len2;
if (i == stackSize - 3) {
runBase[i + 1] = runBase[i + 2];
runLen[i + 1] = runLen[i + 2];
}
stackSize--;
/*
* Find where the first element of run2 goes in run1. Prior elements in run1 can be ignored (because they're already in
* place).
*/
var k = gallopRight(global_a[base2], global_a, base1, len1, 0, compare);
base1 += k;
len1 -= k;
if (len1 == 0) return;
/*
* Find where the last element of run1 goes in run2. Subsequent elements in run2 can be ignored (because they're already in
* place).
*/
len2 = gallopLeft(global_a[base1 + len1 - 1], global_a, base2, len2, len2 - 1, compare);
if (len2 == 0) return;
// Merge remaining runs, using tmp array with min(len1, len2) elements
if (len1 <= len2)
mergeLo(base1, len1, base2, len2);
else
mergeHi(base1, len1, base2, len2);
}
/**
* Locates the position at which to insert the specified key into the specified sorted range; if the range contains an element
* equal to key, returns the index of the leftmost equal element.
*
* #param key the key whose insertion point to search for
* #param a the array in which to search
* #param base the index of the first element in the range
* #param len the length of the range; must be > 0
* #param hint the index at which to begin the search, 0 <= hint < n. The closer hint is to the result, the faster this method
* will run.
* #param c the comparator used to order the range, and to search
* #return the int k, 0 <= k <= n such that a[b + k - 1] < key <= a[b + k], pretending that a[b - 1] is minus infinity and a[b
* + n] is infinity. In other words, key belongs at index b + k; or in other words, the first k elements of a should
* precede key, and the last n - k should follow it.
*/
function gallopLeft (key, a, base, len, hint, compare) {
var lastOfs = 0;
var ofs = 1;
if (compare(key, a[base + hint]) > 0) {
// Gallop right until a[base+hint+lastOfs] < key <= a[base+hint+ofs]
var maxOfs = len - hint;
while (ofs < maxOfs && compare(key, a[base + hint + ofs]) > 0) {
lastOfs = ofs;
ofs = (ofs << 1) + 1;
if (ofs <= 0) // int overflow
ofs = maxOfs;
}
if (ofs > maxOfs) ofs = maxOfs;
// Make offsets relative to base
lastOfs += hint;
ofs += hint;
} else { // key <= a[base + hint]
// Gallop left until a[base+hint-ofs] < key <= a[base+hint-lastOfs]
var maxOfs = hint + 1;
while (ofs < maxOfs && compare(key, a[base + hint - ofs]) <= 0) {
lastOfs = ofs;
ofs = (ofs << 1) + 1;
if (ofs <= 0) // int overflow
ofs = maxOfs;
}
if (ofs > maxOfs) ofs = maxOfs;
// Make offsets relative to base
var tmp = lastOfs;
lastOfs = hint - ofs;
ofs = hint - tmp;
}
/*
* Now a[base+lastOfs] < key <= a[base+ofs], so key belongs somewhere to the right of lastOfs but no farther right than ofs.
* Do a binary search, with invariant a[base + lastOfs - 1] < key <= a[base + ofs].
*/
lastOfs++;
while (lastOfs < ofs) {
var m = lastOfs + ((ofs - lastOfs) >>> 1);
if (compare(key, a[base + m]) > 0)
lastOfs = m + 1; // a[base + m] < key
else
ofs = m; // key <= a[base + m]
}
return ofs;
}
/**
* Like gallopLeft, except that if the range contains an element equal to key, gallopRight returns the index after the
* rightmost equal element.
*
* #param key the key whose insertion point to search for
* #param a the array [] in which to search
* #param base the index of the first element in the range
* #param len the length of the range; must be > 0
* #param hint the index at which to begin the search, 0 <= hint < n. The closer hint is to the result, the faster this method
* will run.
* #param c the comparator used to order the range, and to search
* #return the int k, 0 <= k <= n such that a[b + k - 1] <= key < a[b + k]
*/
function gallopRight (key, a, base, len, hint, compare) {
var ofs = 1;
var lastOfs = 0;
if (compare(key, a[base + hint]) < 0) {
// Gallop left until a[b+hint - ofs] <= key < a[b+hint - lastOfs]
var maxOfs = hint + 1;
while (ofs < maxOfs && compare(key, a[base + hint - ofs]) < 0) {
lastOfs = ofs;
ofs = (ofs << 1) + 1;
if (ofs <= 0) // int overflow
ofs = maxOfs;
}
if (ofs > maxOfs) ofs = maxOfs;
// Make offsets relative to b
var tmp = lastOfs;
lastOfs = hint - ofs;
ofs = hint - tmp;
} else { // a[b + hint] <= key
// Gallop right until a[b+hint + lastOfs] <= key < a[b+hint + ofs]
var maxOfs = len - hint;
while (ofs < maxOfs && compare(key, a[base + hint + ofs]) >= 0) {
lastOfs = ofs;
ofs = (ofs << 1) + 1;
if (ofs <= 0) // int overflow
ofs = maxOfs;
}
if (ofs > maxOfs) ofs = maxOfs;
// Make offsets relative to b
lastOfs += hint;
ofs += hint;
}
/*
* Now a[b + lastOfs] <= key < a[b + ofs], so key belongs somewhere to the right of lastOfs but no farther right than ofs.
* Do a binary search, with invariant a[b + lastOfs - 1] <= key < a[b + ofs].
*/
lastOfs++;
while (lastOfs < ofs) {
var m = lastOfs + ((ofs - lastOfs) >>> 1);
if (compare(key, a[base + m]) < 0)
ofs = m; // key < a[b + m]
else
lastOfs = m + 1; // a[b + m] <= key
}
return ofs;
}
/**
* Merges two adjacent runs in place, in a stable fashion. The first element of the first run must be greater than the first
* element of the second run (a[base1] > a[base2]), and the last element of the first run (a[base1 + len1-1]) must be greater
* than all elements of the second run.
*
* For performance, this method should be called only when len1 <= len2; its twin, mergeHi should be called if len1 >= len2.
* (Either method may be called if len1 == len2.)
*
* #param base1 index of first element in first run to be merged
* #param len1 length of first run to be merged (must be > 0)
* #param base2 index of first element in second run to be merged (must be aBase + aLen)
* #param len2 length of second run to be merged (must be > 0)
*/
function mergeLo (base1, len1, base2, len2) {
// Copy first run into temp array
var a = global_a;// For performance
var tmp=a.slice(base1,base1+len1);
var cursor1 = 0; // Indexes into tmp array
var cursor2 = base2; // Indexes int a
var dest = base1; // Indexes int a
// Move first element of second run and deal with degenerate cases
a[dest++] = a[cursor2++];
if (--len2 == 0) {
arraycopy(tmp, cursor1, a, dest, len1);
return;
}
if (len1 == 1) {
arraycopy(a, cursor2, a, dest, len2);
a[dest + len2] = tmp[cursor1]; // Last elt of run 1 to end of merge
return;
}
var c = compare;// Use local variable for performance
var minGallop = MIN_GALLOP; // " " " " "
outer:
while (true) {
var count1 = 0; // Number of times in a row that first run won
var count2 = 0; // Number of times in a row that second run won
/*
* Do the straightforward thing until (if ever) one run starts winning consistently.
*/
do {
if (compare(a[cursor2], tmp[cursor1]) < 0) {
a[dest++] = a[cursor2++];
count2++;
count1 = 0;
if (--len2 == 0) break outer;
} else {
a[dest++] = tmp[cursor1++];
count1++;
count2 = 0;
if (--len1 == 1) break outer;
}
} while ((count1 | count2) < minGallop);
/*
* One run is winning so consistently that galloping may be a huge win. So try that, and continue galloping until (if
* ever) neither run appears to be winning consistently anymore.
*/
do {
count1 = gallopRight(a[cursor2], tmp, cursor1, len1, 0, c);
if (count1 != 0) {
arraycopy(tmp, cursor1, a, dest, count1);
dest += count1;
cursor1 += count1;
len1 -= count1;
if (len1 <= 1) // len1 == 1 || len1 == 0
break outer;
}
a[dest++] = a[cursor2++];
if (--len2 == 0) break outer;
count2 = gallopLeft(tmp[cursor1], a, cursor2, len2, 0, c);
if (count2 != 0) {
arraycopy(a, cursor2, a, dest, count2);
dest += count2;
cursor2 += count2;
len2 -= count2;
if (len2 == 0) break outer;
}
a[dest++] = tmp[cursor1++];
if (--len1 == 1) break outer;
minGallop--;
} while (count1 >= MIN_GALLOP | count2 >= MIN_GALLOP);
if (minGallop < 0) minGallop = 0;
minGallop += 2; // Penalize for leaving gallop mode
} // End of "outer" loop
this.minGallop = minGallop < 1 ? 1 : minGallop; // Write back to field
if (len1 == 1) {
arraycopy(a, cursor2, a, dest, len2);
a[dest + len2] = tmp[cursor1]; // Last elt of run 1 to end of merge
} else if (len1 == 0) {
throw new Error("IllegalArgumentException. Comparison method violates its general contract!");
} else {
arraycopy(tmp, cursor1, a, dest, len1);
}
}
/**
* Like mergeLo, except that this method should be called only if len1 >= len2; mergeLo should be called if len1 <= len2.
* (Either method may be called if len1 == len2.)
*
* #param base1 index of first element in first run to be merged
* #param len1 length of first run to be merged (must be > 0)
* #param base2 index of first element in second run to be merged (must be aBase + aLen)
* #param len2 length of second run to be merged (must be > 0)
*/
function mergeHi ( base1, len1, base2, len2) {
// Copy second run into temp array
var a = global_a;// For performance
var tmp=a.slice(base2, base2+len2);
var cursor1 = base1 + len1 - 1; // Indexes into a
var cursor2 = len2 - 1; // Indexes into tmp array
var dest = base2 + len2 - 1; // Indexes into a
// Move last element of first run and deal with degenerate cases
a[dest--] = a[cursor1--];
if (--len1 == 0) {
arraycopy(tmp, 0, a, dest - (len2 - 1), len2);
return;
}
if (len2 == 1) {
dest -= len1;
cursor1 -= len1;
arraycopy(a, cursor1 + 1, a, dest + 1, len1);
a[dest] = tmp[cursor2];
return;
}
var c = compare;// Use local variable for performance
var minGallop = MIN_GALLOP; // " " " " "
outer:
while (true) {
var count1 = 0; // Number of times in a row that first run won
var count2 = 0; // Number of times in a row that second run won
/*
* Do the straightforward thing until (if ever) one run appears to win consistently.
*/
do {
if (compare(tmp[cursor2], a[cursor1]) < 0) {
a[dest--] = a[cursor1--];
count1++;
count2 = 0;
if (--len1 == 0) break outer;
} else {
a[dest--] = tmp[cursor2--];
count2++;
count1 = 0;
if (--len2 == 1) break outer;
}
} while ((count1 | count2) < minGallop);
/*
* One run is winning so consistently that galloping may be a huge win. So try that, and continue galloping until (if
* ever) neither run appears to be winning consistently anymore.
*/
do {
count1 = len1 - gallopRight(tmp[cursor2], a, base1, len1, len1 - 1, c);
if (count1 != 0) {
dest -= count1;
cursor1 -= count1;
len1 -= count1;
arraycopy(a, cursor1 + 1, a, dest + 1, count1);
if (len1 == 0) break outer;
}
a[dest--] = tmp[cursor2--];
if (--len2 == 1) break outer;
count2 = len2 - gallopLeft(a[cursor1], tmp, 0, len2, len2 - 1, c);
if (count2 != 0) {
dest -= count2;
cursor2 -= count2;
len2 -= count2;
arraycopy(tmp, cursor2 + 1, a, dest + 1, count2);
if (len2 <= 1) // len2 == 1 || len2 == 0
break outer;
}
a[dest--] = a[cursor1--];
if (--len1 == 0) break outer;
minGallop--;
} while (count1 >= MIN_GALLOP | count2 >= MIN_GALLOP);
if (minGallop < 0) minGallop = 0;
minGallop += 2; // Penalize for leaving gallop mode
} // End of "outer" loop
this.minGallop = minGallop < 1 ? 1 : minGallop; // Write back to field
if (len2 == 1) {
dest -= len1;
cursor1 -= len1;
arraycopy(a, cursor1 + 1, a, dest + 1, len1);
a[dest] = tmp[cursor2]; // Move first elt of run2 to front of merge
} else if (len2 == 0) {
throw new Error("IllegalArgumentException. Comparison method violates its general contract!");
} else {
arraycopy(tmp, 0, a, dest - (len2 - 1), len2);
}
}
/**
* Checks that fromIndex and toIndex are in range, and throws an appropriate exception if they aren't.
*
* #param arrayLen the length of the array
* #param fromIndex the index of the first element of the range
* #param toIndex the index after the last element of the range
* #throws IllegalArgumentException if fromIndex > toIndex
* #throws ArrayIndexOutOfBoundsException if fromIndex < 0 or toIndex > arrayLen
*/
function rangeCheck (arrayLen, fromIndex, toIndex) {
if (fromIndex > toIndex) throw new Error( "IllegalArgument fromIndex(" + fromIndex + ") > toIndex(" + toIndex + ")");
if (fromIndex < 0) throw new Error( "ArrayIndexOutOfBounds "+fromIndex);
if (toIndex > arrayLen) throw new Error( "ArrayIndexOutOfBounds "+toIndex);
}
}
// java System.arraycopy(Object src, int srcPos, Object dest, int destPos, int length)
function arraycopy(s,spos,d,dpos,len){
var a=s.slice(spos,spos+len);
while(len--){
d[dpos+len]=a[len];
}
}
Active Referencee
https://github.com/bellbind/stepbystep-timsort
https://github.com/Scipion/interesting-javascript-codes
Related
codewars kata: "Is my friend cheating?" Passed all test but using bruteforce, If someone could help me optimize my code by using actual math maybe?
Here is my solution export function removeNb (n:number):number[][] { const total = n * (n+1) / 2; for(let a = n; a > 1; a--){ for(let b = a-1 ;b > 1 ; b--){ if(total - b - a === b*a){ return [[b,a],[a,b]] } } } return [] } The problem is this: A friend of mine takes the sequence of all numbers from 1 to n (where n > 0). Within that sequence, he chooses two numbers, a and b. He says that the product of a and b should be equal to the sum of all numbers in the sequence, excluding a and b. Given a number n, could you tell me the numbers he excluded from the sequence? The function takes the parameter: n (n is always strictly greater than 0) and returns an array of arrays. The test cases are as follows: removeNb(26); //[[15,21],[21,15]] removeNb(101); //[[55,91],[91,55]] removeNb(102); //[[70,73],[73,70]] removeNb(110); //[[70,85],[85,70]]
Update As Ike mentioned the previous solution doesn't take in account the fact that a and b both need to be less or equal to n so the solution should be updated like this : (b needs to be less then n, ie (total - a) / (a + 1) <= n ie (total - n) / (n + 1) <= a) function removeNb(n: number) { const total = n * (n + 1) / 2; const t: [number, number][] = []; for (let a = Math.ceil((total - n) / (n + 1)); a * a < total; a++) { const b = (total - a) / (a + 1) if (!(b % 1)) t.push([a,b],...(a === b ? [] : [[b,a]])) } return t } With a bit of maths we can check that tatal - a - b = a * b is equivalent to (total - a) / (a + 1) = b so we can deduce b from a and just check if it is an integer. Also since a and b are exchangeable we can require b to be greater or equal a i.e (total - a) / (a + 1) >= a <=> total >= a * (a + 2) so we can require a² < total. So your function can be optimized this way: function removeNb(n: number) { const total = n * (n + 1) / 2; const t: [number, number][] = []; for (let a = 1; a * a < total; a++) { const b = (total - a) / (a + 1) if (!(b % 1)) t.push([a,b],...(a === b ? [] : [[b,a]])) } return t }
How to make the checking more robust and shorter?
I wrote this code, but I dont uderstand why it works this way, especially using the third and fourth examples as input. Why the 'middle' position remains so behind? -in the number 5 (or index 2) using the [1, 3, 5, 6] array and the number 7 as target?? And how to make it better?? I cant think of a shorter or better way to check the if/elses when the target value is not in the array, especially if the input is an array with only one value and the target to find is 0. Maybe a better way to check the possible different scenarios. Or how to better check the correct place of the target without so many if/elses. For example, is this code good enough to a coding interview? What can I do better? from LeetCode: Search Insert Position Given a sorted array of distinct integers and a target value, return the index if the target is found. If not, return the index where it would be if it were inserted in order. You must write an algorithm with O(log n) runtime complexity. Example 1: Input: nums = [1,3,5,6], target = 5 Output: 2 Example 2: Input: nums = [1,3,5,6], target = 2 Output: 1 Example 3: Input: nums = [1,3,5,6], target = 7 Output: 4 And especially this one: Example 4: Input: nums=[1], target= 0 Constraints: 1 <= nums.length <= 104 -104 <= nums[i] <= 104 nums contains distinct values sorted in ascending order. -104 <= target <= 104 this is my code: /** * #param {number[]} nums * #param {number} target * #return {number} */ var searchInsert = function(nums, target) { let left = 0; let right = nums.length -1; let middle; while(left <= right){ middle = nums.length>1 ? (Math.floor(left + (right - left)/2)) : 0; if(nums[middle] === target){ return middle; } else if(target < nums[middle]){ right = middle -1; } else { left = middle + 1; } } console.log(`Middle: ${middle}`); console.log(`Middle-1: ${nums[middle-1]}`); if(nums.lenght === 1){ return 0; } else { if((target < nums[middle] && target > nums[middle-1] )|| (target < nums[middle] && nums[middle-1] === undefined)){ /* No more items to the left ! */ return middle; } else if(target<nums[middle] && target<nums[middle-1]){ return middle-1; } else if(target > nums[middle] && target > nums[middle + 1]) { return middle + 2; /* Why the 'middle' is so behind here? using the THIRD example as input?? */ } else { return middle + 1; } } };
Problem The issue lies in the variable you are checking for after the while loop. In a "classical" binary search algorithm, reaching beyond the while loop would indicate the needle isn't present in the haystack. In case of this problem, though, we simply need to return right + 1 in this place in the code (rather than checking the middle). Your code adjusted for this: var searchInsert = function(nums, target) { let left = 0; let right = nums.length -1; let middle; while(left <= right){ middle = nums.length>1 ? (Math.floor(left + (right - left)/2)) : 0; if(nums[middle] === target){ return middle; } else if(target < nums[middle]){ right = middle -1; } else { left = middle + 1; } } return right + 1; }; console.log( searchInsert([1,3,5,6], 5), searchInsert([1,3,5,6], 2), searchInsert([1,3,5,6], 7), searchInsert([1], 0) ); Side note Also, the below is redundant... middle = nums.length>1 ? (Math.floor(left + (right - left)/2)) : 0; ...and can be shortened to: middle = Math.floor((left + right) / 2); Revised variant const searchInsertProblem = (arr, n) => { let start = 0; let end = arr.length - 1; while (start <= end) { const middle = Math.floor((start + end) / 2); if (arr[middle] === n) { return middle; } // on target if (arr[middle] > n) { end = middle - 1; } // overshoot else { start = middle + 1; } // undershoot } return end + 1; }; console.log( searchInsertProblem([1,3,5,6], 5), searchInsertProblem([1,3,5,6], 2), searchInsertProblem([1,3,5,6], 7), searchInsertProblem([1], 0) );
Simple function giving unexpected result
So I'm making a simple program to find the largest common divider (lcd) between two numbers, using the Euclidean Algorithm (http://www.math.jacobs-university.de/timorin/PM/continued_fractions.pdf, on the second page) it's very simple. So here's the function I've created: function largest_common_divider(num1, num2) { var remainder; var alpha = num1; var beta = num2; for (i = 0; i < 100; i++) { /* we want to find an "n" such that n * beta <= alpha < (n + 1) * beta I initiated n at 1 so that I could use n = 0 as the end condition for my loop */ for (n = 1; n <= 0; n++) { if (alpha > (n * beta)) { //still didn't find the n } else if (alpha == (n * beta)) { //Hurray! our beta is the lcd n = 0; return beta; } else { // figure out the remainder // and set n = 0 to terminate the loop remainder = alpha - (n - 1) * beta; n = 0; } } //If we didn't find our lcd, than the previous beta //become the new alpha (largest number) and the //the remainder becomes the new beta (smaller number) alpha = beta; beta = remainder; } } Note that, in this program I expect only positive integers and num1 > numb2 always. So, to test this function I made this: var a = 45; var b = 15; var bloopidy = largest_common_divider(a,b); console.log("The largest common divider between " + a + " and " + b + " is: " + bloopidy); expecting the result to be 15, but what I got is "undefined". I used all the little knowledge I have to debug this, but I couldn't! I need help guy, any is appreciated. Thank you!!
Your second for loop never runs: for (n = 1; n <= 0; n++) 1 is not <= 0, so the condition is not true to begin with, so the loop never starts. That means your code can be reduced to: function largest_common_divider(num1, num2) { var remainder; var alpha = num1; var beta = num2; for (i = 0; i < 100; i++) { alpha = beta; beta = remainder; } } This doesn't contain an explicit return, so it returns undefined implicitly.
Add or Subtract From 64bit Integer in Javascript
Is there a simple way (hoping for a small function, not a library) to add or subtract from a 64bit integer in JavaScript? Example 64bit int: 291270990346989568 Background: I am working with the Twitter API which has 64bit tweet ID's. I'd like to add or subtract one from those IDs to manipulate what results I get back from Twitter.
The following code does what you've described. Splitting, adding or subtracting, then re-joining... This code has both increment and decrement functions, and a test function with some edge cases. When splitting number strings and doing math on them, you need to consider what happens to "leading zeroes", so there's a padding function for that. Thanks, Justin, for providing a JSFiddle with this solution. /* * Prepend zeros to expand a given string to given length * * #var {String} numStr Number * #var {Number} len Length to pad out to * * #returns {String} */ function pad0 (numStr,len) { while (numStr.length < len) { numStr = "0" + numStr; } return numStr } /* * Decrement the given (64 bit) integer. * * #var {String} int64 Postive non-zero integer, as a string * * #returns {String} */ function decrInt64 (int64) { var result = ""; var midpt = Math.floor(int64.length/2); var upper = int64.substring(0,midpt); var lower = int64.substring(midpt); var upperVal = new Number(upper); var lowerVal = new Number(lower); if (lowerVal == 0) { if (upperVal == 0) { // We don't support negative numbers result = "*ERROR*" } else { // borrow 1 result = pad0((--upperVal).toString(),upper.length) + (new Number("1"+lower) - 1).toString(); } } else { var newLower = (lowerVal - 1).toString(); result = upper + pad0(newLower,lower.length); } alert(result); } /* * Increment the given (64 bit) integer. * * #var {String} int64 Postive, as a string * * #returns {String} */ function incrInt64 (int64) { var result = ""; var midpt = Math.floor(int64.length/2); var upper = int64.substring(0,midpt); var lower = int64.substring(midpt); var upperVal = new Number(upper); var lowerVal = new Number(lower); var newLower = (++lowerVal).toString(); // Did we overflow? if (lower.length < newLower.length) { // Yes, carry the 1 result = (++upperVal).toString() + newLower.substring(1); } else { result = upper + pad0(newLower,lower.length); } alert(result); } // Test function window.displaymessage= function () { decrInt64("291270990046989568"); incrInt64("291270990046989568"); decrInt64("000000000000000000"); incrInt64("000000000000000000"); decrInt64("000000001000000000"); incrInt64("000000001000000000"); decrInt64("099999999999999999"); incrInt64("099999999999999999"); decrInt64("999999999999999999"); incrInt64("999999999999999999"); }
Addition of integer-formatted strings (base 10) of indefinite lengths can be done by splitting the string into segments of 9 characters, calculating + or - for that and then moving to the preceding 9 characters. This is because the largest 9 character number, 999999999 is 32-bit safe (as is 1999999999, which I used in subtraction). The following code has 3 functions and the word integer is assumed to mean an integer-formatted string. addAsString, takes two non-negative integers x and y, returns x + y subtractAsString, takes two non-negative integers x and y, |x| >= |y|, returns x - y addORsub, takes any two integers x and y, returning x + y I've tried to explain what is happening via comments in the code // Indefinate length addition function addAsString(x, y) { // x, y strings var s = ''; if (y.length > x.length) { // always have x longer s = x; x = y; y = s; } s = (parseInt(x.slice(-9),10) + parseInt(y.slice(-9),10)).toString(); // add last 9 digits x = x.slice(0,-9); // cut off last 9 digits y = y.slice(0,-9); if (s.length > 9) { // if >= 10, add in the 1 if (x === '') return s; // special case (e.g. 9+9=18) x = addAsString(x, '1'); s = s.slice(1); } else if (x.length) { // if more recursions to go while (s.length < 9) { // make sure to pad with 0s s = '0' + s; } } if (y === '') return x + s; // if no more chars then done, return return addAsString(x, y) + s; // else recurse, next digit } // Indefinate length subtraction (x - y, |x| >= |y|) function subtractAsString(x, y) { var s; s = (parseInt('1'+x.slice(-9),10) - parseInt(y.slice(-9),10)).toString(); // subtract last 9 digits x = x.slice(0,-9); // cut off last 9 digits y = y.slice(0,-9); if (s.length === 10 || x === '') { // didn't need to go mod 1000000000 s = s.slice(1); } else { // went mod 1000000000, inc y if (y.length) { // only add if makes sense y = addAsString(y, '1'); } else { // else set y = '1'; } if (x.length) { while (s.length < 9) { // pad s s = '0' + s; } } } if (y === '') { // finished s = (x + s).replace(/^0+/,''); // dont return all 0s return s; } return subtractAsString(x, y) + s; } // Indefinate length addition or subtraction (via above) function addORsub(x, y) { var s = ''; x = x.replace(/^(-)?0+/,'$1').replace(/^-?$/,'0'); // -000001 = -1 y = y.replace(/^(-)?0+/,'$1').replace(/^-?$/,'0'); // -000000 = 0 if (x[0] === '-') { // x negative if (y[0] === '-') { // if y negative too return '-' + addAsString(x.slice(1), y.slice(1)); // return -(|x|+|y|) } return addORsub(y, x); // else swap } if (y[0] === '-') { // x positive, y negative s = y.slice(1); if (s.length < x.length || (s.length === x.length && s < x)) return subtractAsString(x, s) || '0'; // if |x|>|y|, return x-y if (s === x) return '0'; // equal then 0 s = subtractAsString(s, x); // else |x|<|y| s = (s && '-' + s) || '0'; return s; // return -(|y|-x) } return addAsString(x, y); // x, y positive, return x+y } Example usage (fiddle) var i = addORsub('291270990346989568', '1'); // add i === '291270990346989569'; i = addORsub('291270990346989568', '-1'); // subtract i === '291270990346989567';
any more optimisation I can do for this function?
I have a simple box blur function in a graphics library (for JavaScript/canvas, using ImageData) I'm writing. I've done a few optimisations to avoid piles of redundant code such as looping through [0..3] for the channels instead of copying the code, and having each surrounding pixel implemented with a single, uncopied line of code, averaging values at the end. Those were optimisations to cut down on redundant lines of code. Are there any further optimisations I can do of that kind, or, better still, any things I can change that may improve performance of the function itself? Running this function on a 200x150 image area, with a Core 2 Duo, takes about 450ms on Firefox 3.6, 45ms on Firefox 4 and about 55ms on Chromium 10. Various notes expressive.data.get returns an ImageData object expressive.data.put writes the contents of an ImageData back to a canvas an ImageData is an object with: unsigned long width unsigned long height Array data, a single-dimensional data in the format r, g, b, a, r, g, b, a ... The code expressive.boxBlur = function(canvas, x, y, w, h) { // averaging r, g, b, a for now var data = expressive.data.get(canvas, x, y, w, h); for (var i = 0; i < w; i++) for (var j = 0; j < h; j++) for (var k = 0; k < 4; k++) { var total = 0, values = 0, temp = 0; if (!(i == 0 && j == 0)) { temp = data.data[4 * w * (j - 1) + 4 * (i - 1) + k]; if (temp !== undefined) values++, total += temp; } if (!(i == w - 1 && j == 0)) { temp = data.data[4 * w * (j - 1) + 4 * (i + 1) + k]; if (temp !== undefined) values++, total += temp; } if (!(i == 0 && j == h - 1)) { temp = data.data[4 * w * (j + 1) + 4 * (i - 1) + k]; if (temp !== undefined) values++, total += temp; } if (!(i == w - 1 && j == h - 1)) { temp = data.data[4 * w * (j + 1) + 4 * (i + 1) + k]; if (temp !== undefined) values++, total += temp; } if (!(j == 0)) { temp = data.data[4 * w * (j - 1) + 4 * (i + 0) + k]; if (temp !== undefined) values++, total += temp; } if (!(j == h - 1)) { temp = data.data[4 * w * (j + 1) + 4 * (i + 0) + k]; if (temp !== undefined) values++, total += temp; } if (!(i == 0)) { temp = data.data[4 * w * (j + 0) + 4 * (i - 1) + k]; if (temp !== undefined) values++, total += temp; } if (!(i == w - 1)) { temp = data.data[4 * w * (j + 0) + 4 * (i + 1) + k]; if (temp !== undefined) values++, total += temp; } values++, total += data.data[4 * w * j + 4 * i + k]; total /= values; data.data[4 * w * j + 4 * i + k] = total; } expressive.data.put(canvas, data, x, y); };
Maybe (just maybe) moving the if checks out as far as possible would be an advantage. Let me present some pseudo-code: I'll just call the code looping over k "inner loop" for simplicity // do a specialized version of "inner loop" that assumes i==0 for (var i = 1; i < (w-1); i++) // do a specialized version of "inner loop" that assumes j==0 && i != 0 && i != (w-1) for (var j = 1; j < (h-1); j++) // do a general version of "inner loop" that can assume i != 0 && j != 0 && i != (w-1) && j != (h-1) } // do a specialized version of "inner loop" that assumes j == (h - 1) && i != 0 && i != (w-1) } // do a specialized version of "inner loop" that assumes i == (w - 1) This would drastically reduce the number if if checks, since the majority of operations would need none of them.
If the only way you use var data is as data.data then you can change: var data = expressive.data.get(canvas, x, y, w, h); to: var data = expressive.data.get(canvas, x, y, w, h).data; and change every line like: temp = data.data[4 * w * (j - 1) + 4 * (i - 1) + k]; to: temp = data[4 * w * (j - 1) + 4 * (i - 1) + k]; and you will save some name lookups. There may be better ways to optimize it but this is just what I've noticed first. Update: Also, if (i != 0 || j != 0) can be faster than if (!(i == 0 && j == 0)) not only because of the negation but also because it can short cuircuit. (Make your own experiments with == vs. === and != vs. !== because my quick tests showed the results that seem counter-intuitive to me.) And also some of the tests are done many times and some of the ifs are mutually exclusive but tested anyway without an else. You can try to refactor it having more nested ifs and more else ifs.
A minor optimization: var imgData = expressive.data.get(canvas, x, y, w, h); var data = imgData.data; // in your if statements temp = data[4 * w * (j - 1) + 4 * (i - 1) + k]; expressive.data.put(canvas, imgData, x, y) You could also perform some minor optimizations in your indices, for example: 4 * w * (j - 1) + 4 * (i - 1) + k // is equal to 4 * ((w * (j-1) + (i-1)) + k var jmin1 = (w * (j-1)) var imin1 = (i-1) //etc, and then use those indices at the right place Also, put {} after every for-statement you have in your code. The 2 additional characters won't make a big difference. The potential bugs will.
You could pull out some common expressions: for (var i = 0; i < w; i++) { for (var j = 0; j < h; j++) { var t = 4*w*j+4*i; var dt = 4*j; for (var k = 0; k < 4; k++) { var total = 0, values = 0, temp = 0; if (!(i == 0 && j == 0)) { temp = data.data[t-dt-4+k]; if (temp !== undefined) values++, total += temp; } if (!(i == w - 1 && j == 0)) { temp = data.data[t-dt+4+k]; if (temp !== undefined) values++, total += temp; } if (!(i == 0 && j == h - 1)) { temp = data.data[t+dt-4+k]; if (temp !== undefined) values++, total += temp; } if (!(i == w - 1 && j == h - 1)) { temp = data.data[t+dt+4+k]; if (temp !== undefined) values++, total += temp; } if (!(j == 0)) { temp = data.data[t-dt+k]; if (temp !== undefined) values++, total += temp; } if (!(j == h - 1)) { temp = data.data[t+dt+k]; if (temp !== undefined) values++, total += temp; } if (!(i == 0)) { temp = data.data[t-4+k]; if (temp !== undefined) values++, total += temp; } if (!(i == w - 1)) { temp = data.data[t+4+k]; if (temp !== undefined) values++, total += temp; } values++, total += data.data[t+k]; total /= values; data.data[t+k] = total; } } } You could try moving the loop over k so it's outermost, and then fold the +k into the definition of t, saving a bit more repeated calculation. (That might turn out to be bad for memory-locality reasons.) You could try moving the loop over j to be outside the loop over i, which will give you better memory locality. This will matter more for large images; it may not matter at all for the size you're using. Rather painful but possibly very effective: you could lose lots of conditional operations by splitting your loops up into {0,1..w-2,w-1} and {0,1..h-2,h-1}. You could get rid of all those undefined tests. Do you really need them, given that you're doing all those range checks? Another way to avoid the range checks: you could pad your image (with zeros) by one pixel along each edge. Note that the obvious way to do this will give different results from your existing code at the edges; this may be a good or a bad thing. If it's a bad thing, you can work out the appropriate value to divide by.
the declaration of temp = 0 is not necessary, just write var total = 0, values = 0, temp;. The next thing is to loop backwards. var length = 100, i; for (i = 0; i < length; i++) {} is slower than var length = 100; for (; length != 0; length--) {} The third tip is to use Duffy's Device for huge for loops.