寻找丑数

题目:我们把只包含因子2、3和5的数称作丑数(Ugly Number)。例如6、8都是丑数,但14不是,因为它包含因子7。习惯上我们把1当做是第一个丑数。 求按从小到大的顺序的第1500个丑数。
分析:这是一道在网络上广为流传的面试题,据说google曾经采用过这道题。
Java代码   收藏代码
  1. package microsoft;  
  2.   
  3. import java.util.ArrayList;  
  4. import java.util.Collections;  
  5. import java.util.HashSet;  
  6. import java.util.List;  
  7. import java.util.Set;  
  8.   
  9. /** 
  10.  * 64. 寻找丑数。<br/> 
  11.  * 题目:我们把只包含因子2、3和5的数称作丑数(Ugly Number)。例如6、8都是丑数, 但14不是,因为它包含因子7。<br/> 
  12.  * 习惯上我们把1当做是第一个丑数。 求按从小到大的顺序的第1500个丑数。<br/> 
  13.  * 分析:这是一道在网络上广为流传的面试题,据说google曾经采用过这道题。<br/> 
  14.  * 总结:<br/> 
  15.  * 1.method1是最基础的遍历,唯一的优点估计就是简单易懂。<br/> 
  16.  * 2.method2,method3的思想是先人工估算范围值,将一定范围内的值乘2,3,5排重增加,不同的地方在于method2重新遍历, 
  17.  * method3排序求下标<br/> 
  18.  * 3.method4的思想是将已经获取的值分别遍历,乘以2,3,5,当比最大值大就停止,比较这3个数的最小值,增加到定义的有序数组中。<br/> 
  19.  * 4.method5的思想是将数进行评估,评估出该数包含丑数的数量,当超过丑数要求数量时,进行2分法进行缩小范围,直至求出解。 
  20.  */  
  21. public class Question64 {  
  22.     private static int num = 1500;  
  23.   
  24.     /** 
  25.      * 最基础的方法 
  26.      */  
  27.     public static void method1() {  
  28.         long l = System.currentTimeMillis();  
  29.         long temp;  
  30.         int time = 0;  
  31.         for (long i = 1;; i++) {  
  32.             temp = i;  
  33.             while (temp % 2 == 0) {  
  34.                 temp /= 2;  
  35.             }  
  36.             while (temp % 3 == 0) {  
  37.                 temp /= 3;  
  38.             }  
  39.             while (temp % 5 == 0) {  
  40.                 temp /= 5;  
  41.             }  
  42.             if (temp == 1) {  
  43.                 time++;  
  44.                 // System.out.println(time + ":" + i);  
  45.                 if (time == num) {  
  46.                     break;  
  47.                 }  
  48.             }  
  49.         }  
  50.         System.out.println(System.currentTimeMillis() - l);  
  51.     }  
  52.   
  53.     public static void method2() {  
  54.         // 初始化  
  55.         long l = System.currentTimeMillis();  
  56.         Set set = new HashSet<Integer>();  
  57.         set.add(1);  
  58.         for (int i = 1; i < Integer.MAX_VALUE / 5; i++) {  
  59.             if (set.contains(i)) {  
  60.                 set.add(2 * i);  
  61.                 set.add(3 * i);  
  62.                 set.add(5 * i);  
  63.             }  
  64.         }  
  65.         // 遍历  
  66.         int time = 0;  
  67.         for (int i = 1; i < Integer.MAX_VALUE; i++) {  
  68.             if (set.contains(i)) {  
  69.                 time++;  
  70.                 // System.out.println(time + ":" + i);  
  71.                 if (time == num) {  
  72.                     break;  
  73.                 }  
  74.             }  
  75.         }  
  76.         System.out.println(System.currentTimeMillis() - l);  
  77.     }  
  78.   
  79.     public static void method3() {  
  80.         // 初始化  
  81.         long l = System.currentTimeMillis();  
  82.         Set set = new HashSet<Integer>();  
  83.         set.add(1);  
  84.         for (int i = 1; i < Integer.MAX_VALUE / 5; i++) {  
  85.             if (set.contains(i)) {  
  86.                 set.add(2 * i);  
  87.                 set.add(3 * i);  
  88.                 set.add(5 * i);  
  89.             }  
  90.         }  
  91.         // 排序  
  92.         List list = new ArrayList(set);  
  93.         Collections.sort(list);  
  94.         // System.out.println(list.get(1499));  
  95.         System.out.println(System.currentTimeMillis() - l);  
  96.     }  
  97.   
  98.     public static void method4() {  
  99.         long l = System.currentTimeMillis();  
  100.         int[] ints = new int[num];  
  101.         ints[0] = 1;  
  102.         for (int count = 1, m2 = 0, m3 = 0, m5 = 0; count < num; count++) {  
  103.             for (int i = 0; i < count; i++) {  
  104.                 if (ints[i] * 2 > ints[count - 1]) {  
  105.                     m2 = ints[i] * 2;  
  106.                     break;  
  107.                 }  
  108.             }  
  109.             for (int i = 0; i < count; i++) {  
  110.                 if (ints[i] * 3 > ints[count - 1]) {  
  111.                     m3 = ints[i] * 3;  
  112.                     break;  
  113.                 }  
  114.             }  
  115.             for (int i = 0; i < count; i++) {  
  116.                 if (ints[i] * 5 > ints[count - 1]) {  
  117.                     m5 = ints[i] * 5;  
  118.                     break;  
  119.                 }  
  120.             }  
  121.             ints[count] = Math.min(m2, Math.min(m3, m5));  
  122.         }  
  123.         System.out.println(System.currentTimeMillis() - l);  
  124.     }  
  125.   
  126.     public static void method5() {  
  127.         long l = System.currentTimeMillis();  
  128.         long varR = 1, varL = 0;  
  129.         while (nums235(varR) < num) {  
  130.             varL = varR;  
  131.             varR *= 2;  
  132.         }  
  133.         while (varL + 1 != varR) {  
  134.             long mid = (varL + varR) / 2;  
  135.             if (nums235(mid) >= num) {  
  136.                 varR = mid;  
  137.             }  
  138.             if (nums235(mid) < num) {  
  139.                 varL = mid;  
  140.             }  
  141.         }  
  142.         System.out.println(System.currentTimeMillis() - l);  
  143.     }  
  144.   
  145.     static int nums235(long val) {  
  146.         if (val < 1)  
  147.             return 0;  
  148.         int n = 1;// 加上1  
  149.         while (val >= 2) {  
  150.             n += 1 + nums35(val);  
  151.             val /= 2;  
  152.         }  
  153.         return n;  
  154.     }  
  155.   
  156.     static int nums35(long val) {  
  157.         int n = 0;  
  158.         while (val >= 3) {  
  159.             n += 1 + nums5(val);  
  160.             val /= 3;  
  161.         }  
  162.         return n;  
  163.     }  
  164.   
  165.     static int nums5(long val) {  
  166.         int n = 0;  
  167.         while (val >= 5) {  
  168.             n++;  
  169.             val /= 5;  
  170.         }  
  171.         return n;  
  172.     }  
  173.   
  174.     public static void main(String[] args) {  
  175.         method1();  
  176.         method2();  
  177.         method3();  
  178.         method4();  
  179.         method5();  
  180.     }  
  181.   
  182. }  

运行结果(运行时间ms):
Java代码   收藏代码
  1. 193672  
  2. 93750  
  3. 33015  
  4. 16  
  5. 0  


题后总结:
感谢 rocketballlewiswRStallmantaolei0628对该题的技术支持。

method4参考于rocketball,lewisw,RStallman
method5参考于taolei0628

该题的最优解为method5。

当然问题还是有的,如果大于long范围时就应该用字符串去处理,但是这个不在算法的考虑之内,因此就不写出处理这个问题的代码了。
那么,到此结题。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值