原文地址:Ugly Numbers
一个数的因数只有2,3或者5这样的质数,那么这个数就被称为丑数(Ugly Numbers)。序列1, 2, 3, 4, 5, 6, 8, 9, 10, 12, 15, …就是前11个丑数。为了方便,把1也包括进来了。
写一个程序找出并打印第150个丑数。
方法1(简单)
算法
循环所有的正整数,直到丑数的个数小于n,如果一个整数是丑数,那么就增加丑数数目。
检查一下这个数字是否是丑数,用这个数除以2,3,5的最大可整除的指数,如果这个数能变为1,那么它就是丑数,否则就不是。
例如,我们看看300是不是丑数。2的最大可整除指数是4,300除以4得到75。3的最大可整除指数是3,然后75除以3等于25。5的最大可整除的指数是25,那么除以25以后结果是1。因为我们最后得到的结果是1,所以300是丑数。
实现:
# include<stdio.h>
# include<stdlib.h>
/*This function divides a by greatest divisible power of b*/
int maxDivide(int a, int b) {
while (a%b == 0)
a = a/b;
return a;
}
/* Function to check if a number is ugly or not */
int isUgly(int no) {
no = maxDivide(no, 2);
no = maxDivide(no, 3);
no = maxDivide(no, 5);
return (no == 1)? 1 : 0;
}
/* Function to get the nth ugly number*/
int getNthUglyNo(int n) {
int i = 1;
int count = 1; /* ugly number count */
/*Check for all integers untill ugly count becomes n*/
while (n > count)
{
i++;
if (isUgly(i))
count++;
}
return i;
}
/* Driver program to test above functions */
int main() {
unsigned no = getNthUglyNo(150);
printf("150th ugly no. is %d ", no);
getchar();
return 0;
}
上面的方法效率不是很高,在丑数的个数达到n之前要检查每一个整数,但是空间复杂度只有O(1)。
方法2(动态规划)
这里有一个在时间上更加高效的算法,它需要O(n)的附加空间。丑数序列是1, 2, 3, 4, 5, 6, 8, 9, 10, 12, 15, …
因为每个整数只能被2,3,5整除,一种方法就是查看这个序列,将这个序列分成一下三组:
(1) 1×2, 2×2, 3×2, 4×2, 5×2, …
(2) 1×3, 2×3, 3×3, 4×3, 5×3, …
(3) 1×5, 2×5, 3×5, 4×5, 5×5, …
我们可以找到每个子序列,丑序列本身 (1, 2, 3, 4, 5, …)乘以2,3,5。然后我们用类似合并的方法最为合并排序,从这个三个子序列中得到每个丑数。每一步我们都选择最小的,然后往后再移一步。
算法:
1. 申明一个丑数数组:ugly[150]
2. 初始化第一个丑数:ugly[0] = 1
3. 初始化3个下标变量i2, i3, i5,指向丑数数组的第一个元素:i2 = i3 = i5 =0;
4. 为下一个丑数初始化三个选项:
next_mulitple_of_2 = ugly[i2]*2;
next_mulitple_of_3 = ugly[i3]*3;
next_mulitple_of_5 = ugly[i5]*5;
5. 然后循环填充所有的数字到数组,直到150:
For (i = 1; i < 150; i++ )
{
/* These small steps are not optimized for good
readability. Will optimize them in C program */
next_ugly_no = Min(next_mulitple_of_2,
next_mulitple_of_3,
next_mulitple_of_5);
if (next_ugly_no == next_mulitple_of_2)
{
i2 = i2 + 1;
next_mulitple_of_2 = ugly[i2]*2;
}
if (next_ugly_no == next_mulitple_of_3)
{
i3 = i3 + 1;
next_mulitple_of_3 = ugly[i3]*3;
}
if (next_ugly_no == next_mulitple_of_5)
{
i5 = i5 + 1;
next_mulitple_of_5 = ugly[i5]*5;
}
ugly[i] = next_ugly_no
}/* end of for loop */
6. 返回next_ugly_no
例子:
下面看看它是如何工作的
初始化
ugly[] = | 1 |
i2 = i3 = i5 = 0;
第一次迭代
ugly[1] = Min(ugly[i2]*2, ugly[i3]*3, ugly[i5]*5)
= Min(2, 3, 5)
= 2
ugly[] = | 1 | 2 |
i2 = 1, i3 = i5 = 0 (i2 got incremented )
第二次迭代
ugly[2] = Min(ugly[i2]*2, ugly[i3]*3, ugly[i5]*5)
= Min(4, 3, 5)
= 3
ugly[] = | 1 | 2 | 3 |
i2 = 1, i3 = 1, i5 = 0 (i3 got incremented )
第三第迭代
ugly[3] = Min(ugly[i2]*2, ugly[i3]*3, ugly[i5]*5)
= Min(4, 6, 5)
= 4
ugly[] = | 1 | 2 | 3 | 4 |
i2 = 2, i3 = 1, i5 = 0 (i2 got incremented )
第四次迭代
ugly[4] = Min(ugly[i2]*2, ugly[i3]*3, ugly[i5]*5)
= Min(6, 6, 5)
= 5
ugly[] = | 1 | 2 | 3 | 4 | 5 |
i2 = 2, i3 = 1, i5 = 1 (i5 got incremented )
第五次迭代
ugly[4] = Min(ugly[i2]*2, ugly[i3]*3, ugly[i5]*5)
= Min(6, 6, 10)
= 6
ugly[] = | 1 | 2 | 3 | 4 | 5 | 6 |
i2 = 3, i3 = 2, i5 = 1 (i2 and i3 got incremented )
一直持续到 I < 150
代码:
# include<stdio.h>
# include<stdlib.h>
# define bool int
/* Function to find minimum of 3 numbers */
unsigned min(unsigned , unsigned , unsigned );
/* Function to get the nth ugly number*/
unsigned getNthUglyNo(unsigned n)
{
unsigned *ugly =
(unsigned *)(malloc (sizeof(unsigned)*n));
unsigned i2 = 0, i3 = 0, i5 = 0;
unsigned i;
unsigned next_multiple_of_2 = 2;
unsigned next_multiple_of_3 = 3;
unsigned next_multiple_of_5 = 5;
unsigned next_ugly_no = 1;
*(ugly+0) = 1;
for(i=1; i<n; i++)
{
next_ugly_no = min(next_multiple_of_2,
next_multiple_of_3,
next_multiple_of_5);
*(ugly+i) = next_ugly_no;
if(next_ugly_no == next_multiple_of_2)
{
i2 = i2+1;
next_multiple_of_2 = *(ugly+i2)*2;
}
if(next_ugly_no == next_multiple_of_3)
{
i3 = i3+1;
next_multiple_of_3 = *(ugly+i3)*3;
}
if(next_ugly_no == next_multiple_of_5)
{
i5 = i5+1;
next_multiple_of_5 = *(ugly+i5)*5;
}
} /*End of for loop (i=1; i<n; i++) */
return next_ugly_no;
}
/* Function to find minimum of 3 numbers */
unsigned min(unsigned a, unsigned b, unsigned c)
{
if(a <= b)
{
if(a <= c)
return a;
else
return c;
}
if(b <= c)
return b;
else
return c;
}
/* Driver program to test above functions */
int main()
{
unsigned no = getNthUglyNo(150);
printf("%dth ugly no. is %d ", 150, no);
getchar();
return 0;
}
算法范式:动态规划
时间复杂度: O(n)
空间复杂度: O(n)