当我试图找出不丑的数字时,我遇到了一些问题。 丑陋的数字是唯一的素数因子是2,3或5的数字。那么数字不是很难看? 我试着找出1到100,000,000之间不难看的数字。我的程序可以解决问题,但似乎有点慢。我怎么能让它更快? 这是代码:
#include <iostream>
#include <queue>
using namespace std;
typedef pair<unsigned long,int> node_type;
main()
{
//generates 1500 ugly numbers into result[];
unsigned long result[1500];
priority_queue<node_type,vector<node_type>,greater<node_type> > Q;
Q.push(node_type(1,2));
for(int i=0;i<1500;i++)
{
node_type node = Q.top();
Q.pop();
switch(node.second)
{
case 2:Q.push(make_pair(node.first*2,2));
case 3:Q.push(make_pair(node.first*3,3));
case 5:Q.push(make_pair(node.first*5,5));
}
result[i]=node.first;
}
/*****************************************************
//Here is the approach I used:
//The initial value of the integer k is 1;
//and will increase by 1 every time
//k will be checked if it's a ugly number,if not increase by 1,keep doing
//this till k is not a ugly number,then count how much ugly number(we may
//call it n) is less the the
//current value of k.The result of (k-n) is the order number of this (k) not ugly
//for example:the 1st not ugly number is 7.
// there are 6 ugly number less than 7,which are 1 2 3 4 5 6,
// k=1-->k==2-->.....k=7, k-n=7-6=1,so 7 is the 1st not ugly number
***************************************************************/
int T; // The amount of cases
cin>>T;
while(T--)
{
int n;
int k=0,i=0,count=0;
cin>>n;
while(n)
{
if((k+1)==result[i]) {k++;i++;count++;}
else
{
k++;
if(k-count==n) {cout<<k<<endl;break;}
}
}
}}
最大的问题是它看起来不够快!你能告诉我如何让它更快吗?还是有其他方法可以解决这个问题?
答案 0 :(得分:5)
好的,我会咬人。根据这个定义,测试一个数字是否丑陋,在计算上实际上相当简单。
蛮力测试1亿个数字#include <iostream>
bool is_ugly(unsigned n) {
while(n % 5 == 0) n /= 5;
while(n % 3 == 0) n /= 3;
while(n % 2 == 0) n /= 2;
return n == 1;
}
int main() {
unsigned counter = 0;
for(unsigned i = 1; i <= 100000000; ++i) {
if(!is_ugly(i)) {
++counter;
}
}
std::cout << counter << std::endl;
}
在我的基准 1 中,只需要半秒钟,这是非常可行的。当然,打印它们需要花费更长的时间,因为有99998895个非丑陋数字低于100000000并且您的终端必须全部渲染它们。即使重定向到/dev/null
(将渲染从等式中取出),打印也需要6秒(比检查长10倍),使用libstdc ++和gcc 4.9和-O2
。如果您要生成所有非丑陋的数字,这不是那么容易被击败,因为瓶颈不是你可以摆脱的东西。
另一方面,如果你的目标是生成低于阈值的所有丑陋数字(或计算非丑陋数字,这与计算丑陋数字并从阈值中减去数字相同)测试所有非丑陋的数字和丑陋的数字远非最佳。更好的方法是生成丑陋的数字,因为它们很少。使用递归最容易做到这一点:
#include <iostream>
#include <set> // could also use an unordered_set, except that it turns
// out to be a pessimization
void generate_uglies(unsigned n, std::set<unsigned> &num, unsigned threshold) {
// Abort recursion if we break the upper limit or find a number
// that was already tested
if(n <= threshold && num.find(n) == num.end()) {
// Remember this ugly number
num.insert(n);
// Since n is an ugly number, these three are also ugly numbers.
generate_uglies(n * 2, num, threshold);
generate_uglies(n * 3, num, threshold);
generate_uglies(n * 5, num, threshold);
}
}
int main() {
std::set<unsigned> num;
generate_uglies(1, num, 100000000);
std::cout << num.size() << std::endl;
}
这报告回来了,好吧......
$ time ./a.out
1105
real 0m0.001s
user 0m0.000s
sys 0m0.000s
你可以使用这个,希望num.find(n) == num.end()
比is_ugly(n)
(使用之前的is_ugly
函数)更快地测试非丑陋,但在我的基准测试中差异是可以忽略不计,使用std::unordered_set
实际上较慢的因子为2-3。
附录
std::vector<bool>
稍后使用// num is to have the wanted size in advance and be full of false
void generate_uglies(unsigned n, std::vector<bool> &num) {
if(n < num.size() && !num[n]) {
num[n] = true;
generate_uglies(n * 2, num);
generate_uglies(n * 3, num);
generate_uglies(n * 5, num);
}
}
测试非丑陋的数字。 !num[i]
测试比!num[i]
函数快得多(对于低于1亿的值,平均值为〜5) 1 。如果您打算将它们打印出来并不重要,原因如上所述,但在不同情况下它可能会产生明显的差异。请注意,此表需要12.5 MB RAM。
1 您的里程将因您的机器不是我的而有所不同。我使用的是一个1.5岁的i7。
答案 1 :(得分:1)
从您的代码中了解,我知道您真正想要的是快速找到 第n个非丑陋的数字。
你的算法找到第n个非丑陋的数字是O(N),你可以发现它们使用二进制搜索,它是O(log(N))。
有T个案例,如果T非常大,我的方法可以节省大量时间。
这是我的代码,改变你的代码。
#include <iostream>
#include <queue>
using namespace std;
typedef pair<unsigned long,int> node_type;
int main()
{
//generates 1500 ugly numbers into result[];
unsigned long result[1500];
priority_queue<node_type,vector<node_type>,greater<node_type> > Q;
Q.push(node_type(1,2));
for(int i=0;i<1500;i++)
{
node_type node = Q.top();
Q.pop();
switch(node.second)
{
case 2:Q.push(make_pair(node.first*2,2));
case 3:Q.push(make_pair(node.first*3,3));
case 5:Q.push(make_pair(node.first*5,5));
}
result[i]=node.first;
}
int T; // The amount of cases
cin>>T;
//b_search_data[i] mean the count of non ugly number blow or equal i, i start from 1
unsigned long b_search_data[1501];
for (int i=0; i<1500; i++)
{
b_search_data[i] = result[i] - i - 1;
}
vector<int> search_data(b_search_data, b_search_data+1500);
while(T--)
{
int n;
int k=0,i=0,count=0;
cin>>n;
// use binary search here to find the n-th non ugly number instead your approach
int position = upper_bound(search_data.begin(), search_data.end(), n-1) - search_data.begin();
// position means
cout<<position + n<<endl;
}
}
答案 2 :(得分:0)
使用递归函数可以生成丑陋的数字。
实际上,如果N
丑陋,那么2*N
,3*N
和5*N
也很难看。
事实上,2*N
,3*N
和5*N
是丑陋的数字大于N ,因此您可以修复最大值,让我们说MAX
并且通过添加N
,2*N
和3*N
(这是递归部分)来查看比5*N
更难看的数字。
要停止重复搜索,我们可以将所有找到的数字存储在一组uglies中,所以当我们有{6}作为3*2
的产品时,我们不会添加它(和研究当我们找到2*3
(也等于6 ---我们称之为碰撞)时,所有丑陋的后代)
程序最终到达:
#include <cstdio>
#include <set>
using namespace std;
const unsigned N = 10000000;
set<unsigned> uglys;
unsigned n_collis = 0;
void srch_ugly(unsigned n)
{
if (uglys.find(n) != uglys.end()) { /* found in set */
++n_collis;
} else {
uglys.insert(n);
if (2*n < N) srch_ugly(2*n);
if (3*n < N) srch_ugly(3*n);
if (5*n < N) srch_ugly(5*n);
} /* if */
} /* srch_ugly(unsigned) */
int main()
{
unsigned col = 0; /* printing column */
srch_ugly(1);
/* print results */
printf("%lu uglys. %u collissions.\n", uglys.size(), n_collis);
for (set<unsigned>::iterator i = uglys.begin();
i != uglys.end();
++i)
{
if (col >= 72) {
puts("");
col = 0;
} /* if */
col += printf("%s%i", col ? " " : "", *i);
} /* for */
if (col) puts("");
} /* main */
很抱歉使用了printf
stdio函数,但我希望剪切72个字符的行,printf
函数返回打印的字符数(我实际上并不知道如何从ostream
类中获取实际打印字符数,所以我改为使用它们。