暴力破解穷举算法的改进

最近想写个网络爬虫,涉及穷举问题,想起暴力破解用的正是此算法,于是网上搜索了一些材料。大部分都是下面这篇文章

http://www.oschina.net/code/snippet_183849_10691

采用递归算法,最傻瓜的方式。

java + Pentium Dual -Core E6700(3.2GHz)+ 4G内存的台式机跑1~5位由a~z组合的密码花了96秒。

组合可能数26+26²+26³+26@4+26@5=26+676+1756+45656+1187056=1235170种

平均每秒约算出12866个,不过这个平均值也没啥意义,根据算法可知位数约大的时候单次求值耗时更大。

网上范例如下:

public class MainService {
    //密码可能会包含的字符集合
    static char[] charSource = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',  
                               'n',  'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z' };
    static int sLength = charSource.length; //字符集长度	
	public static void main(String[] args)
    {
		long beginMillis = System.currentTimeMillis();
        System.out.println(beginMillis);//开始时间
		
        int maxLength = 5; //设置可能最长的密码长度
        CrackPass(maxLength);
		
        long endMillis = System.currentTimeMillis();
        System.out.println(endMillis);//结束时间
        
        System.out.println(endMillis - beginMillis);//总耗时,毫秒

    }
	
	//得到密码长度从 1到maxLength的所有不同长的密码集合
    public static void CrackPass(int maxLength)
    {
        for (int i = 1; i <= maxLength; i++)
        { 
            char[] list =new char[i];
            Crack(list, i);
        }
    
    }	

	//得到长度为len所有的密码组合,在字符集charSource中
	//递归表达式:fn(n)=fn(n-1)*sLenght; 大致是这个意思吧 
    private static void Crack(char[] list, int len)
    {
        if (len == 0)
        {  //递归出口,list char[] 转换为字符串,并打印
           System.out.println(ArrayToString(list));
        }
        else
        {
            for (int i = 0; i < sLength; i++)
            {
                list[len - 1] = charSource[i];
                Crack(list, len - 1);
            }
        }        
    }
    
    //list char[] 转换为字符串
    private static String ArrayToString(char[] list)
    {
        if (list == null||list.length == 0)
            return "";
        StringBuilder buider = new StringBuilder(list.length*2);
        for (int i = 0; i < list.length; i++)
        {
            buider.append(list[i]);
        }
        return buider.toString();

    }    
}

这下问题来了,爬虫开了多个线程,取穷举数据的时候还真不知道把锁加在哪个地方好。

忽然想起计算机里面的进制原理,其实可以把穷举的字符抽象成基本数字,构建一套动态的进制体系。有多少个穷举字符即为多少进制结构。

改进后的代码:

public class MainService2 {
    //密码可能会包含的字符集合
    static char[] charSource = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',  
                               'n',  'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z' };
    static int sLength = charSource.length; //字符集长度,26个字符即可抽象成26进制系统的基本数
	public static void main(String[] args) {
		
		long beginMillis = System.currentTimeMillis();
        System.out.println(beginMillis);//开始时间
		
        int maxLength = 5; //设置可能最长的密码长度
		int counter = 0;//计数器,多线程时可以对其加锁,当然得先转换成Integer类型。
		StringBuilder buider = new StringBuilder();
		
		while (buider.toString().length() <= maxLength) {
			buider = new StringBuilder(maxLength*2);
			int _counter = counter;
			while (_counter >= sLength) {//10进制转换成26进制
				buider.insert(0, charSource[_counter % sLength]);//获得低位
				_counter = _counter / sLength;
				_counter--;//精髓所在,处理进制体系中只有10没有01的问题,在穷举里面是可以存在01的
			}
			buider.insert(0,charSource[_counter]);//最高位
			counter++;
			System.out.println(buider.toString());
		}
		
        long endMillis = System.currentTimeMillis();
        System.out.println(endMillis);//结束时间
        
        System.out.println(endMillis - beginMillis);//总耗时,毫秒
	} 
}

同样条件运行依然是 96秒,不过代码省了很多,最重要的是可以使用多线程加锁了。


你可能感兴趣的:(算法)