返回

破解“超级丑数”之谜:轻松寻获第N个超级丑数

前端

引子:超级丑数的魅力

在数字世界中,“超级丑数”是一个令人着迷的存在。它不仅是一个正整数,而且其所有质因数都出现在一个特定的质数数组中。这种独特的性质让超级丑数成为数学和计算机科学领域的研究热点。

揭秘超级丑数的本质

质因数分解是理解超级丑数的关键。质因数分解指的是将一个数字分解成若干质数乘积的过程。对于一个超级丑数来说,其质因数必须全部存在于指定的质数数组中。例如,如果质数数组为{2, 3, 5},那么8 (2³)、15 (3 × 5)和30 (2 × 3 × 5)都是超级丑数。

动态规划:寻丑之旅

动态规划是一种强大的算法范式,它适用于解决具有重叠子问题和最优子结构的问题。在寻找超级丑数时,动态规划算法可以帮助我们避免重复计算,显著提高效率。

动态规划算法的关键在于建立一个存储已计算结果的表。该表中的每一项都代表一个特定丑数的索引。当我们需要计算第N个丑数时,我们可以先检查它是否已经存储在表中。如果是,我们可以直接返回结果;如果不是,我们可以根据前面计算的丑数来推导出它。

C++实现:代码中的优雅

为了进一步理解动态规划算法在寻找超级丑数中的应用,让我们来看一个C++实现的示例:

vector<int> superUglyNumbers(int n, vector<int>& primes) {
    vector<int> ugly(n);
    vector<int> idx(primes.size(), 0);
    ugly[0] = 1;
    for (int i = 1; i < n; i++) {
        ugly[i] = *min_element(idx.begin(), idx.end());
        for (int j = 0; j < primes.size(); j++) {
            if (ugly[i] == primes[j] * ugly[idx[j]]) idx[j]++;
        }
    }
    return ugly;
}

在代码中,ugly向量存储着超级丑数,idx向量存储着每个质数的当前索引。算法从丑数1开始,通过不断比较当前丑数和候选丑数,选择最小的作为下一个丑数。

Python实现:简洁之美

使用Python,我们可以用更简洁的代码实现相同的算法:

import heapq

def superUglyNumbers(n, primes):
    ugly = [1]
    heap = [(p, p, i) for i, p in enumerate(primes)]
    while len(ugly) < n:
        val, p, i = heapq.heappop(heap)
        if val != ugly[-1]:
            ugly.append(val)
        heapq.heappush(heap, (p * ugly[idx[i]], p, i + 1))
    return ugly

在这个Python实现中,我们使用了一个优先队列heap来跟踪候选丑数。优先队列按照候选丑数的大小排序,因此算法可以高效地选择最小的候选丑数。

结语:掌握超级丑数

通过理解质因数分解和动态规划算法,我们掌握了寻找超级丑数的利器。无论是C++还是Python,我们都可以使用清晰高效的代码解决这类问题。下次遇到超级丑数,请不要犹豫,运用你新获得的知识来征服它吧!