&引用;“Javascript内存不足”;使用Lodash记忆

&引用;“Javascript内存不足”;使用Lodash记忆,javascript,algorithm,recursion,lodash,memoization,Javascript,Algorithm,Recursion,Lodash,Memoization,我试图通过将记忆应用到递归解决方案中,使用Javascript解决LeetCode的问题。下面是递归解决方案,longestPalindromicSubsequence.js: function longestPalindromicSubsequence(string, start = 0, end = string.length) { if (end < start) { return 0; } if (start === end) { return 1; } if (str

我试图通过将记忆应用到递归解决方案中,使用Javascript解决LeetCode的问题。下面是递归解决方案,
longestPalindromicSubsequence.js

function longestPalindromicSubsequence(string, start = 0, end = string.length) {
  if (end < start) { return 0; }
  if (start === end) { return 1; }
  if (string[start] === string[end]) {
    return 2 + longestPalindromicSubsequence(string, start + 1, end - 1);
  }
  return Math.max(
    longestPalindromicSubsequence(string, start + 1, end),
    longestPalindromicSubsequence(string, start, end - 1),
  );
}

module.exports = longestPalindromicSubsequence;
const longestPalindromicSubsequence = require('./longestPalindromicSubsequence');

describe('longest palindromic subsequence', () => {
  test('works for aab', () => {
    expect(longestPalindromicSubsequence('aab')).toBe(2);
  });

  test('works for long string', () => {
    expect(longestPalindromicSubsequence(`${'a'.repeat(50)}bcdef`)).toBe(50);
  });
});
const _ = require('lodash');

const longestPalindromicSubsequence = _.memoize(
  (string, start = 0, end = string.length) => {
    if (end < start) { return 0; }
    if (start === end) { return 1; }
    if (string[start] === string[end]) {
      return 2 + longestPalindromicSubsequence(string, start + 1, end - 1);
    }
    return Math.max(
      longestPalindromicSubsequence(string, start + 1, end),
      longestPalindromicSubsequence(string, start, end - 1),
    );
  },
  (string, start, end) => [string, start, end], // resolver function
);

module.exports = longestPalindromicSubsequence;
这是可行的,但由于递归调用的数量呈指数级增长,速度相当慢。例如,对于长度约为50的字符串,需要9秒:

$ jest longestPalindromicSubsequence.test.js
 PASS  ./longestPalindromicSubsequence.test.js (9.6s)
  longest palindromic subsequence
    ✓ works for aab (3ms)
    ✓ works for long string (9315ms)

Test Suites: 1 passed, 1 total
Tests:       2 passed, 2 total
Snapshots:   0 total
Time:        10.039s
Ran all test suites matching /longestPalindromicSubsequence.test.js/i.
为了提高性能,我尝试在更新的模块
longestPalindromicSubsequence2.js中使用:

function longestPalindromicSubsequence(string, start = 0, end = string.length) {
  if (end < start) { return 0; }
  if (start === end) { return 1; }
  if (string[start] === string[end]) {
    return 2 + longestPalindromicSubsequence(string, start + 1, end - 1);
  }
  return Math.max(
    longestPalindromicSubsequence(string, start + 1, end),
    longestPalindromicSubsequence(string, start, end - 1),
  );
}

module.exports = longestPalindromicSubsequence;
const longestPalindromicSubsequence = require('./longestPalindromicSubsequence');

describe('longest palindromic subsequence', () => {
  test('works for aab', () => {
    expect(longestPalindromicSubsequence('aab')).toBe(2);
  });

  test('works for long string', () => {
    expect(longestPalindromicSubsequence(`${'a'.repeat(50)}bcdef`)).toBe(50);
  });
});
const _ = require('lodash');

const longestPalindromicSubsequence = _.memoize(
  (string, start = 0, end = string.length) => {
    if (end < start) { return 0; }
    if (start === end) { return 1; }
    if (string[start] === string[end]) {
      return 2 + longestPalindromicSubsequence(string, start + 1, end - 1);
    }
    return Math.max(
      longestPalindromicSubsequence(string, start + 1, end),
      longestPalindromicSubsequence(string, start, end - 1),
    );
  },
  (string, start, end) => [string, start, end], // resolver function
);

module.exports = longestPalindromicSubsequence;
const=require('lodash');
const longestPalindromicSubsequence=u.memoize(
(字符串,开始=0,结束=string.length)=>{
if(end[string,start,end],//解析器函数
);
module.exports=最长的回文子序列;
但是,当我尝试使用此模块运行测试时,会出现“Javascript堆内存不足”错误:

$jest longestPalindromicSubsequence.test.js
运行。/longestPalindromicSubsequence.test.js
在[89308:0x104801e00]15800毫秒时:标记扫描1379.2(1401.3)->1379.2(1401.3)MB,1720.4/0.0毫秒(+自标记开始后5个步骤中的0.0毫秒,最大步骤0.0毫秒,自标记开始以来的壁时间1735毫秒)(平均mu=0.128,当前mu=0.057)分配[89308:0x104801e00]17606毫秒:标记扫描1390.0(1412.3)->1390.0(1412.3)MB,1711.7/0.0毫秒(+0.0毫秒,自标记开始后分4步进行,最大一步为0.0毫秒,自标记开始后的墙时间为1764毫秒)(平均mu=0.091,当前mu=0.052)分配
==JS堆栈跟踪=========================================
0:ExitFrame[pc:0x20b000bdc01d]
安全上下文:0x1c189571e549
1://*匿名*/[0x1C18F768201][/Users/kurtpeek/GoogleDrive/LeetCode/longestPalindromicSubsequence2.js:~14][pc=0x20b0015cd091](this=0x1c18d38893a1,string=0x1C18F768271,start=45,end=45)
2:已记忆的[0x1c18f7682309][/Users/kurtpeek/GoogleDrive/LeetCode/node\ux。。。
致命错误:无效的标记压缩接近堆限制分配失败-JavaScript堆内存不足
1:0x100037733节点::中止()[/usr/local/bin/node]
2:0x1000378d6节点::FatalTryCatch::~FatalTryCatch()[/usr/local/bin/node]
3:0x10018e57b v8::Utils::ReportOOMFailure(v8::internal::Isolate*,char const*,bool)[/usr/local/bin/node]
4:0x10018e51c v8::internal::v8::FatalProcessOutOfMemory(v8::internal::Isolate*,char const*,bool)[/usr/local/bin/node]
5:0x1004682ee v8::内部::堆::更新生存统计(int)[/usr/local/bin/node]
6:0x100469ed7 v8::internal::Heap::CheckIneffectiveMarkCompact(无符号长,双精度)[/usr/local/bin/node]
7:0x1004675cb v8::internal::Heap::PerformGarbageCollection(v8::internal::GarbageCollector,v8::GCCallbackFlags)[/usr/local/bin/node]
8:0x1004663e6 v8::internal::Heap::CollectGarbage(v8::internal::AllocationSpace,v8::internal::GarbageCollectionReason,v8::GCCallbackFlags)[/usr/local/bin/node]
9:0x10046eafc v8::internal::Heap::AllocateRawWithLigthRetry(int,v8::internal::AllocationSpace,v8::internal::AllocationAlignment)[/usr/local/bin/node]
10:0x10046eb48 v8::internal::Heap::AllocateRawWithRetryOrFail(int,v8::internal::AllocationSpace,v8::internal::AllocationAlignment)[/usr/local/bin/node]
11:0x10044eb7a v8::internal::Factory::NewFillerObject(int、bool、v8::internal::AllocationSpace)[/usr/local/bin/node]
12:0x100634916 v8::internal::Runtime_AllocateInTargetSpace(int,v8::internal::Object**,v8::internal::Isolate*)[/usr/local/bin/node]
13:0x20b000bdc01d
中止陷阱:6

据我所知,Node的标准内存使用量是1.7GB,我认为这应该足够了。你知道为什么记忆版本不起作用,以及如何修复它吗?

我通过将解析器函数从
(string,start,end)=>[string,start,end]
更改为
(string,start,end)解决了这个问题=>字符串+开始+结束

const _ = require('lodash');

const longestPalindromicSubsequence = _.memoize(
  (string, start = 0, end = string.length) => {
    if (end < start) { return 0; }
    if (start === end) { return 1; }
    if (string[start] === string[end]) {
      return 2 + longestPalindromicSubsequence(string, start + 1, end - 1);
    }
    return Math.max(
      longestPalindromicSubsequence(string, start + 1, end),
      longestPalindromicSubsequence(string, start, end - 1),
    );
  },
  (string, start, end) => string + start + end, // resolver function
);

module.exports = longestPalindromicSubsequence;

使用字符串作为缓存的键似乎比使用数组更节省内存-可能是因为字符串在Javascript中是不可变的?欢迎对此改进进行任何解释。

我知道您给出了最佳答案,但想补充一点说明。根本问题是使用数组这就是造成瓶颈的原因。在幕后,lodash有自己的定义,它们似乎假设字符串将被传递

但是,重新查看和注释时,它们确实会公开
缓存
对象,以供您覆盖,前提是该对象与它们的映射具有相同的接口

创建一个函数,用于记录函数的结果。如果解析器为 前提是,它根据以下内容确定用于存储结果的缓存密钥: 提供给记忆函数的参数。默认情况下,第一个 提供给memorized函数的参数用作映射缓存 func是用memonized的这个绑定调用的 功能

注意:缓存作为“已记忆”对象上的“缓存”属性公开 功能。它的创建可以通过替换 \uuuu.memoize.Cache构造函数,其实例实现了清除、删除、获取、拥有和设置的映射方法接口

我进去测试了你的代码,因为如果你想引用键作为对象/非字符串,你应该使用的实际映射是a

const _ = require('lodash');

// override Cache and use WeakMap
_.memoize.Cache = WeakMap;

const longestPalindromicSubsequence = _.memoize(
  (string, start = 0, end = string.length) => {
    if (end < start) { return 0; }
    if (start === end) { return 1; }
    if (string[start] === string[end]) {
      return 2 + longestPalindromicSubsequence(string, start + 1, end - 1);
    }
    return Math.max(
      longestPalindromicSubsequence(string, start + 1, end),
      longestPalindromicSubsequence(string, start, end - 1),
    );
  },
  (string, start, end) => [string, start, end], // resolver function
);

module.exports = longestPalindromicSubsequence;
const=require('lodash');
//覆盖缓存并使用WeakMap
_.memoize.Cache=WeakMap;
const longestPalindromicSubsequence=u.memoize(
(字符串,开始=0,结束=string.length)=>{
if(end