丑数

原文地址: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)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值