开发者问题收集

是否有更好的方法来递归执行 shuffle 方法而不超出大型数组的最大调用堆栈?

2019-10-31
517

我正在尝试编写一个 RECURSIVE 函数来随机化/打乱数组。

我编写的函数使用 Fisher-Yates 打乱方法,该方法在小型数组上运行良好,但在包含 5000 个元素的目标数组上出现“最大调用堆栈超出错误”

我想知道是否有人可以帮助我修复此方法,以便它仍然在较大的数组上递归工作?

以下是函数:

shuffleArray = (array, currentIndex=0) => {
    if (currentIndex >= array.length) {
      return array;
    }

    let randomIndex = Math.floor(Math.random() * currentIndex);

    let tempValue = array[currentIndex];
    array[currentIndex] = array[randomIndex];
    array[randomIndex] = tempValue;
    let newIndex = currentIndex += 1;
    return this.shuffleArray(array, newIndex);
  }

console.log(shuffleArray([1, 2, 3, 4, 5, 6]));
// returns a random array like [3,4,6,1,2,5]

console.log(shuffleArray([...Array(5000).keys()]));
// an example array of 5000 elements returns error: Uncaught RangeError: Maximum call stack size exceeded
2个回答

任何像这样的循环:

for (let i = 0; i < n; i++) {
    // do something with i
}

可以毫无意义地但递归地扩展到 O(log n) 空间:

function foo(start, end) {
    if (start + 1 === end) {
        // do something with start
    } else {
        let mid = start + Math.floor((end - start) / 2);
        foo(start, mid);
        foo(mid, end);
    }
}

foo(0, n);
2019-10-31

您始终可以使用 trampolining。

// data Trampoline a where

// Result :: a -> Trampoline a
const Result = result => ({ bounce: false, result });

// Bounce :: (a -> Trampoline b) -> a -> Trampoline b
const Bounce = func => (...args) => ({ bounce: true, func, args });

// trampoline :: Trampoline a -> a
const trampoline = t => {
    while (t.bounce) t = t.func(...t.args);
    return t.result;
};

// _shuffleArray :: ([a], Int) -> Trampoline [a]
const _shuffleArray = Bounce((array, currentIndex) => {
    if (currentIndex >= array.length) return Result(array);
    let randomIndex = Math.floor(Math.random() * currentIndex);
    [array[currentIndex], array[randomIndex]] = [array[randomIndex], array[currentIndex]];
    return _shuffleArray(array, currentIndex + 1);
});

// shuffleArray :: [a] -> [a]
const shuffleArray = array => trampoline(_shuffleArray(array, 0));

// Shuffle 100k numbers without a stack overflow.
console.log(shuffleArray([...Array(1e5).keys()]));

这可让您将任何递归函数展平为迭代循环。

Aadit M Shah
2019-10-31