如何在 javascript 中取消嵌套此递归算法?
How to un-nest this recursive algorithm in javascript?
在游戏idle heroes中,妖铃可以是1,2,3,4级。 4级由4级1组成,3级由3级1组成,依此类推。
我想找到给定固定数字的数据库的所有排列。我在 javascript:
中做了这个递归算法
更简单的方法更接近:
function findDB(numDB, arr) {
console.log("findDB"+numDB);
if (numDB == 1) {
console.log("HERE2");
return 1;
} else {
for (let i = 1; i < numDB; i++) {
console.log("FOR"+i);
console.log("COND" +(numDB + (numDB-i)));
if((numDB + (numDB-i)) > numDB+1)
continue;
arr= arr.concat([numDB,[i,findDB(numDB - i, arr)]]);
}
return arr;
}
}
var final = []
var y = findDB(3, final);
console.log(JSON.stringify(y));
输出:
findDB(2) 正确!
findDB2
FOR1
COND3
findDB1
HERE2
[2,[1,1]]
FindDB(3) 缺少 1,1,1,
findDB3
FOR1
COND5
FOR2
COND4
findDB1
HERE2
[3,[2,1]]
这里是输入 1 到 6 的预期输出(算法需要缩放任何数字输入)
/1/ (1)
/2/ (2),
(1,1)
/3/ (3),
(2,1),
(1,1,1)
/4/ (4),
(3,1),
(2,2),(2,1,1),
(1,1,1,1)
/5/ (4,1),
(3,2),(3,1,1),
(2,2,1),(2,1,1,1),
(1,1,1,1,1)
/6/ (4,2),(4,1,1),
(3,3),(3,2,1),(3,1,1,1),
(2,2,2),(2,2,1,1),(2,1,1,1,1)
(1,1,1,1,1,1)
这是一个递归函数,可以产生你想要的结果。它尝试将输入 (numDB
) 分解为最大数量的部分 (maxDB
,默认为 4
)。它通过将数字从 numDB
下降到 1
并将递归调用的所有可能结果添加到该数字来实现,请注意 maxDB
的值必须更改为 no多于第一个数字。
const findDB = function(numDB, maxDB = 4) {
if (numDB == 0) return [ [] ];
let result = [];
let thisDB = Math.min(numDB, maxDB);
for (let i = thisDB; i > 0; i--) {
findDB(numDB - i, Math.min(i, thisDB)).forEach(n => {
result.push([i].concat(n));
});
}
return result;
}
;
[6, 5, 4, 3, 2, 1].forEach((i) => console.log(JSON.stringify(findDB(i))))
.as-console-wrapper {
min-height: 100% !important;
}
我把上面的函数写成你问题中的样式,使用各种ES6 Array方法可以简化:
const DBlist = (n) => [...Array(n).keys()].map(k => n - k)
const findDB = (numDB, maxDB = 4) => {
if (numDB == 0) return [ [] ];
const thisDB = Math.min(numDB, maxDB);
return DBlist(thisDB).flatMap((i) => findDB(numDB - i, Math.min(i, thisDB)).map(a => [i, ...a]))
}
DBlist(6).forEach((n) => console.log(JSON.stringify(findDB(n))))
.as-console-wrapper {
min-height: 100% !important;
}
这称为 partitions of a number,是一个 well-known 问题。我敢肯定计算机科学家有比这更有效的算法,但朴素的递归版本可能如下所示:
const partitions = (n, m = n) =>
m > n
? partitions (n, n)
: m == 1
? [Array (n) .fill (1)]
: m < 1
? [[]]
: [
... partitions (n - m, m) .map (p => [m, ...p]),
... partitions (n, m - 1)
];
[1, 2, 3, 4, 5, 6] .forEach ((n) => console .log (`${n}: ${JSON .stringify (partitions (n))}`))
而且如果您担心默认参数(有时 good reasons 需要担心),那么您可以将其设为辅助函数并将其包装在 public 函数中,如下所示:
const _partitions = (n, m) =>
m > n
? _partitions (n, n)
: m == 1
? [Array (n) .fill (1)]
: m < 1
? [[]]
: [
... _partitions (n - m, m) .map (p => [m, ...p]),
... _partitions (n, m - 1)
];
const partitions = (n) => _partitions (n, n);
[1, 2, 3, 4, 5, 6] .forEach ((n) => console .log (`${n}: ${JSON .stringify (partitions (n))}`))
在任何一种情况下,n
是我们求和的整数,m
是我们可以使用的最大整数。如果 m
太大,我们只需使用适当的 m
再次调用。如果它等于 1
,那么我们只能有一个 n
1
的数组。如果 m
达到零,那么我们只有空分区。最后,我们有两个递归情况要组合:当我们选择使用那个最大数时,我们用余数和那个最大值递归,将最大值放在每个结果之前。当我们不使用最大值时,我们会以相同的目标值和递减的最大值重复出现。
我觉得这个案例太多了,但我没有立即看到如何将它们组合起来。
时间是指数级的,而且无论如何都会是指数级的,因为结果在 n
的大小上是指数级的。如果我们添加记忆,我们真的可以加快速度,但我把它留作练习。
更新
我被那些额外的案例所困扰,并找到了一个显示更简单版本的 Erlang answer。转换为 JS,它可能是这样的:
const countdown = (n) => n > 0 ? [n , ...countdown (n - 1)] : []
const _partitions = (n, m) =>
n < 0
? []
: n == 0
? [[]]
: countdown (m) .flatMap (x => _partitions (n - x, x) .map (p => [x, ...p]))
我们有一个快速帮手,countdown
可以将 5
变成 [5, 4, 3, 2, 1]
。 main 函数有两个基本情况,如果 n
为负,则结果为空;如果 n
为零,则结果仅包含空分区。否则,我们对单个分区中最大值的可能性进行倒计时,并在小于这个新最大值的目标分区上重复,将最大值添加到每个分区的前面。
这应该具有与上述类似的性能特征,但更简单一些。
在游戏idle heroes中,妖铃可以是1,2,3,4级。 4级由4级1组成,3级由3级1组成,依此类推。
我想找到给定固定数字的数据库的所有排列。我在 javascript:
中做了这个递归算法更简单的方法更接近:
function findDB(numDB, arr) {
console.log("findDB"+numDB);
if (numDB == 1) {
console.log("HERE2");
return 1;
} else {
for (let i = 1; i < numDB; i++) {
console.log("FOR"+i);
console.log("COND" +(numDB + (numDB-i)));
if((numDB + (numDB-i)) > numDB+1)
continue;
arr= arr.concat([numDB,[i,findDB(numDB - i, arr)]]);
}
return arr;
}
}
var final = []
var y = findDB(3, final);
console.log(JSON.stringify(y));
输出:
findDB(2) 正确!
findDB2
FOR1
COND3
findDB1
HERE2
[2,[1,1]]
FindDB(3) 缺少 1,1,1,
findDB3
FOR1
COND5
FOR2
COND4
findDB1
HERE2
[3,[2,1]]
这里是输入 1 到 6 的预期输出(算法需要缩放任何数字输入)
/1/ (1)
/2/ (2),
(1,1)
/3/ (3),
(2,1),
(1,1,1)
/4/ (4),
(3,1),
(2,2),(2,1,1),
(1,1,1,1)
/5/ (4,1),
(3,2),(3,1,1),
(2,2,1),(2,1,1,1),
(1,1,1,1,1)
/6/ (4,2),(4,1,1),
(3,3),(3,2,1),(3,1,1,1),
(2,2,2),(2,2,1,1),(2,1,1,1,1)
(1,1,1,1,1,1)
这是一个递归函数,可以产生你想要的结果。它尝试将输入 (numDB
) 分解为最大数量的部分 (maxDB
,默认为 4
)。它通过将数字从 numDB
下降到 1
并将递归调用的所有可能结果添加到该数字来实现,请注意 maxDB
的值必须更改为 no多于第一个数字。
const findDB = function(numDB, maxDB = 4) {
if (numDB == 0) return [ [] ];
let result = [];
let thisDB = Math.min(numDB, maxDB);
for (let i = thisDB; i > 0; i--) {
findDB(numDB - i, Math.min(i, thisDB)).forEach(n => {
result.push([i].concat(n));
});
}
return result;
}
;
[6, 5, 4, 3, 2, 1].forEach((i) => console.log(JSON.stringify(findDB(i))))
.as-console-wrapper {
min-height: 100% !important;
}
我把上面的函数写成你问题中的样式,使用各种ES6 Array方法可以简化:
const DBlist = (n) => [...Array(n).keys()].map(k => n - k)
const findDB = (numDB, maxDB = 4) => {
if (numDB == 0) return [ [] ];
const thisDB = Math.min(numDB, maxDB);
return DBlist(thisDB).flatMap((i) => findDB(numDB - i, Math.min(i, thisDB)).map(a => [i, ...a]))
}
DBlist(6).forEach((n) => console.log(JSON.stringify(findDB(n))))
.as-console-wrapper {
min-height: 100% !important;
}
这称为 partitions of a number,是一个 well-known 问题。我敢肯定计算机科学家有比这更有效的算法,但朴素的递归版本可能如下所示:
const partitions = (n, m = n) =>
m > n
? partitions (n, n)
: m == 1
? [Array (n) .fill (1)]
: m < 1
? [[]]
: [
... partitions (n - m, m) .map (p => [m, ...p]),
... partitions (n, m - 1)
];
[1, 2, 3, 4, 5, 6] .forEach ((n) => console .log (`${n}: ${JSON .stringify (partitions (n))}`))
而且如果您担心默认参数(有时 good reasons 需要担心),那么您可以将其设为辅助函数并将其包装在 public 函数中,如下所示:
const _partitions = (n, m) =>
m > n
? _partitions (n, n)
: m == 1
? [Array (n) .fill (1)]
: m < 1
? [[]]
: [
... _partitions (n - m, m) .map (p => [m, ...p]),
... _partitions (n, m - 1)
];
const partitions = (n) => _partitions (n, n);
[1, 2, 3, 4, 5, 6] .forEach ((n) => console .log (`${n}: ${JSON .stringify (partitions (n))}`))
在任何一种情况下,n
是我们求和的整数,m
是我们可以使用的最大整数。如果 m
太大,我们只需使用适当的 m
再次调用。如果它等于 1
,那么我们只能有一个 n
1
的数组。如果 m
达到零,那么我们只有空分区。最后,我们有两个递归情况要组合:当我们选择使用那个最大数时,我们用余数和那个最大值递归,将最大值放在每个结果之前。当我们不使用最大值时,我们会以相同的目标值和递减的最大值重复出现。
我觉得这个案例太多了,但我没有立即看到如何将它们组合起来。
时间是指数级的,而且无论如何都会是指数级的,因为结果在 n
的大小上是指数级的。如果我们添加记忆,我们真的可以加快速度,但我把它留作练习。
更新
我被那些额外的案例所困扰,并找到了一个显示更简单版本的 Erlang answer。转换为 JS,它可能是这样的:
const countdown = (n) => n > 0 ? [n , ...countdown (n - 1)] : []
const _partitions = (n, m) =>
n < 0
? []
: n == 0
? [[]]
: countdown (m) .flatMap (x => _partitions (n - x, x) .map (p => [x, ...p]))
我们有一个快速帮手,countdown
可以将 5
变成 [5, 4, 3, 2, 1]
。 main 函数有两个基本情况,如果 n
为负,则结果为空;如果 n
为零,则结果仅包含空分区。否则,我们对单个分区中最大值的可能性进行倒计时,并在小于这个新最大值的目标分区上重复,将最大值添加到每个分区的前面。
这应该具有与上述类似的性能特征,但更简单一些。