在网上看了几个算法题
居然发现速度最快的接法我居然看不懂。。
请高手帮我贴上上详细注释,或则为什么能这样算的原因把 谢谢 分少。
package calculation.fuxi.interstingsuanfa;
/*
* 给定一个十进制数N,写下从1开始,到N的所有整数,然后数一下其中出现的所有"1"的个数。
例如:
N=2,写下1,2。这样只出现了1个"1"
N=12,写下 1,2,3,4,5,6,7,8,9,10,11,12。这样"1"的个数是5
请写出一个函数,返回1到N之间出现"1"的个数,比如 f(12)=5
一开始看这个就想到遍历1-N,每个数里面的1数量加起来,这样算法复杂度为 N乘N的位数,即O(NLogN)。
后来看了原作所写,这个问题有复杂度为o(LogN)的解,于是思考了一番,用递归实现了。
同时写上O(NLogN)的遍历算法和原文作者的算法以作比较 */
interface Algrithm {
public int resolve(int number);
}
class RecursiveAlgrithm implements Algrithm {// 按位递归算法,o(LogN)
@Override
public int resolve(int number) {
if (number == 0) {
return 0;
}
int length = (int) Math.log10(number);
if (length == 0) {
return 1;
} else {
int currentLvl = scale(10, length);
int head = number / currentLvl;
if (head > 1) {
return currentLvl + (head) * resolve(currentLvl - 1)
+ resolve(number - head * currentLvl);
} else {
return number - currentLvl + 1 + (head)
* resolve(currentLvl - 1)
+ resolve(number - currentLvl);
}
}
}
public int scale(int a, int b) { //指数运算
int res = 1;
for (int i = 0; i < b; i++) {
res = a * res;
}
return res;
}
}
class EnumerateAlgrithm implements Algrithm {// 遍历算法,o(NLogN)
@Override
public int resolve(int number) {
int sum = 0;
for (int i = 0; i <= number; i++) {
sum += findInSingleNum(i);
}
return sum;
}
private int findInSingleNum(int number) {//单个数中包含'1'数量
int res = 0;
while (number > 0) {
if (number % 10 == 1) {
res++;
}
number = number / 10;
}
return res;
}
}
class ReferedAlgrithm implements Algrithm {//原作者的算法,o(LogN)
@Override
public int resolve(int n) {
int count = 0;
int factor = 1;
int lower;
int current;
int higher;
while (n / factor != 0) {
lower = n - (n / factor) * factor;
current = (n / factor) % 10;
higher = n / (factor * 10);
switch (current) {
case 0:
count += higher * factor;
break;
case 1:
count += higher * factor + lower + 1;
break;
default:
count += (higher + 1) * factor;
}
factor *= 10;
}
return count;
}
}
public class getOne {
public static void calculateWith(Algrithm al) {
long start = System.nanoTime();
int res = al.resolve(100000000);
long end = System.nanoTime();
System.out.println(al.getClass().getName()+":"+res);
System.out.println("time cost:" + (end - start) + "ns");
} public static void main(String[] arg) {
ReferedAlgrithm test = new ReferedAlgrithm();
test.resolve(521);
getOne.calculateWith(new RecursiveAlgrithm());
getOne.calculateWith(new ReferedAlgrithm());
getOne.calculateWith(new EnumerateAlgrithm());
}
}由于ReferedAlgrithm和RecursiveAlgrithm的算法太快,以至于以毫秒计时都是0ms,因此改用纳秒
output:
puzzles.RecursiveAlgrithm:80000001
time cost:96940ns
puzzles.ReferedAlgrithm:80000001
time cost:4191ns
puzzles.EnumerateAlgrithm:80000001
time cost:25053647670ns
可以看到原作者的算法是最快的,递归算法虽然算法复杂度同为o(LogN),不过递归在性能上不如循环,因此也差了一个数量级
而最后o(NLogN)复杂度的算法,性能则整整差了6个数量级以上...
这个差距,大概抵得上20年的硬件性能的发展了吧.
各位有兴趣也可以用C#分别实现下这几种算法,看看和java耗时相比如何^^
居然发现速度最快的接法我居然看不懂。。
请高手帮我贴上上详细注释,或则为什么能这样算的原因把 谢谢 分少。
package calculation.fuxi.interstingsuanfa;
/*
* 给定一个十进制数N,写下从1开始,到N的所有整数,然后数一下其中出现的所有"1"的个数。
例如:
N=2,写下1,2。这样只出现了1个"1"
N=12,写下 1,2,3,4,5,6,7,8,9,10,11,12。这样"1"的个数是5
请写出一个函数,返回1到N之间出现"1"的个数,比如 f(12)=5
一开始看这个就想到遍历1-N,每个数里面的1数量加起来,这样算法复杂度为 N乘N的位数,即O(NLogN)。
后来看了原作所写,这个问题有复杂度为o(LogN)的解,于是思考了一番,用递归实现了。
同时写上O(NLogN)的遍历算法和原文作者的算法以作比较 */
interface Algrithm {
public int resolve(int number);
}
class RecursiveAlgrithm implements Algrithm {// 按位递归算法,o(LogN)
@Override
public int resolve(int number) {
if (number == 0) {
return 0;
}
int length = (int) Math.log10(number);
if (length == 0) {
return 1;
} else {
int currentLvl = scale(10, length);
int head = number / currentLvl;
if (head > 1) {
return currentLvl + (head) * resolve(currentLvl - 1)
+ resolve(number - head * currentLvl);
} else {
return number - currentLvl + 1 + (head)
* resolve(currentLvl - 1)
+ resolve(number - currentLvl);
}
}
}
public int scale(int a, int b) { //指数运算
int res = 1;
for (int i = 0; i < b; i++) {
res = a * res;
}
return res;
}
}
class EnumerateAlgrithm implements Algrithm {// 遍历算法,o(NLogN)
@Override
public int resolve(int number) {
int sum = 0;
for (int i = 0; i <= number; i++) {
sum += findInSingleNum(i);
}
return sum;
}
private int findInSingleNum(int number) {//单个数中包含'1'数量
int res = 0;
while (number > 0) {
if (number % 10 == 1) {
res++;
}
number = number / 10;
}
return res;
}
}
class ReferedAlgrithm implements Algrithm {//原作者的算法,o(LogN)
@Override
public int resolve(int n) {
int count = 0;
int factor = 1;
int lower;
int current;
int higher;
while (n / factor != 0) {
lower = n - (n / factor) * factor;
current = (n / factor) % 10;
higher = n / (factor * 10);
switch (current) {
case 0:
count += higher * factor;
break;
case 1:
count += higher * factor + lower + 1;
break;
default:
count += (higher + 1) * factor;
}
factor *= 10;
}
return count;
}
}
public class getOne {
public static void calculateWith(Algrithm al) {
long start = System.nanoTime();
int res = al.resolve(100000000);
long end = System.nanoTime();
System.out.println(al.getClass().getName()+":"+res);
System.out.println("time cost:" + (end - start) + "ns");
} public static void main(String[] arg) {
ReferedAlgrithm test = new ReferedAlgrithm();
test.resolve(521);
getOne.calculateWith(new RecursiveAlgrithm());
getOne.calculateWith(new ReferedAlgrithm());
getOne.calculateWith(new EnumerateAlgrithm());
}
}由于ReferedAlgrithm和RecursiveAlgrithm的算法太快,以至于以毫秒计时都是0ms,因此改用纳秒
output:
puzzles.RecursiveAlgrithm:80000001
time cost:96940ns
puzzles.ReferedAlgrithm:80000001
time cost:4191ns
puzzles.EnumerateAlgrithm:80000001
time cost:25053647670ns
可以看到原作者的算法是最快的,递归算法虽然算法复杂度同为o(LogN),不过递归在性能上不如循环,因此也差了一个数量级
而最后o(NLogN)复杂度的算法,性能则整整差了6个数量级以上...
这个差距,大概抵得上20年的硬件性能的发展了吧.
各位有兴趣也可以用C#分别实现下这几种算法,看看和java耗时相比如何^^
解决方案 »
- 有哪位高手可以用JAVA实现DES加密和解密算法么?不使用JAVA自带的Cipher内部类
- NoClassDefFoundErro, SessionFactory f=g.configure().buildSessionFactory()执行不了!
- com.sun.jdi.InvocationException occurred invoking method.
- 各位有没有看过hibernet 源码的呀?
- struts上传多文件的问题
- 求助,Myeclipse 发布网页与数据库MySQL连接问题
- 在struts下,使用什么标签实现多行文本区(textarea)?
- javascript 在什么环境下编译的啊,用的是什么编译器呢???哪里下载
- 截取一个文件里面每行数据@@后面的数据肿么弄
- 如何处理这个错误五
- Spring AOP 配置
- 在web页面点击按钮 备份ORACLE数据库的问题
原作者的算法是分析n就可以得到f(n)
而不用遍历,
所以复杂度是O(len) len是n的长度
而不是O(logn)