I'm doing an exercise which i'm no being able to solve. I need to get the maximum accumulated profit by buying and selling bitcoins. I have a function(A,Y) which receive an A = array of different prices during time and a Y = fee
Restrictions:
Note: If a bitcoin was bought at 0 and sold at 1, we would have ad a loss of A[1] - A[0] =7050 -7200 - Y = -200. So, that movement was not made.
Note2: You can only have 1 bitcoin at the time. To sell, you have to have bought first. To buy, you need to have nothing or sold before.
Note3: Movements need to be time consequents. You cannot buy at A[5] and sell at A[4]
Note4: If no profit cannot be made, it should return 0
complexity is O(N)
A = [7200,7050,7300,7500,7440,7200,7300,7280,7400] //expected result 550
Y = 50
A[3] - A[1] - Y = 7500 - 7050 - 50 = 400
A[8] - A[5] - Y = 7400 - 7200 - 50 = 150
result = 550 //maximum accumulated profit
This is what i have
function solution(A, Y) {
if(A.length < 2) {
return 0;
}
var minIndex = (A[0] > A[1]) ? 1 : 0;
var minPrice = A[minIndex];
var acum = 0;
var i = minIndex + 1
for (i; i< A.length-1; i++) {
if( (A[i] - minPrice - Y) > (A[i+1] - minPrice - Y )) {
acum += A[i] - minPrice - Y;
i = i+1
} else {
acum += A[i + 1] - minPrice - Y;
i = i+2
}
minPrice = (A[i] > A[i+1]) ? A[i+1] : A[i];
}
return acum > 0 ? acum : 0;
}
Actually i'm getting 450 but it should be 550
It looks more complicated as it seems to be, because you need to check every single buying price with all possible selling price.
The result is a tree with this brute force approach.
This solution returns only the maximum profit with all buy/sell prices.
function maxima(array, fee) {
function iter(prices, index, count) {
var i = 0, profit = 0;
if (index >= array.length) {
if (!prices.length || prices.length % 2) {
return;
}
if (prices.some((v, i, a) => i && (i % 2 ? a[i - 1] >= v : a[i - 1] < v))) {
return;
}
while (i < prices.length) {
profit += prices[i + 1] - prices[i] - fee;
i += 2;
}
if (!result.length || result[0].profit < profit) {
result = [{ profit, prices }];
} else if (result[0].profit === profit) {
result.push({ profit, prices });
}
return;
}
iter(prices.concat(array[index]), index + 1); // buy/sell
iter(prices, index + 1); // no action
}
var result = [];
iter([], 0, 0);
return result;
}
console.log(maxima([7200, 7050, 7300, 7500, 7440, 7200, 7300, 7280, 7400], 50));
.as-console-wrapper { max-height: 100% !important; top: 0; }
I know you already have an answer, but I would like to propose a possible O(n) solution. The idea is that you monitor direction of price movement along with local min and max. You define a change in direction anytime the price changes direction by more than Y from the local min or max. You buy and sell on direction changes.
var A = [6000, 7200, 7050, 7040, 7045, 7041, 7039, 7300, 7500, 7490, 7480, 7501, 7440, 7200, 7300, 7280, 7400];
var A = [7200, 7050, 7300, 7500, 7440, 7200, 7300, 7280, 7400];
let Y = 50
function buysell(A, Y) {
let direction = -1
let min = A[0]
let max = 0
let total = 0
for (let i = 1; i < A.length; i++) {
if (direction == -1) {
if (A[i] < min) min = A[i]
if (A[i] - min > Y) { // only change direction if change is greater than Y
direction = 1;
max = A[i]
console.log('buy at', min)
}
} else { // price is going up
if (A[i] > max) max = A[i]
if (max - A[i] > Y) {
total += max - min - Y
console.log('sell at ', max)
min = A[i]
direction = -1
}
}
}
// buy at end if price was going up
if (direction == 1) {
console.log('sell at ', max)
total += max - min - Y
}
return total
}
console.log("total: ", buysell(A, Y))
// Test with some edge cases:
var A = [6000, 7200,7050, 7040, 7045, 7041, 7039,7040, 7300,7500, 7490, 7480,7501, 7440,7200,7300,7280,7400];
console.log("total: ", buysell(A, Y))
var A = [ 7172, 2477, 4755, 2297, 2893, 8863 ]
console.log("total: ", buysell(A, Y))
(I believe Mark_M's answer is the best here but I'll leave mine just for completeness.)
For each selling price we'd like to know the best buying price before it so we can pair that sale with the maximum accumulated before that. We can have an O(n^2) algorithm since we have to traverse back anyway.
function f(A, Y){
let m = [0].concat(new Array(A.length - 1));
for (let i=1; i<A.length; i++){
let smallest = A[i-1];
m[i] = m[i - 1];
for (let j=i-1; j>0; j--){
smallest = Math.min(smallest, A[j]);
if (smallest < A[i] + Y)
m[i] = Math.max(m[i], A[i] - smallest - Y + (m[j - 1] || 0));
}
}
return m[m.length - 1];
}
var a = [7200,7050,7300,7500,7440,7200,7300,7280,7400];
console.log(f(a, 50));
Related
I'm trying to implement a function that takes a string as input and returns the longest palindrome subsequence in the string.
I've tried using dynamic programming and have come up with the following code:
function longestPalindromicSubsequence(str) {
let n = str.length;
let dp = Array(n);
for (let i = 0; i < n; i++) {
dp[i] = Array(n);
dp[i][i] = 1;
}
for (let cl = 2; cl <= n; cl++) {
for (let i = 0; i < n - cl + 1; i++) {
let j = i + cl - 1;
if (str[i] === str[j] && cl === 2)
dp[i][j] = 2;
else if (str[i] === str[j])
dp[i][j] = dp[i + 1][j - 1] + 2;
else
dp[i][j] = Math.max(dp[i][j - 1], dp[i + 1][j]);
}
}
return dp[0][n - 1];
}
However, this code doesn't seem to be giving me efficient and better results for all test cases. The Time and Space Complexity is also be reduced. I've been struggling with this for days and can't seem to find the issue. Can someone help me figure out what's going wrong and how to fix it?
Oh, I think Dynamic Programming does not work with this sort of problem, because it does not break down recursively, i.e. to find the longest palindrome in a string, you don't need all second-largest palindromes. You can just check at each position and see if it is the center of a palindrome longer than any before. This can be solved with a greedy algorithm:
const pals = "asd1234321fghjkl1234567887654321qwertzu1234321"
function palindromeAtPos(str, pos, checkEven = false){
let ix = 0
const off = checkEven ? 2 : 1
while(pos-ix-1 >= 0 && pos+ix+1+off < str.length && str[pos-ix-1] === str[pos+ix+off]){
ix++
}
return ix === 0 ? str[pos] : str.substring(pos-ix, pos+ix+off)
}
function longestPalindrome(str){
let longest = ''
for(let i = 1; i < str.length; i++){
const odd = palindromeAtPos(str, i)
longest = odd.length > longest.length ? odd : longest
const even = palindromeAtPos(str, i, true)
longest = even.length > longest.length ? even : longest
}
return longest
}
console.log(longestPalindrome(pals))
On paper (and for a string like aaaaaaaaaa), this has quadratic complexity, but for most strings, it will be almost linear.
/*
* s => string
* return [] of strings witch have the max lenth
*/
function maxLenPalindromes(s) {
const l = s.length
let c, z, zz, a, b, a1, b1, maxl = 0, result = []
if (l < 2) return result
for (c = 0; c < l - 1; c++) {
a = -1
if (maxl>(l-c)*2+1) return result
if (c > 0 && s[c - 1] == s[c + 1]) {
zz = Math.min(c, l - c - 1)
for (z = 1; z <= zz; z++) {
if (s[c - z] != s[c + z]) {
a = c - z + 1; b = c + z
break
}
else if (z == zz) {
a = c - z; b = c + z + 1
break
}
}
if (a >= 0) {
if (b-a > maxl) {
result = [s.slice(a, b)]
maxl = b-a
}
else if (b-a == maxl) {
result.push(s.slice(a, b))
}
}
}
a=-1
if (s[c] == s[c + 1]) {
if (c == 0 || c == l - 2) {
a = c; b = c + 2
}
else {
zz = Math.min(c, l - c - 2)
for (z = 1; z <= zz; z++) {
if (s[c - z] != s[c + z + 1]) {
a = c - z + 1; b = c + z + 1
break
}
else if (z == zz) {
a = c - z; b = c + z + 2
break
}
}
}
if (a >= 0) {
if (b-a > maxl) {
result = [s.slice(a, b)]
maxl = b-a
}
else if (b-a == maxl) {
result.push(s.slice(a, b))
}
}
}
}
return result
}
const s1="112233111222333"
const s2="11_22_33_111_222_333"
const s3="12345_54321xqazws_swzaq_qwertytrewq"
const s4="sdfgsdfg1qqqqqAAAAA_123456789o o987654321_AAAAAqqqqq;lakdjvbafgfhfhfghfh"
console.log(maxLenPalindromes(s1))
console.log(maxLenPalindromes(s2))
console.log(maxLenPalindromes(s3))
console.log(maxLenPalindromes(s4))
I am trying to solve the Fibonacci algorithm using matrices. My target time complexity is an o(logn) instead of an o(n). The return output of the program is not the number for the series but the sixth significant digits. Its why I am returning the remainder of the solution divided by a million.
I have written the code and it runs well but I noticed that for extremely large inputs, I get a NAN(not a number) instead of an output
const fib = (n) => {
let fibMatrix = [[1,1], [1,0]]
if(n == 0){
return 0;
}
raiseToPower(fibMatrix, n - 1);
return Math.floor(fibMatrix[0][0] % 1000000)
}
const raiseToPower = (matrix, n) => {
if(n == 0 || n == 1){
return;
}
let newMatrix = [[1,1], [1,0]]
raiseToPower(matrix, Math.floor(n / 2))
multiplyMatrices(matrix, matrix)
if(n % 2 !== 0){
multiplyMatrices(matrix, newMatrix)
}
}
const multiplyMatrices = (matrix, newMatrix) => {
let x = matrix[0][0]*newMatrix[0][0] + matrix[0][1]*newMatrix[1][0];
let y = matrix[0][0]*newMatrix[0][1] + matrix[0][1]*newMatrix[1][1];
let z = matrix[1][0]*newMatrix[0][0] + matrix[1][1]*newMatrix[1][0];
let w = matrix[1][0]*newMatrix[0][1] + matrix[1][1]*newMatrix[1][1];
matrix[0][0] = x;
matrix[0][1] = y;
matrix[1][0] = z;
matrix[1][1] = w;
}
console.log(fib(2000))
Thats my code above. Is there anything I could change to actually make this much more performant?
I actually found the error. My numbers were getting larger than the maximum value.
I changed this by instead returning the remainder of my value divided by a million to the matrix and then returning the value from the matrix instead of returning the value and then dividing by a million. The former is efficient and works for any sized inputs.
const fib = (n) => {
let fibMatrix = [[1,1], [1,0]]
if(n == 0){
return 0;
}
raiseToPower(fibMatrix, n - 1);
return (fibMatrix[0][0])
}
const raiseToPower = (matrix, n) => {
if(n == 0 || n == 1){
return;
}
let newMatrix = [[1,1], [1,0]]
raiseToPower(matrix, Math.floor(n / 2))
multiplyMatrices(matrix, matrix)
if(n % 2 !== 0){
multiplyMatrices(matrix, newMatrix)
}
}
const multiplyMatrices = (matrix, newMatrix) => {
let x = (matrix[0][0]*newMatrix[0][0] + matrix[0][1]*newMatrix[1][0]);
let y = (matrix[0][0]*newMatrix[0][1] + matrix[0][1]*newMatrix[1][1]);
let z = (matrix[1][0]*newMatrix[0][0] + matrix[1][1]*newMatrix[1][0]);
let w = (matrix[1][0]*newMatrix[0][1] + matrix[1][1]*newMatrix[1][1]);
matrix[0][0] = x % 1000000;
matrix[0][1] = y % 1000000;
matrix[1][0] = z % 1000000;
matrix[1][1] = w % 1000000;
}
console.log(fib(10000))
I am working through a two-sum problem where I pass in an unsorted array, and a target, k, and I return the the highest sum of any two numbers that are less than k. If there's no possible sum less than k, then return -1.
I think I am on the right path by sorting the array and then using a 2-pointer technique but I am stuck now. If my sum of numbers is greater than the target, then I decrement the end pointer...that seems definitive. The else though, I am not sure if I am doing correctly.
var twoSumLessThanK = function(nums, k) {
// [1,8,23,23,33,34,54,75] 60
nums.sort((a, b) => a - b)
let start = 0;
let end = 0;
let max = -1;
while (start < end) {
if (nums[start] + nums[end] >= k) {
end--
} else if (nums[start] + nums[end] < k) {
max = Math.max(max, nums[start] + nums[end])
start++
}
}
return max;
};
console.log(twoSumLessThanK([1,8,23,23,33,34,54,75], 60));
You could check the sum of two values and decrement the right index if greater or equal than k or store the sum, if greater than the max value and increment the left index.
1 8 23 23 33 34 54 75 sum max < 60
> < 76
> < 55 -> max
> < 62
> < 42
> < 57 -> max
> < 57
> < 67
const
twoSumLessThanK = function(nums, k) {
nums.sort((a, b) => a - b);
let left = 0,
right = nums.length -1,
max = -Number.MAX_VALUE;
while (left < right) {
let sum = nums[left] + nums[right];
if (sum >= k) {
right--;
continue;
}
if (max < sum) max = sum;
left++;
}
return max;
};
console.log(twoSumLessThanK([1, 8, 23, 23, 33, 34, 54, 75], 60));
An alternative could be a nested for loop, this way you do't have to handle start and end manually
const twoSumLessThanK = function(nums, k)
{
let max = -1;
const len = nums.length - 1
for (let start = 0; start < len; start++)
{
for (let end = len; end > start; end--)
{
if (nums[start] + nums[end] < k) max = Math.max(max, nums[start] + nums[end])
}
}
return max;
};
console.log(twoSumLessThanK([1,8,23,23,33,34,54,75], 60)); // Logs 57
A different option would be the following:
const twoSumLessThanK = function(nums, k)
{
nums.sort((a, b) => a - b)
let max = -1;
let greatest = null
while (nums.length > 1 && max < k - 1)
{
greatest = nums.pop()
for (let i = nums.length - 1; i >= 0; i--)
{
if (greatest + nums[i] < k)
{
max = Math.max(max, greatest + nums[i])
break
}
}
}
return max;
};
Or you can just fix your version if you want:
const twoSumLessThanK = function(nums, k)
{
// [1,8,23,23,33,34,54,75] 60
nums.sort((a, b) => a - b)
const len = nums.length - 1
let start = 0;
let max = -1;
let end = len;
while (start < len)
{
if (nums[start] + nums[end] < k)
{
max = Math.max(max, nums[start] + nums[end])
start++
end = len;
}
else end--
}
return max;
};
it wasn't so hard to code...?
const twoSumLessThanK = (nums, k) =>
{
let max = -1
, arr = nums.reduce((a,c)=> // decrase order values < k
{
if (c < k)
{
let p = a.findIndex(x=>x < c)
if (p<0) p = a.length
a.splice( p,0,c)
}
return a
}
,[])
;
if (arr.length<2) return max
for(i=arr.length;--i;)
for(j=i;j--;)
{
let sum = arr[i] + arr[j]
if (sum >= k && i === j+1) return max
if (sum < k && sum > max ) max = sum
}
return max
}
console.log(twoSumLessThanK([33,1,8,23,23,34,54,75], 60))
I have some question and Maybe One of you can help me,
I have a float array and I won't find the closest float value to the target number(float)
I use a simple binary Search With javascript And from here I got stuck.
function binarySearch(arr, val, start = 0, end = arr.length - 1) {
const mid = Math.floor((start + end) / 2);
if(Math.abs(val - arr[mid]) < Math.abs(val - arr[mid + 1])){
return mid;
}
if (start >= end) {
return -1;
}
return val < arr[mid]
? binarySearch(arr, val, start, mid - 1)
: binarySearch(arr, val, mid + 1, end);
}
let array = [0,0.03336667683886884,0.06673335367773768,0.10010003051660653,0.13346670735547536,0.1668333841943442,0.20020006103321306]
let result = binarySearch(array,'0.166833') //=> This value needs to be returned 0.1668333841943442
Your condition Math.abs(val - arr[mid]) < Math.abs(val - arr[mid + 1]) is wrong, you are always checking the left value is closer than the right value, so even for value 1 in array [10, 20, 30, 40], when you check number 20, 1 is closer to 20 than to 30 - incorrect result is then returned.
Also, you need to check the edge cases, the index mid + 1 may not be available. Also the value may be smaller than the minimum or larger than the maximum, so an infinite loop may occur.
Let me offer you this solution instead:
function binarySearch(arr, val, start, end) {
// edge case: value of smaller than min or larger than max
if(array[0] >= val) return 0;
if(array[array.length - 1] <= val) return array.length - 1;
while (start <= end) {
let mid = Math.floor((end + start) / 2);
// value is in interval from previous to current element
if(val >= arr[mid - 1] && val <= arr[mid]) {
return Math.abs(val - arr[mid - 1]) < Math.abs(val - arr[mid]) ? mid - 1 : mid;
}
else {
if(arr[mid] < val) {
start = mid + 1;
}
else {
end = mid - 1;
}
}
}
return -1;
}
Your stop condition is broken. In your example, if you feed the following array:
[0.3, 0.5, 0.8, 2.0]
You have at initiation:
arr[mid] = 0.5
arr[mid + 1] = 0.8
So if you feed val = 0.1 to your algorithm, you will have:
Math.abs(val - arr[mid]) = 0.4
Math.abs(val - arr[mid + 1]) = 0.7
Therefore, you will return the index of 0.5 although you were expecting the index of 0.3.
A pseudo idea to solve your problem would be:
if array contains 1 element => return element
else if array contains 2 elements => return element closest to val.
else if val < array[mid] => recurse on first half
else if val > array[mid] => recurse on second half
It looks you have a typo. You should not return mid index, but item of array from array[mid]:
function binarySearch(arr, val, start = 0, end = arr.length - 1) {
const mid = Math.floor((start + end) / 2);
if(Math.abs(val - arr[mid]) < Math.abs(val - arr[mid + 1])){
return arr[mid];
}
if (start >= end) {
return -1;
}
return val < arr[mid]
? binarySearch(arr, val, start, mid - 1)
: binarySearch(arr, val, mid + 1, end);
}
let array = [0, 0.03336667683886884, 0.06673335367773768, 0.10010003051660653,
0.13346670735547536, 0.1668333841943442, 0.20020006103321306];
console.log(binarySearch(array,'0.166833')) // 0.1668333841943442
I found this solution but I need to return the index of The right value
function binarySearch(arr, target, lo = 0, hi = arr.length - 1) {
if (target < arr[lo]) {return arr[0]}
if (target > arr[hi]) {return arr[hi]}
const mid = Math.floor((hi + lo) / 2);
return hi - lo < 2
? (target - arr[lo]) < (arr[hi] - target) ? arr[lo] : arr[hi]
: target < arr[mid]
? binarySearch(arr, target, lo, mid)
: target > arr[mid]
? binarySearch(arr, target, mid, hi)
: arr[mid]
}
let array = [0, 0.03336667683886884, 0.06673335367773768, 0.10010003051660653,0.13346670735547536, 0.1668333841943442, 0.20020006103321306,30.01];
console.log(binarySearch(array,0.03336))
I have Float32Array textures which can be displayed through WebGL correctly. However, when I tried to convert them into Uint16Array, the problem occurs.
Here is my conversion part.
var _floatToHalfFloat = function(input, offset) {
var largestHalf = Math.pow(2, 30-15) * (1 + 1023/1024);
var m = new ArrayBuffer(4);
var n = new Float32Array(m);
var o = new Uint32Array(m);
var f = 0.0;
for (var i = input.length - 1 - offset; i >= 0;i--) {
n[0] = input[i];
f = o[0];
// fast conversion of half
// ref : ftp://www.fox-toolkit.org/pub/fasthalffloatconversion.pdf
if (isNaN(input[i])) {
input[i] = 0x7fff;
} else if(n === Infinity || n > largestHalf) {
input[i] = 0x7c00;
} else if(n === -Infinity || n < -largestHalf) {
input[i] = 0xfc00;
} else if(n === 0) {
input[i] = 0;
} else {
input[i] = ((f>>16)&0x8000)|((((f&0x7f800000)-0x38000000)>>13)&0x7c00)|((f>>13)&0x03ff);
}
}
return new Uint16Array(input);
};
We can see saturated colors (full red, green and/or blue) in the converted image when reaching black color in the original image. I think the function doesn't work very well near 0.
I have done a quick implementation of wikipedia explanation of norm of the float 16 bits.
<html>
<head>
<script>
var _floatToHalfFloat = #### YOUR FUNCTION HERE CUT ####
var _halfFloatToFloat = function(hf) {
var m = new ArrayBuffer(2);
var n = new Uint16Array(m);
n[0] = hf;
var sign = n[0] & 0x8000;
var exp = (n[0] >> 10) & 0x1F;
var mant = n[0]& 0x03FF;
document.getElementById('sign').innerHTML += sign+" - ";
document.getElementById('exp').innerHTML += exp+" - ";
document.getElementById('mant').innerHTML += mant+" - ";
if (exp == 0x1F) {
return 1.0 * Math.pow(-1, sign) * Infinity;
} else if (exp == 0) {
return Math.pow(-1, sign) *
Math.pow(2, -14) *
(mant / Math.pow(2, 10));
} else {
return Math.pow(-1, sign) *
Math.pow(2, exp-15) *
(1+(mant / Math.pow(2, 10)));
}
};
document.addEventListener("DOMContentLoaded", function(event) {
var input = new Float32Array(8);
input[0] = 2.5;
input[1] = 0.25;
input[2] = 0.025;
input[3] = 0.025;
input[4] = 0.0025;
input[5] = 0.00025;
input[6] = 0.000025;
input[7] = 0.0;
var i, s = "Value before = ";
for (i = 0; i < input.length; i++)
s += input[i] + " - ";
document.getElementById('res1').innerHTML = s;
var output = _floatToHalfFloat(input, 0);
s = "Value after = ";
for (i = 0; i < output.length; i++)
s += _halfFloatToFloat(output[i]) + " - ";
document.getElementById('res2').innerHTML = s;
});
</script>
</head>
<body>
<span id="res1">result</span></br>
<span id="res2">result</span></br>
</br></br></br>
<span id="sign">signs =</span></br>
<span id="exp">exponents =</span></br>
<span id="mant">mantissas =</span></br>
</body>
</html>
The test results are shown below :
Value before = 2.5 - 0.25 - 0.02500000037252903 - 0.02500000037252903 - 0.0024999999441206455 - 0.0002500000118743628 - 0.00002499999936844688 - 0 -
Value after = 2.5 - 0.25 - 0.024993896484375 - 0.024993896484375 - 0.002498626708984375 - 0.0002498626708984375 - Infinity - 2 -
signs =0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 -
exponents =16 - 13 - 9 - 9 - 6 - 3 - 31 - 16 -
mantissas =256 - 0 - 614 - 614 - 286 - 24 - 653 - 0 -
This shows that the 2 last information are not coherent. 0.000025 is transformed into Infinity (rather than 0?) and 0 itself is transformed to 2. This doesn't appear to be correct. When you want to code a zero "wikipedia says" your mantissa AND your exponent should be zero. In the code you provided the mantissa is zero but the exponent is 16 which leads to 2 (2^(16-15)).
After tweaking a bit your function it appears that all cases are treated as normal one. This is due to a bug in your if statements. So instead of having :
} else if(n === 0) {
input[i] = 0;
}
You want probably do something like that :
} else if(n[0] === 0) {
input[i] = 0;
}
And the same for all uses of n variable. But you still have the underflow problem.So may be you can find acceptable to do :
} else if(Math.abs(n[0]) < 0.0001) {
input[i] = 0;
}