Min heap in javascript - javascript

Hey I tried to implement a min heap in javascript, but i had a question regarding the algorithm for remove min. I'm using an array to represent the heap internally. When I'm percolating downwards, what should be the stopping condition? In my code I have it at 2 * k <= this.size so it would travel down to potentially the very last element, but it doesn't feel "correct", is there a better stopping condition? Thanks in advance!
this.removeMin = function () {
//replace root with last element and percolate downwards
var min = this._heap[1],
k,
left,
right;
this._heap[1] = this._heap.pop();
this.size--;
k = 1;
while ( ( 2 * k ) <= this.size) {
left = 2 * k;
right = 2 * k + 1;
if (this._heap[k] > this._heap[left] && this._heap[k] > this._heap[right]) {
if (this._heap[left] <= this._heap[right]) {
swap(this._heap, k, left);
k = left;
} else {
swap(this._heap, k, right);
k = right;
}
} else if (this._heap[k] > this._heap[left]) {
swap(this._heap, k, left);
k = left;
} else {
swap(this._heap, k, right);
k = right;
}
}
return min;
};

This is a much easier implementation, I hope this can help.
class MinHeap {
constructor(array) {
this.heap = this.buildHeap(array);
}
// O(n) time | O(1) space
buildHeap(array) {
const firstParentIdx = Math.floor((array.length - 2) / 2);
for (let currentIdx = firstParentIdx; currentIdx >= 0; currentIdx--) {
this.siftDown(currentIdx, array.length - 1, array);
}
return array;
}
// O(log(n)) time | O(1) space
siftDown(currentIdx, endIdx, heap) {
let childOneIdx = currentIdx * 2 + 1;
while (childOneIdx <= endIdx) {
const childTwoIdx = currentIdx * 2 + 2 <= endIdx ? currentIdx * 2 + 2 : -1;
let idxToSwap;
if (childTwoIdx !== -1 && heap[childTwoIdx] < heap[childOneIdx]) {
idxToSwap = childTwoIdx;
} else {
idxToSwap = childOneIdx;
}
if (heap[idxToSwap] < heap[currentIdx]) {
this.swap(currentIdx, idxToSwap, heap);
currentIdx = idxToSwap;
childOneIdx = currentIdx * 2 + 1;
} else {
return;
}
}
}
// O(log(n)) time | O(1) space
siftUp(currentIdx, heap) {
let parentIdx = Math.floor((currentIdx - 1) / 2);
while (currentIdx > 0 && heap[currentIdx] < heap[parentIdx]) {
this.swap(currentIdx, parentIdx, heap);
currentIdx = parentIdx;
parentIdx = Math.floor((currentIdx - 1) / 2);
}
}
// O(1) time | O(1) space
peek() {
return this.heap[0];
}
// O(log(n)) time | O(1) space
remove() {
this.swap(0, this.heap.length - 1, this.heap);
const valueToRemove = this.heap.pop();
this.siftDown(0, this.heap.length - 1, this.heap);
return valueToRemove;
}
// O(log(n)) time | O(1) space
insert(value) {
this.heap.push(value);
this.siftUp(this.heap.length - 1, this.heap);
}
swap(i, j, heap) {
[heap[i], heap[j]] = [heap[j], heap[i]];
}
}

I think you miss one if condition. When the k element is both less than the right and the left, the downwards must stop.
It must be:
if (this._heap[k] > this._heap[left] && this._heap[k] > this._heap[right]) {
if (this._heap[left] <= this._heap[right]) {
swap(this._heap, k, left);
k = left;
} else {
swap(this._heap, k, right);
k = right;
}
} else if (this._heap[k] > this._heap[left]) {
swap(this._heap, k, left);
k = left;
} else if(this._heap[k] < this._heap[right]) {
swap(this._heap, k, right);
k = right;
}else{
break;
}

Why you are writing compare code again and again. Sharing below code for reference which will optimize your code, you can replace it with your while loop.
.....
while (2 * k <= this.size) {
let j = 2 * key;
if (j < this.size && less(j, j + 1)) j++; // find smallest child
if (!less(k, j)) break; // check parent is lesser than smallest child or not
exch(k, j); //if parent is bigger then exchange
k = j; //keep checking untill reaches to the end(this.size)
}
.....
function less(i, j){
if(this._heap[j] < this._heap[i] < 0) return true;
else return false;
}
function exch(i, j){
let temp = this._heap[i];
this._heap[i] = this._heap[j];
this._heap[j] = temp;
}
Hope this works.

Related

Binary Search for multiple items within a Range (Log time filter)

I have an Array of Log items, already sorted by their timestamp (number of milliseconds since 1970). Now I want to filter them by a specific time range, so I think of Binary Search, however this variant is different than all variants I knew before as I need to find a range within a range. Note that there may be none or multiple items at the value edges.
I came up with this to reduce one range requirement but still don't know how to get to the first/last edge items:
filterByTime(min: number, max: number): LogItem[] {
const items = this.items;
const len = items.length;
if (min === undefined && max === undefined) {
return items.slice(0, len - 1);
}
min = min || Number.MIN_VALUE;
max = max || Number.MAX_VALUE;
const fn = (a: LogItem) => {
if (a.time < min) {
return -1;
} else if (a.time > max) {
return 1;
} else {
return 0;
}
};
const lowerBound = this.binarySearchBound(fn, 0, len, true);
if (lowerBound == -1) { return []; }
const upperBound = this.binarySearchBound(fn, 0, len, false);
return items.slice(lowerBound, upperBound + 1);
}
binarySearchBound(compareFn: (a: LogItem) => number, left: number, right: number, isLowerBound: boolean): number {
const items = this.items;
if (items.length == 0) {
return -1;
}
if (isLowerBound && compareFn(items[0]) == 0) {
return 0;
} else if (!isLowerBound && compareFn(items[items.length - 1]) == 0) {
return items.length - 1;
}
while (left <= right) {
const mid = (left + right) / 2;
const item = this.items[mid];
const compare = compareFn(item);
if (compare < 0) {
left = mid + 1;
} else if (compare > 0) {
right = mid - 1;
} else {
// What to do now?
}
}
return -1;
}
Worst case scenario, I can just do a linear search from the edge since I can assume there are not that much items at the edge but surely there is a better way I didn't think of but then I may have to iterate through the whole result set if mid falls in the middle of the result set.
EDIT for adding a note: It's possible for min or max is undefined (and could be both, in which case I can just set an if and return the copy of the whole array). Is it better to just substitute it with MIN_VALUE and MAX_VALUE if they are undefined, or is there a better way to handle that case?
I would suggest the following:
Write two binary search functions, as the execution time is then not hampered by passing and checking the isLowerBound boolean.
Make the returned upperBound to mean the next index after the potential last index that belongs to the range. This corresponds with how arguments work with native functions like slice.
Don't use -1 as a special index. If coded well, an empty range will come out of the two binary searches any way and give an empty array as result
Make the compare function to work with 2 parameters, so you can actually search for either the min or the max value.
Yes, I would use MIN_VALUE and MAX_VALUE as defaults and not test for boundary cases. If boundary cases happen often, it might be worth to include those checks, but in general be aware that these checks will then be executed for every filter, which may bring down the average execution time.
Here is the suggested implementation with integer data (instead of objects) to keep it simple. In order to have it run in a snippet I also removed the type references:
function filterByTime(min=Number.MIN_VALUE, max=Number.MAX_VALUE) {
const fn = (a, b) => a - b; // simplified (should be a.time - b.time)
const lowerBound = this.binarySearchLowerBound(fn, 0, this.items.length, min);
const upperBound = this.binarySearchUpperBound(fn, lowerBound, this.items.length, max);
return this.items.slice(lowerBound, upperBound);
}
function binarySearchLowerBound(compareFn, left, right, target) {
while (left < right) {
const mid = (left + right) >> 1;
if (compareFn(this.items[mid], target) < 0) {
left = mid + 1;
} else { // Also when equal...
right = mid;
}
}
return left;
}
function binarySearchUpperBound(compareFn, left, right, target) {
while (left < right) {
const mid = (left + right) >> 1;
if (compareFn(this.items[mid], target) <= 0) { // Also when equal...
left = mid + 1;
} else {
right = mid;
}
}
return left;
}
// Demo with simplified data (instead of objects with time property)
this.items = [1, 2, 2, 2, 3, 4, 4, 5, 5, 5, 6, 7, 8, 8];
console.log(this.filterByTime(2, 4));
console.log(this.filterByTime(4, 5));
Combined the variants on this article, I merged first and last code into a single function:
filterByTime(items: LogItem[], min: number, max: number): LogItem[] {
const len = items.length;
if (len == 0) {
return [];
}
if (min === undefined && max === undefined) {
return items.slice(0, len - 1);
}
min = min || Number.MIN_VALUE;
max = max || Number.MAX_VALUE;
const fn = (a: LogItem) => {
if (a.time < min) {
return -1;
} else if (a.time > max) {
return 1;
} else {
return 0;
}
};
const lowerBound = this.binarySearchBound(fn, 0, len, true);
if (lowerBound == -1) { return []; }
const upperBound = this.binarySearchBound(fn, 0, len, false);
return items.slice(lowerBound, upperBound + 1);
}
binarySearchBound(compareFn: (a: LogItem) => number, left: number, right: number, isLowerBound: boolean): number {
const items = this.items;
if (items.length == 0) {
return -1;
}
if (isLowerBound && compareFn(items[0]) == 0) {
return 0;
} else if (!isLowerBound && compareFn(items[items.length - 1]) == 0) {
return items.length - 1;
}
let result = -1;
while (left <= right) {
const mid = (left + right) / 2;
const item = this.items[mid];
const compare = compareFn(item);
if (compare < 0) {
left = mid + 1;
} else if (compare > 0) {
right = mid - 1;
} else {
result = mid;
if (isLowerBound) {
right = mid - 1;
} else {
left = mid + 1;
}
}
}
return result;
}

How to generate a random number based on fixed character set in javascript

I'm generating a number based on a fixed character set.
function generator()
{
var text = "";
var char_list = "LEDGJR", number_list = "0123456789";
for(var i=0; i < 2; i++ )
{
text += char_list.charAt(Math.floor(Math.random() * char_list.length));
}
for(var j=0; j < 2; j++ )
{
text += number_list.charAt(Math.floor(Math.random() *
number_list.length));
}
return text;
}
Result :
RE39, JE12 etc...
Once all the permutation related to the above sequence is done, then the generator should generate string as RE391, JE125 means adding one more number to the complete number.
How can I get the permutation count of sequence?
For simplicity consider the case where:
chars = "AB"
nums = "123";
and we want to generate a 4-digit sequence of two chars and two numbers.
We define these variables
rows = [chars, chars, nums, nums]
rowSizes = rows.map(row => row.length) // [2, 2, 3, 3]
It’s easy to see the set size of all possible permuations equals:
spaceSize = rowSizes.reduce((m, n) => m * n, 1) // 2*2*3*3 = 36
And we define two set of utility functions, usage of which I'll explain in detail later.
decodeIndex() which gives us uniqueness
function euclideanDivision(a, b) {
const remainder = a % b;
const quotient = (a - remainder) / b;
return [quotient, remainder]
}
function decodeIndex(index, rowSizes) {
const rowIndexes = []
let dividend = index
for (let i = 0; i < rowSizes.length; i++) {
const [quotient, remainder] = euclideanDivision(dividend, rowSizes[i])
rowIndexes[i] = remainder
dividend = quotient
}
return rowIndexes
}
getNextIndex() which gives us pseudo-randomness
function isPrime(n) {
if (n <= 1) return false;
if (n <= 3) return true;
if (n % 2 == 0 || n % 3 == 0) return false;
for (let i = 5; i * i <= n; i = i + 6) {
if (n % i == 0 || n % (i + 2) == 0) return false;
}
return true;
}
function findNextPrime(n) {
if (n <= 1) return 2;
let prime = n;
while (true) {
prime++;
if (isPrime(prime)) return prime;
}
}
function getIndexGeneratorParams(spaceSize) {
const N = spaceSize;
const Q = findNextPrime(Math.floor(2 * N / (1 + Math.sqrt(5))))
const firstIndex = Math.floor(Math.random() * spaceSize);
return [firstIndex, N, Q]
}
function getNextIndex(prevIndex, N, Q) {
return (prevIndex + Q) % N
}
Uniqueness
Like mentioned above, spaceSize is the number of all possible permutations, thus each index in range(0, spaceSize) uniquely maps to one permutation. decodeIndex helps with this mapping, you can get the corresponding permutation to an index by:
function getSequenceAtIndex(index) {
const tuple = decodeIndex(index, rowSizes)
return rows.map((row, i) => row[tuple[i]]).join('')
}
Pseudo-Randomness
(Credit to this question. I just port that code into JS.)
We get pseudo-randomness by polling a "full cycle iterator"†. The idea is simple:
have the indexes 0..35 layout in a circle, denote upperbound as N=36
decide a step size, denoted as Q (Q=23 in this case) given by this formula‡
Q = findNextPrime(Math.floor(2 * N / (1 + Math.sqrt(5))))
randomly decide a starting point, e.g. number 5
start generating seemingly random nextIndex from prevIndex, by
nextIndex = (prevIndex + Q) % N
So if we put 5 in we get (5 + 23) % 36 == 28. Put 28 in we get (28 + 23) % 36 == 15.
This process will go through every number in circle (jump back and forth among points on the circle), it will pick each number only once, without repeating. When we get back to our starting point 5, we know we've reach the end.
†: I'm not sure about this term, just quoting from this answer
‡: This formula only gives a nice step size that will make things look more "random", the only requirement for Q is it must be coprime to N
Full Solution
Now let's put all the pieces together. Run the snippet to see result.
I've also includes the a counter before each log. For your case with char_list="LEDGJR", number_list="0123456789", the spaceSize for 4-digit sequence should be 6*6*10*10 = 3600
You'll observe the log bump to 5-digit sequence at 3601 😉
function euclideanDivision(a, b) {
const remainder = a % b;
const quotient = (a - remainder) / b;
return [quotient, remainder];
}
function decodeIndex(index, rowSizes) {
const rowIndexes = [];
let divident = index;
for (let i = 0; i < rowSizes.length; i++) {
const [quotient, remainder] = euclideanDivision(divident, rowSizes[i]);
rowIndexes[i] = remainder;
divident = quotient;
}
return rowIndexes;
}
function isPrime(n) {
if (n <= 1) return false;
if (n <= 3) return true;
if (n % 2 == 0 || n % 3 == 0) return false;
for (let i = 5; i * i <= n; i = i + 6) {
if (n % i == 0 || n % (i + 2) == 0) return false;
}
return true;
}
function findNextPrime(n) {
if (n <= 1) return 2;
let prime = n;
while (true) {
prime++;
if (isPrime(prime)) return prime;
}
}
function getIndexGeneratorParams(spaceSize) {
const N = spaceSize;
const Q = findNextPrime(Math.floor((2 * N) / (1 + Math.sqrt(5))));
const firstIndex = Math.floor(Math.random() * spaceSize);
return [firstIndex, N, Q];
}
function getNextIndex(prevIndex, N, Q) {
return (prevIndex + Q) % N;
}
function generatorFactory(rows) {
const rowSizes = rows.map((row) => row.length);
function getSequenceAtIndex(index) {
const tuple = decodeIndex(index, rowSizes);
return rows.map((row, i) => row[tuple[i]]).join("");
}
const spaceSize = rowSizes.reduce((m, n) => m * n, 1);
const [firstIndex, N, Q] = getIndexGeneratorParams(spaceSize);
let currentIndex = firstIndex;
let exhausted = false;
function generator() {
if (exhausted) return null;
const sequence = getSequenceAtIndex(currentIndex);
currentIndex = getNextIndex(currentIndex, N, Q);
if (currentIndex === firstIndex) exhausted = true;
return sequence;
}
return generator;
}
function getRows(chars, nums, rowsOfChars, rowsOfNums) {
const rows = [];
while (rowsOfChars--) {
rows.push(chars);
}
while (rowsOfNums--) {
rows.push(nums);
}
return rows;
}
function autoRenewGeneratorFactory(chars, nums, initRowsOfChars, initRowsOfNums) {
let realGenerator;
let currentRowOfNums = initRowsOfNums;
function createRealGenerator() {
const rows = getRows(chars, nums, initRowsOfChars, currentRowOfNums);
const generator = generatorFactory(rows);
currentRowOfNums++;
return generator;
}
realGenerator = createRealGenerator();
function proxyGenerator() {
const sequence = realGenerator();
if (sequence === null) {
realGenerator = createRealGenerator();
return realGenerator();
} else {
return sequence;
}
}
return proxyGenerator;
}
function main() {
const char_list = "LEDGJR"
const number_list = "0123456789";
const generator = autoRenewGeneratorFactory(char_list, number_list, 2, 2);
let couter = 0
setInterval(() => {
console.log(++couter, generator())
}, 10);
}
main();

Why Is Empty Argument Not Getting Identified as Undefined?

I have an (almost) working solution for a coding challenge:
function addLetters(...letters) {
let sum = 0;
const alphabet = 'abcdefghijklmnopqrstuvwxyz'.split('');
if (typeof letters === 'undefined' || letters === [] || letters === undefined) {
return 'z';
}
for (let i = 0; i < letters.length; i++) {
sum += (alphabet.indexOf(letters[i]) + 1);
}
if (sum <= 26) {
return alphabet[sum - 1];
} else {
while (sum > 26) {
sum = (sum - 26);
if (sum <= 26) {
return alphabet[sum - 1];
}
}
}
}
console.log(addLetters())
But as you can see, in this particular case of console.log(addLetters()), it's returning undefined instead of 'z' - why is that?
I think it must have something to do with the way that ...letters is a rest / default / destructured / spread argument.
The challenge does, in fact, want the argument to appear as a spread, but I don't know how to accommodate for it.
EDIT Test specs for challenge:
letters === []
Will always be false, as these are two different references which will never evaluate to true, you need to check the length of array to check if it's empty or not
Also you can safely remove the other two conditions from if statement as letters will always be an array
function addLetters(...letters) {
let sum = 0;
const alphabet = 'abcdefghijklmnopqrstuvwxyz'.split('');
if (letters.length === 0) {
return 'z';
}
for (let i = 0; i < letters.length; i++) {
sum += (alphabet.indexOf(letters[i]) + 1);
}
if (sum <= 26) {
return alphabet[sum - 1];
} else {
while (sum > 26) {
sum = (sum - 26);
if (sum <= 26) {
return alphabet[sum - 1];
}
}
}
}
console.log(addLetters())
Try this. :)
function addLetters(...letters) {
let sum = 0;
const alphabet = 'abcdefghijklmnopqrstuvwxyz'.split('');
if (!letters.length) {
return 'z';
}
for (let i = 0; i < letters.length; i++) {
sum += (alphabet.indexOf(letters[i]) + 1);
}
if (sum <= 26) {
return alphabet[sum - 1];
} else {
while (sum > 26) {
sum = (sum - 26);
if (sum <= 26) {
return alphabet[sum - 1];
}
}
}
}

How can I find the prime factors of an integer in JavaScript?

I was trying to find the prime factors of a number, recorded below as 'integer' using a for loop in javascript. I can't seem to get it working and I'm not sure whether it's my JavaScript or my calculation logic.
//integer is the value for which we are finding prime factors
var integer = 13195;
var primeArray = [];
//find divisors starting with 2
for (i = 2; i < integer/2; i++) {
if (integer % i == 0) {
//check if divisor is prime
for (var j = 2; j <= i / 2; j++) {
if (i % j == 0) {
isPrime = false;
} else {
isPrime = true;
}
}
//if divisor is prime
if (isPrime == true) {
//divide integer by prime factor & factor store in array primeArray
integer /= i
primeArray.push(i);
}
}
}
for (var k = 0; k < primeArray.length; k++) {
console.log(primeArray[k]);
}
The answer above is inefficient with O(N^2) complexity. Here is a better answer with O(N) complexity.
function primeFactors(n) {
const factors = [];
let divisor = 2;
while (n >= 2) {
if (n % divisor == 0) {
factors.push(divisor);
n = n / divisor;
} else {
divisor++;
}
}
return factors;
}
const randomNumber = Math.floor(Math.random() * 10000);
console.log('Prime factors of', randomNumber + ':', primeFactors(randomNumber).join(' '))
You can filter for duplicates as you please!
Here's a working solution:
function getPrimeFactors(integer) {
const primeArray = [];
let isPrime;
// Find divisors starting with 2
for (let i = 2; i <= integer; i++) {
if (integer % i !== 0) continue;
// Check if the divisor is a prime number
for (let j = 2; j <= i / 2; j++) {
isPrime = i % j !== 0;
}
if (!isPrime) continue;
// if the divisor is prime, divide integer with the number and store it in the array
integer /= i
primeArray.push(i);
}
return primeArray;
}
console.log(getPrimeFactors(13195).join(', '));
You were very much on the right track. There were two minor mistakes. The evaluation of integer - 1 seemed to be incorrect. I believe the more appropriate evaluation is <= integer in your outer for loop. This is because when you divide your integer below integer /= i, this results in the final integer evaluation to be 29. The final prime divisor in this case is also 29 and as such will need to be evaluated as <= as oppose to < integer - 1.
As for why the final log statement isn't working, there was a simple typo of primeArray[i] as oppose to primeArray[k].
I do believe there is a mistake in both code above. If you replace the integer by 100 the prime factorization won't work anymore as the factor 2 cannot be considered with those for loops. As j = 2, i = 2 and j<=i/2 in the condition - meaning the loop will never run for i=2, which is a prime factor.
Tried to make it work this way but couldn't figure out.
Had to rely on a different approach with a while loop here :
function getAllFactorsFor(remainder) {
var factors = [], i;
for (i = 2; i <= remainder; i++) {
while ((remainder % i) === 0) {
factors.push(i);
remainder /= i;
}
}
return factors;
}
https://jsfiddle.net/JamesOR/RC7SY/
You could also go with something like that :
let findPrimeFactors = (num) => {
let arr = [];
for ( var i = 2; i < num; i++) {
let isPrime
if (num % i === 0) {
isPrime = true;
for (var j = 2; j <= i; j++) {
if ( i % j === 0) {
isPrime == false;
}
}
}if (isPrime == true) { arr.push(i)}
}console.log(arr)
}
findPrimeFactors(543)
We can find the prime factor numbers up to n with only one loop. It is a very simple solution without any nested loop.
Time complexity would be less than O(n) because we are dividing "n" by "i".
function primeFactors(n) {
let arr=[];
let i = 2;
while(i<=n){
if(n%i == 0) {
n= n/i;
arr.push(i);
} else {
i++;
}
}
return arr;
}
// primeFactors(10) [2,5]
// primeFactors(10) [2,2,5,5]
// primeFactors(2700) [2, 2, 3, 3, 3, 5, 5]
When factorizing an integer (n) to its prime factors, after finding the first prime factor, the problem in hand is reduced to finding prime factorization of quotient (q).
Suppose n is divisible to prime p1 then we have n = p1 * q1 so after finding p1 the problem is reduced to factorizing q1 (quotient). If the function name is primeFactorizer then we can call it recursively and solution for n would be:
n = p1 * primeFactorizer(q1)
n = p1 * p2 * primeFactorizer(q2)
...
Until qn is prime itself.
Also I'm going to use a helper generator function which generates primes for us:
function * primes () {
let n = 2
while (true) {
let isPrime = true
for (let i = 2; i <= n / 2; i++) {
if (n % i === 0) {
isPrime = false
break
}
}
if (isPrime) {
yield n
}
n++
}
}
And function to factorize n would be:
function primeFactorizer (n, result = []) {
for (const p of primes()) {
if (n === p) {
result.push(p)
return result
}
if (n % p === 0) {
result.push(p)
return primeFactorizer(n / p, result)
}
}
}
I've refined this function over time, trying to get it as fast as possible (racing it against others' functions that I've found online, haven't found one that runs consistently faster than it yet).
function primFact(num) {
var factors = [];
/* since 2 is the only even prime, it's easier to factor it out
* separately from the odd factor loop (for loop doesn't need to
* check whether or not to add 1 or 2 to f).
* The condition is essentially checking if the number is even
* (bitwise "&" operator compares the bits of 2 numbers in binary
* and outputs a binary number with 1's where their digits are the
* same and 0's where they differ. In this case it only checks if
* the final digit for num in binary is 1, which would mean the
* number is odd, in which case the output would be 1, which is
* interpreted as true, otherwise the output will be 0, which is
* interpreted as false. "!" returns the opposite boolean, so this
* means that '!(num & 1)' is true when the num is not odd)
*/
while (!(num & 1)) {
factors.push(2);
num /= 2;
}
// 'f*f <= num' is faster than 'f <= Math.sqrt(num)'
for (var f = 3; f*f <= num; f += 2) {
while (!(num % f)) { // remainder of 'num / f' isn't 0
factors.push(f);
num /= f;
}
}
/* if the number is already prime, then this adds it to factors so
* an empty array isn't returned
*/
if (num != 1) {
factors.push(num);
}
return factors;
}
This performs very well at large numbers compared to functions I've run it against, especially when the number is prime, (rarely runs slower than 10ms when I've run it in an online compiler like OneCompiler) so if you want speed I'd say this is a pretty good way to go about it.
Still working on making it even faster, but only way to include all primes without adding new conditions to check is to iterate through all odd numbers.
I just started JavaScript but i managed to come up with my own solution for this while working on a school project with a similar objective.
Only issue is that it takes a very long time for large numbers, its not v ery efficient. But it works perfectly.
function isPrime(n){
if (n === 1){
return false;
}
else if (n === 2){
return true;
}
else{
for (let x = 2; x < n; x ++){
if (n % x === 0){
return false;
}
}
return true;
}
}
let primeFac = []
let num = 30
for (let x = 0; x <= num; x++){
if (num % x === 0 && isPrime(x) === true){
primeFac.push(x);
}
}
console.log(`${primeFac}`)
If you work up from the bottom there's no need to check if any following factor is prime. This is because any lower primes will have already been divided out.
function getPrimeFactorsFor(num) {
const primes = [];
for (let factor = 2; factor <= num; factor++) {
while ((num % factor) === 0) {
primes.push(factor);
num /= factor;
}
}
return primes;
}
console.log("10 has the primes: ", getPrimeFactorsFor(10));
console.log("8 has the primes: ", getPrimeFactorsFor(8));
console.log("105 has the primes: ", getPrimeFactorsFor(105))
console.log("1000 has the primes: ", getPrimeFactorsFor(1000))
console.log("1155 has the primes: ", getPrimeFactorsFor(1155))
In case somebody is looking for the fastest solution, here's one based on my library prime-lib. It can calculate prime factors for any number between 2 and 2^53 - 1, in under 1ms. The function source code is available here.
import {primeFactors} from 'prime-lib';
const factors = primeFactors(600851475143);
//=> [71, 839, 1471, 6857]
Here an other implementation to find prime factors, in three variations. It's more efficient than the other implementations, worst case sqrt(n), because it stops earlier.
The function* means it's a generator function. So a generator is returned instead of an array and the next prime factor is only calculated as soon as it is requested.
// Example: 24 -> 2, 3
function* singlePrimeFactors (n) {
for (var k = 2; k*k <= n; k++) {
if (n % k == 0) {
yield k
do {n /= k} while (n % k == 0)
}
}
if (n > 1) yield n
}
// Example: 24 -> 2, 2, 2, 3
function* repeatedPrimeFactors (n) {
for (var k = 2; k*k <= n; k++) {
while (n % k == 0) {
yield k
n /= k
}
}
if (n > 1) yield n
}
// Example: 24 -> {p: 2, m: 3}, {p: 3, m: 1}
function* countedPrimeFactors (n) {
for (var k = 2; k*k <= n; k++) {
if (n % k == 0) {
var count = 1
for (n /= k; n % k == 0; n /= k) count++
yield {p: k, m: count}
}
}
if (n > 1) yield {p: n, m: 1}
}
// Test code
for (var i=1; i<=100; i++) {
var single = JSON.stringify(Array.from(singlePrimeFactors(i)))
var repeated = JSON.stringify(Array.from(repeatedPrimeFactors(i)))
var counted = JSON.stringify(Array.from(countedPrimeFactors(i)))
console.log(i, single, repeated, counted)
}
// Iterating over a generator
for (var p of singlePrimeFactors(24)) {
console.log(p)
}
// Iterating over a generator, an other way
var g = singlePrimeFactors(24)
for (var r = g.next(); !r.done; r = g.next()) {
console.log(r.value);
}
My solution avoids returning not prime factors:
let result = [];
let i = 2;
let j = 2;
let number = n;
for (; i <= number; i++) {
let isPrime = number % i === 0;
if (isPrime) {
result.push(i);
number /= i;
}
while (isPrime) {
if (number % i === 0) {
result.push(i);
number /= i;
} else {
isPrime = false;
}
}
}
return result;
With so many good solutions above, wanted to make a little bit of improvement by using this theorem in the Math Forum Finding prime factors by taking the square root
.
function primeFactors(n)
{
// Print the number of 2s that divide n
while (n%2 == 0)
{
console.log(2);
n = n/2;
}
// n must be odd at this point. So we can skip
// one element (Note i = i +2)
for (var i = 3; i <= Math.sqrt(n); i = i+2)
{
// While i divides n, print i and divide n
while (n%i == 0)
{
console.log(i);
n = n/i;
}
}
// This condition is to handle the case when n
// is a prime number greater than 2
if (n > 2)
console.log(n);
}
primeFactors(344);
console.log("--------------");
primeFactors(4);
console.log("--------------");
primeFactors(10);
Hope this answer adds value.
Here is a solution using recursion
function primeFactors(num, primes){
let i = 2;
while(i < num){
if(num % i === 0){
primes.push(i);
return primeFactors(num/i, primes);
}
i++
}
primes.push(num);
return primes;
}
console.log(primeFactors(55, []))
console.log(primeFactors(15, []))
console.log(primeFactors(40, []))
console.log(primeFactors(13, []))
// [ 5, 11 ]
// [ 3, 5 ]
// [ 2, 2, 2, 5 ]
// [ 13 ]
I found this solution by chance when i was trying to simplify several
solutions that i saw here. Although it doesn't check if the divisor
is a prime number somehow it seems to work, i tested it with
miscellaneous numbers but i could not explain how this was possible.
function start() {
var integer = readInt("Enter number: ");
println("The prime factorization is: ");
for(var i = 2; i <= integer; i++) {
if (integer % i == 0) {
println(i);
integer = integer / i;
i = i - 1;
}
}
}
I checked the algorithm with yield, but that is a lot slower than recursive calls.
function rootnth(val, power=2) {
let o = 0n; // old approx value
let x = val;
let limit = 100;
let k = BigInt(power);
while(x**k!==k && x!==o && --limit) {
o=x;
x = ((k-1n)*x + val/x**(k-1n))/k;
}
return x;
}
// Example: 24 -> 2, 2, 2, 3
function repeatedPrimeFactors (n,list) {
if (arguments.length == 1) list = "";
if (n % 2n == 0) return repeatedPrimeFactors(n/2n, list + "*2")
else if (n % 3n == 0) return repeatedPrimeFactors(n/3n, list + "*3")
var sqrt = rootnth(n);
let k = 5n;
while (k <= sqrt) {
if (n % k == 0) return repeatedPrimeFactors(n/k, list + "*" + k)
if (n % (k+2n) == 0) return repeatedPrimeFactors(n/(k+2n), list + "*" + (k+2n))
k += 6n;
}
list = list + "*" + n;
return list;
}
var q = 11111111111111111n; // seventeen ones
var t = (new Date()).getTime();
var count = repeatedPrimeFactors(BigInt(q)).substr(1);
console.log(count);
console.log(("elapsed=" + (((new Date()).getTime())-t)+"ms");
Here I try for the factors 2 and 3, followed by alternatingly adding 2 anf 4 (5,7,11,13,17,...) until the square root of the number.
Seventeen ones (which is not prime) takes about 1 second and nineteen ones (which is prime) eight seconds (Firefox).
Here is the solution with the nested function using the filter method.
function primeFactors(params) {
function prime(number) {
for (let i = 2; i < number + 1; ) {
if (number === 2) {
return true;
}
if (number % i === 0 && number !== i) {
return false;
} else if (i < number) {
i++;
} else {
return true;
}
}
}
let containerPrime = [];
let containerUnPrime = [];
for (let i = 0; i < params; i++) {
if (prime(i)) {
containerPrime.push(i);
} else {
containerUnPrime.push(i);
}
}
return containerPrime.filter((e) => params % e === 0);
}
console.log(primeFactors(13195));
function primeFactorization(n) {
let factors = [];
while (n % 2 === 0) {
factors.push(2);
n = n / 2;
}
for (let i = 3; i <= Math.sqrt(n); i += 2) {
while (n % i === 0) {
factors.push(i);
n = n / i;
}
}
if (n > 2) {
factors.push(n);
}
return factors;
}
console.log(primeFactorization(100));
The answer with O(sqrt(n)) complexity, it's faster than O(n):
const number = 13195;
let divisor = 2;
const result = [];
let n = number;
while (divisor * divisor <= number) {
if (n % divisor === 0) {
result.push(divisor);
n /= divisor;
} else {
divisor++;
}
}
if (n > 1) {
result.push(n);
}
console.log(result);
The above code (the code which has while loop) is correct, but there is one small correction in that code.
var num, i, factorsArray = [];
function primeFactor(num) {
for (i = 2; i <= num; i++) {
while (num % i == 0) {
factorsArray.push(i);
num = num / 2;
}
}
}
primeFactor(18);
var newArray = Array.from(new Set(factorsArray));
document.write(newArray);
This is my solution
function prime(n) {
for (var i = 1; i <= n; i++) {
if (n%i===0) {
myFact.push(i);
var limit = Math.sqrt(i);
for (var j = 2; j < i; j++) {
if (i%j===0) {
var index = myFact.indexOf(i);
if (index > -1) {
myFact.splice(index, 1);
}
}
}
}
}
}
var n = 100, arr =[],primeNo = [],priFac=[];
for(i=0;i<=n;i++){
arr.push(true);
}
//console.log(arr)
let uplt = Math.sqrt(n)
for(j=2;j<=uplt;j++){
if(arr[j]){
for(k=j*j;k<=n;k+=j){
arr[k] = false;
}
}
}
for(l=2;l<=n;l++){
if(arr[l])
primeNo.push(l)
}
for(m=0;m<primeNo.length;m++){
if(n%primeNo[m]==0)
priFac.push(primeNo[m])
}
console.log(...priFac);

Damerau-Levenshtein distance Implementation

I'm trying to create a damerau-levenshtein distance function in JS.
I've found a description off the algorithm on WIkipedia, but they is no implementation off it. It says:
To devise a proper algorithm to calculate unrestricted
Damerau–Levenshtein distance note that there always exists an optimal
sequence of edit operations, where once-transposed letters are never
modified afterwards. Thus, we need to consider only two symmetric ways
of modifying a substring more than once: (1) transpose letters and
insert an arbitrary number of characters between them, or (2) delete a
sequence of characters and transpose letters that become adjacent
after deletion. The straightforward implementation of this idea gives
an algorithm of cubic complexity: O\left (M \cdot N \cdot \max(M, N)
\right ), where M and N are string lengths. Using the ideas of
Lowrance and Wagner,[7] this naive algorithm can be improved to be
O\left (M \cdot N \right) in the worst case. It is interesting that
the bitap algorithm can be modified to process transposition. See the
information retrieval section of[1] for an example of such an
adaptation.
https://en.wikipedia.org/wiki/Damerau%E2%80%93Levenshtein_distance
The section [1] points to http://acl.ldc.upenn.edu/P/P00/P00-1037.pdf which is even more complicated to me.
If I understood this correctly, it's not that easy to create an implementation off it.
Here's the levenshtein implementation I currently use :
levenshtein=function (s1, s2) {
// discuss at: http://phpjs.org/functions/levenshtein/
// original by: Carlos R. L. Rodrigues (http://www.jsfromhell.com)
// bugfixed by: Onno Marsman
// revised by: Andrea Giammarchi (http://webreflection.blogspot.com)
// reimplemented by: Brett Zamir (http://brett-zamir.me)
// reimplemented by: Alexander M Beedie
// example 1: levenshtein('Kevin van Zonneveld', 'Kevin van Sommeveld');
// returns 1: 3
if (s1 == s2) {
return 0;
}
var s1_len = s1.length;
var s2_len = s2.length;
if (s1_len === 0) {
return s2_len;
}
if (s2_len === 0) {
return s1_len;
}
// BEGIN STATIC
var split = false;
try {
split = !('0')[0];
} catch (e) {
// Earlier IE may not support access by string index
split = true;
}
// END STATIC
if (split) {
s1 = s1.split('');
s2 = s2.split('');
}
var v0 = new Array(s1_len + 1);
var v1 = new Array(s1_len + 1);
var s1_idx = 0,
s2_idx = 0,
cost = 0;
for (s1_idx = 0; s1_idx < s1_len + 1; s1_idx++) {
v0[s1_idx] = s1_idx;
}
var char_s1 = '',
char_s2 = '';
for (s2_idx = 1; s2_idx <= s2_len; s2_idx++) {
v1[0] = s2_idx;
char_s2 = s2[s2_idx - 1];
for (s1_idx = 0; s1_idx < s1_len; s1_idx++) {
char_s1 = s1[s1_idx];
cost = (char_s1 == char_s2) ? 0 : 1;
var m_min = v0[s1_idx + 1] + 1;
var b = v1[s1_idx] + 1;
var c = v0[s1_idx] + cost;
if (b < m_min) {
m_min = b;
}
if (c < m_min) {
m_min = c;
}
v1[s1_idx + 1] = m_min;
}
var v_tmp = v0;
v0 = v1;
v1 = v_tmp;
}
return v0[s1_len];
}
What are your ideas for building such an algorithm and, if you think it would be too complicated, what could I do to make no difference between 'l' (L lowercase) and 'I' (i uppercase) for example.
The gist #doukremt gave: https://gist.github.com/doukremt/9473228
gives the following in Javascript.
You can change the weights of operations in the weighter object.
var levenshteinWeighted= function(seq1,seq2)
{
var len1=seq1.length;
var len2=seq2.length;
var i, j;
var dist;
var ic, dc, rc;
var last, old, column;
var weighter={
insert:function(c) { return 1.; },
delete:function(c) { return 0.5; },
replace:function(c, d) { return 0.3; }
};
/* don't swap the sequences, or this is gonna be painful */
if (len1 == 0 || len2 == 0) {
dist = 0;
while (len1)
dist += weighter.delete(seq1[--len1]);
while (len2)
dist += weighter.insert(seq2[--len2]);
return dist;
}
column = []; // malloc((len2 + 1) * sizeof(double));
//if (!column) return -1;
column[0] = 0;
for (j = 1; j <= len2; ++j)
column[j] = column[j - 1] + weighter.insert(seq2[j - 1]);
for (i = 1; i <= len1; ++i) {
last = column[0]; /* m[i-1][0] */
column[0] += weighter.delete(seq1[i - 1]); /* m[i][0] */
for (j = 1; j <= len2; ++j) {
old = column[j];
if (seq1[i - 1] == seq2[j - 1]) {
column[j] = last; /* m[i-1][j-1] */
} else {
ic = column[j - 1] + weighter.insert(seq2[j - 1]); /* m[i][j-1] */
dc = column[j] + weighter.delete(seq1[i - 1]); /* m[i-1][j] */
rc = last + weighter.replace(seq1[i - 1], seq2[j - 1]); /* m[i-1][j-1] */
column[j] = ic < dc ? ic : (dc < rc ? dc : rc);
}
last = old;
}
}
dist = column[len2];
return dist;
}
Stolen from here, with formatting and some examples on how to use it:
function DamerauLevenshtein(prices, damerau) {
//'prices' customisation of the edit costs by passing an object with optional 'insert', 'remove', 'substitute', and
//'transpose' keys, corresponding to either a constant number, or a function that returns the cost. The default cost
//for each operation is 1. The price functions take relevant character(s) as arguments, should return numbers, and
//have the following form:
//
//insert: function (inserted) { return NUMBER; }
//
//remove: function (removed) { return NUMBER; }
//
//substitute: function (from, to) { return NUMBER; }
//
//transpose: function (backward, forward) { return NUMBER; }
//
//The damerau flag allows us to turn off transposition and only do plain Levenshtein distance.
if (damerau !== false) {
damerau = true;
}
if (!prices) {
prices = {};
}
let insert, remove, substitute, transpose;
switch (typeof prices.insert) {
case 'function':
insert = prices.insert;
break;
case 'number':
insert = function (c) {
return prices.insert;
};
break;
default:
insert = function (c) {
return 1;
};
break;
}
switch (typeof prices.remove) {
case 'function':
remove = prices.remove;
break;
case 'number':
remove = function (c) {
return prices.remove;
};
break;
default:
remove = function (c) {
return 1;
};
break;
}
switch (typeof prices.substitute) {
case 'function':
substitute = prices.substitute;
break;
case 'number':
substitute = function (from, to) {
return prices.substitute;
};
break;
default:
substitute = function (from, to) {
return 1;
};
break;
}
switch (typeof prices.transpose) {
case 'function':
transpose = prices.transpose;
break;
case 'number':
transpose = function (backward, forward) {
return prices.transpose;
};
break;
default:
transpose = function (backward, forward) {
return 1;
};
break;
}
function distance(down, across) {
//http://en.wikipedia.org/wiki/Damerau%E2%80%93Levenshtein_distance
let ds = [];
if (down === across) {
return 0;
} else {
down = down.split('');
down.unshift(null);
across = across.split('');
across.unshift(null);
down.forEach(function (d, i) {
if (!ds[i]) {
ds[i] = [];
}
across.forEach(function (a, j) {
if (i === 0 && j === 0) {
ds[i][j] = 0;
} else if (i === 0) {
//Empty down (i == 0) -> across[1..j] by inserting
ds[i][j] = ds[i][j - 1] + insert(a);
} else if (j === 0) {
//Down -> empty across (j == 0) by deleting
ds[i][j] = ds[i - 1][j] + remove(d);
} else {
//Find the least costly operation that turns the prefix down[1..i] into the prefix across[1..j] using
//already calculated costs for getting to shorter matches.
ds[i][j] = Math.min(
//Cost of editing down[1..i-1] to across[1..j] plus cost of deleting
//down[i] to get to down[1..i-1].
ds[i - 1][j] + remove(d),
//Cost of editing down[1..i] to across[1..j-1] plus cost of inserting across[j] to get to across[1..j].
ds[i][j - 1] + insert(a),
//Cost of editing down[1..i-1] to across[1..j-1] plus cost of substituting down[i] (d) with across[j]
//(a) to get to across[1..j].
ds[i - 1][j - 1] + (d === a ? 0 : substitute(d, a))
);
//Can we match the last two letters of down with across by transposing them? Cost of getting from
//down[i-2] to across[j-2] plus cost of moving down[i-1] forward and down[i] backward to match
//across[j-1..j].
if (damerau && i > 1 && j > 1 && down[i - 1] === a && d === across[j - 1]) {
ds[i][j] = Math.min(ds[i][j], ds[i - 2][j - 2] + (d === a ? 0 : transpose(d, down[i - 1])));
}
}
});
});
return ds[down.length - 1][across.length - 1];
}
}
return distance;
}
//Returns a distance function to call.
let dl = DamerauLevenshtein();
console.log(dl('12345', '23451'));
console.log(dl('this is a test', 'this is not a test'));
console.log(dl('testing testing 123', 'test'));

Categories