JavaScript - 返回最大累积利润

6

我正在做一项练习,但无法解决。我需要通过购买和销售比特币来获得最大的累计利润。我有一个函数(A,Y),它接收一个A =不同时间的价格数组和一个Y =费用。

限制:

注意:如果比特币在0时购买,并在1时出售,则会产生A[1] - A[0] = 7050-7200-Y = -200的损失。因此,该操作未执行。

注意2:您一次只能拥有1个比特币。要出售,必须先购买。要购买,您需要没有或已经出售。

注意3:动作需要按时间连续进行。您不能在A[5]购买并在A[4]出售。

注意4:如果无法获利,则应返回0

复杂度为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

这是我所拥有的

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;
}

实际上我得到的是450,但应该是550


1
有一个错误:如果我变成A.length-1,那么A [i + 1]将是A [A.length]。嗯,在Javascript中应该是未定义的。 - atmin
你如何判断局部最大值? - Nina Scholz
@NinaScholz 这个练习告诉我应该得到的值。 - Franco Manzur
我认为正确答案是Mark_M的回答。 - גלעד ברקן
3个回答

3

看起来比实际上更加复杂,因为您需要检查每个购买价格与所有可能的销售价格。

结果是一棵树,使用这种暴力方法。

这个解决方案只返回所有购买/销售价格中的最大利润。

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; }


很棒的解决方案,它确实比我想象中更复杂。 - Franco Manzur
@גלעדברקן,我可以插入一些短路。 - Nina Scholz
1
@Mark_M,这里已经很晚了,我考虑一下,也许明天我会提出一个更简短的方法。 - Nina Scholz

2

我相信Mark_M的答案是最好的,但为了完备起见,我还是留下我的翻译。

对于每个销售价格,我们想知道它之前的最佳购买价格,以便将该销售与之前的最大累积配对。由于我们必须进行回溯,所以我们可以采用O(n ^ 2)的算法。

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));


2

我知道你已经有了一个答案,但是我想提出一种可能的O(n)解决方案。这个想法是监控价格运动的方向以及本地最小值和最大值。每当价格从本地最小值或最大值改变方向超过Y时,您就定义了一个方向变化。您在方向变化时买入和卖出。

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))


似乎对于 [ 7172, 2477, 4755, 2297, 2893, 8863 ], 50 失败了。我得到的是 在2477买入,在8863卖出 => 6336,但我相信答案应该是 8863 - 50 - 2297 + 4755 - 50 - 2477 = 8744 - גלעד ברקן
谢谢 @גלעדברקן - 我觉得在改变方向时,我设置了错误的新局部最大值。我认为这次修改修复了这个问题。 - Mark

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接