寻找丑数的问题

无意间看到一个有关丑数的问题,问题描述:我们把只包含因子235的数称作丑数(Ugly Number)。例如68都是丑数,但14不是,因为它包含因子7。习惯上我们把1当做是第一个丑数。求按从小到大的顺序的第n个丑数。

最简单的方法就是从小到大遍历一遍,找出第n个,但是耗时实在是无法忍受:

#include<stdio.h>

int IsUgly(int checkNum)
{
    while(checkNum % 2 == 0)
        checkNum /= 2;
    while(checkNum % 3 == 0)
        checkNum /= 3;
    while(checkNum % 5 == 0)
        checkNum /= 5;
    if(checkNum == 1)
        return 1;
    else 
        return 0;
}

int main()
{
    int uglyNum;
    printf("input the number:\n");
    scanf("%d",&uglyNum);

    int numCounter=0;
    int uglyCounter=0;
    if(uglyNum <= 0)
        printf("illegal input!\n");
    else
    {    
        while(uglyCounter < uglyNum)
        {
            numCounter++;
            if(IsUgly(numCounter) == 1)
                uglyCounter++;        
        }
        printf("output the result:\n");
        printf("%d\n",numCounter);
    } 
return 0;
}

于是乎,有了第二种方法,就是从第一个丑数开始,乘以2,3,5,把每个丑数都通过乘法算出来,然后一直下去,直到得到第n个丑数,http://www.cnblogs.com/mingzi/archive/2009/08/04/1538491.html 里的算法描述比较清晰,可以参考下,这里贴出关键部分:

我们假设数组中已经有若干个丑数,排好序后存在数组中。我们把现有的最大丑数记做M。现在我们来生成下一个丑数,该丑数肯定是前面某一个丑数乘以23或者5的结果。我们首先考虑把已有的每个丑数乘以2。在乘以2的时候,能得到若干个结果小于或等于M的。由于我们是按照顺序生成的,小于或者等于M肯定已经在数组中了,我们不需再次考虑;我们还会得到若干个大于M的结果,但我们只需要第一个大于M的结果,因为我们希望丑数是按从小到大顺序生成的,其他更大的结果我们以后再说。我们把得到的第一个乘以2后大于M的结果,记为M2。同样我们把已有的每一个丑数乘以35,能得到第一个大于M的结果M3M5。那么下一个丑数应该是M2M3M5三个数的最小者

前面我们分析的时候,提到把已有的每个丑数分别都乘以235,事实上是不需要的,因为已有的丑数是按顺序存在数组中的。对乘以2而言,肯定存在某一个丑数T2,排在它之前的每一个丑数乘以2得到的结果都会小于已有最大的丑数,在它之后的每一个丑数乘以2得到的结果都会太大。我们只需要记下这个丑数的位置,同时每次生成新的丑数的时候,去更新这个T2。对乘以35而言,存在着同样的T3T5

 

#include<stdio.h>

int Min(int num1, int num2, int num3)
{
    int temp = num1 < num2 ? num1 : num2; 
    return (temp < num3 ? temp : num3);
}

int main()
{
    int uglyIndex;
    printf("input the number:\n");
    scanf("%d",&uglyIndex);

    if(uglyIndex <= 0)
        printf("illegal input!\n");
    else
    {
        int *uglyNums = new int[uglyIndex];
        uglyNums[0]=1;
        int uglyCounter=1;
        int T2 = 0;
        int T3 = 0;
        int T5 = 0;
        while(uglyCounter < uglyIndex)
        {
            int nextUgly = Min(uglyNums[T2]*2, uglyNums[T3]*3, uglyNums[T5]*5);
            uglyNums[uglyCounter++]=nextUgly;
            if(nextUgly == uglyNums[T2]*2)
                T2++;
            if(nextUgly == uglyNums[T3]*3)
                T3++;
            if(nextUgly == uglyNums[T5]*5)
                T5++;
        }
        printf("output the result:\n");
        printf("%d\n",uglyNums[uglyIndex-1]);
    }    
    return 0;
}

 

后来搜了下,发现了一个好贴 http://www.iteye.com/topic/832545 里面总结了五种方法,其中有个因数分解的方法,挺有意思的,就直接贴过来好了:

public class Q64
{
    //基于因数分解求出val以内有多少个丑数(不包含1)
    static int nums235(long val)
    {
        int n = 0;
        while(val>=2)
        {
            n += 1+nums35(val);
            val/=2;
        }
        return n;
    }
    static int nums35(long val)
    {
        int n = 0;
        while(val>=3)
        {
            n += 1+nums5(val);
            val/=3;
        }
        return n;
    }
    static int nums5(long val)
    {
         int n = 0;
         while(val>=5)
         {
             n++;
             val/=5;
         }
         return n;
    }
    //用二分法查找第n个丑数
    //对于X,如果X以内的丑数个数是n,而X-1以内的丑数个数是n-1,那么X就是第n个丑数
    static long numOfIndex(int n)
    {
        if(n == 1)
            return 1;
        n--;
        long val1 = 1;
        int nums1 = 0;
        long val2 = 2;
        int nums2 = nums235(val2);
        while(nums2<n)
        {
            val1 = val2;
            nums1 = nums2;
            val2 = val1*2;
            nums2 = nums235(val2);
        }
        if(nums1 == n)
            return val1;
        if(nums2 == n)
            return val2;
        for(;;)
        {
            long mid = (val1+val2)/2;
            int nums = nums235(mid);
            if(val2 == mid+1 && nums == n-1 && nums2==n)
                return val2;
            if(mid == val1+1 && nums1 == n-1 && nums==n)
                return mid;
            if(nums >= n)
            {
                val2 = mid;
                nums2 = nums;
            }
            else
            {
                val1 = mid;
                nums1 = nums;
            }
        }
    }
    static long check(long val)
    {
        long v = val;
        while(v%2==0) v/=2;
        while(v%3==0) v/=3;
        while(v%5==0) v/=5;
        if(v!=1)
            System.out.println(val + " 不是丑数");
        return val;
    }

    static void calc(int n)
    {
        long val = numOfIndex(n);
        System.out.println(n +  ":" + val);
        check(val);
    }
    static void calc1(int n)
    {
        calc(n-1);
        calc(n);
        calc(n+1);
    }
    public static void main(String[] args)
    {
        long t = System.currentTimeMillis();
        calc1(100);
        calc1(1000);
        calc1(1500);
        calc1(5000);
        calc1(9918);
        calc1(10000);
        System.out.println("used time " + (System.currentTimeMillis()-t) + " ms.");
        /*
        check(257363915118311232L); 
        check(257492065429687488L); 
        check(257492065429687520L);*/ 

    }

}

大家都去原帖膜拜吧~

 

posted @ 2013-02-03 19:00  sillypudding  阅读(275)  评论(0编辑  收藏  举报