求解自然数的因数
之前室友问了我一个题目,怎么求一个自然数的所有因数(升序排列)。
我很快想到了一个时间复杂度 O(n) 的简单实现。
std::vector<size_t> get_factors(size_t N)
{
using std::vector;
if (N <= 1)
{
return vector<size_t>(1, N);
}
std::vector<size_t> factors;
size_t t = N >> 1;
for (size_t i = 2; i <= t; ++i)
{
if (N % i == 0)
{
factors.emplace_back(i);
}
}
return factors;
}
这个算法效率较低,闲来无事,我感觉这个问题蛮常见的,就想了想优化一下。
比较容易想到质因数分解,为了效率,我先实现了一个简单的函数,把 [1, n]
的质数输出到文件中保存起来。
size_t generate_prime(const size_t N, const char* path = "prime.txt")
{
using std::ofstream;
using std::vector;
ofstream f(path, std::ofstream::ate);
size_t cnt = 0;
if (!f.is_open())
{
return cnt;
}
vector<size_t> V(N + 1, 0);
for (size_t i = 2; i <= N; ++i)
{
if (V[i] == 0)
{
for (size_t j = i * i; j <= N; j += i)
{
V[j] = 1;
}
f << i << ',';
++cnt;
}
}
f.close();
return cnt;
}
然后在需要的时候把质数拿出来,这个过程只需要在最开始执行一次。
std::vector<size_t> read_prime(const size_t N, const char* path = "prime.txt")
{
using std::ifstream;
using std::string;
using std::getline;
using std::vector;
vector<size_t> prime(N);
ifstream f(path, std::ifstream::in);
if (!f.is_open())
{
return prime;
}
string buf;
size_t cur = 0, next = 0, str_len = 0, cnt = 0;
while (!f.eof())
{
getline(f, buf);
str_len = buf.length();
while (cur < str_len && cnt < N)
{
next = buf.find(',', cur);
for (size_t i = cur; i < next; ++i)
{
prime[cnt] *= 10;
prime[cnt] += buf[i] - '0';
}
++cnt;
cur = next + 1;
}
}
return prime;
}
把质素数组引用作为参数传入函数补助质因数分解。
每次分解质因数后,与之前的数据相乘放入到临时集合中,每次迭代后再与之前的质因数集合合并。
返回时转为 vector
方便后续操作。
- 这里用集合是为了防止元素重复。也有其他办法,但这是我认为最方便快捷的,考虑到一个数如果不是特别大,因数也不会特别多,使用集合的效率是可以保证的。
- 集合里面的元素默认按照 key 值升序排列。
// Please make sure that prime array is enough.
std::vector<size_t> get_factors(size_t N, std::vector<size_t>& prime)
{
using std::set;
using std::vector;
set<size_t> factors;
if (N <= 1)
{
return vector<size_t>(1, N);
}
size_t cur = prime[0], idx = 0;
while (N > 1)
{
while (N % cur == 0)
{
N /= cur;
set<size_t> _tmp{cur};
for (auto&& it : factors)
{
_tmp.insert(cur * it);
}
factors.insert(_tmp.begin(), _tmp.end());
}
cur = prime[idx++];
}
return vector<size_t>{factors.begin(), factors.end()};
}
测试结果。
[1, 10000](2倍)
time of using prime: 0.057 s
time of no prime: 0.105 s
[1, 100000](10倍)
time of using prime: 0.96 s
time of no prime: 9.615 s
[1, 1000000](30倍)
time of using prime: 29.06 s
time of no prime: 947.528 s
测试代码。
void test()
{
using std::cout;
using std::endl;
auto prime{ read_prime(generate_prime(1000010)) };
time_t cb1 = clock();
for (size_t i = 1; i <= 100000; ++i)
{
auto r = get_factors(i, prime);
}
time_t ce1 = clock();
cout << "time of using prime: " << (double)(ce1 - cb1) / CLOCKS_PER_SEC << " s" << endl;
time_t cb2 = clock();
for (size_t i = 1; i <= 100000; ++i)
{
auto r = get_factors(i);
}
time_t ce2 = clock();
cout << "time of no prime: " << (double)(ce2 - cb2) / CLOCKS_PER_SEC << " s" << endl;
}
If it is useful, and you would like to help me, please Money🤡Money🤡Money🤡!
- 本文链接:https://morisa66.github.io/2021/03/17/Prime/
- 版权声明:本博客所有文章除特别声明外,均默认采用 许可协议。