经典算法|自幂数算法优化|39位1秒多,应该够用了

上一篇http://t.csdn.cn/oYybh,指数级算法,求8位自幂数还可以,但是再外后就有点算不过来了,需要优化


题目抽象

n位自幂数符合条件:

a1*(10^(n-1)) + a2*(10^(n-2)) + an*(10^0)=a1^n+a2^n+....+an^n,

其中a1~an为0~9的随机数

思路

由题目抽象可知,符合条件的数字为:0~9随机数的n次幂乘以出现次数 之和。

所以可以转化为求0-9出现次数的组合,根据符合条件选取计算选取符合条件数字的方法

数据准备

/**
     * 存放0~9出现次数
     */
    private static int[] selected = new int[10];
    /**
     * 10的(0~n)次幂
     */
    private static BigInteger[] powerOf10;
    /**
     * (0~9)的n次幂
     */
    private static BigInteger[] powerOfN = new BigInteger[10];
    /**
     * 使用二位数组存储(0~9)的n次幂*出现次数,i标识一维((0~9)的n次幂),j标识二维((0~9出现的慈善))
     */
    private static BigInteger[][] preTable;
    /**
     * 二位数组,存储n位数最小值与preTable对应位置数的差值
     */
    private static BigInteger[][] preTable2;
    /**
     * 存储对应preTable位置数字位数n的最小值的位数n-1
     */
    private static int[][] preTable3;

    /**
     * 当前所求数字长度
     */
    private static int length;

    /**
     * 初始化基础数据
     * @param n
     */
    private static void initPre(int n)
    {
        powerOf10 = new BigInteger[n + 1];
        powerOf10[0] = BigInteger.ONE;
        length = n;

        for (int i = 1; i <= n; i++){
            powerOf10[i] = powerOf10[i - 1].multiply(BigInteger.TEN);
        }
        for(int i=0;i<10;i++){
            powerOfN[i] = BigInteger.valueOf(i).pow(n);
        }

        preTable = new BigInteger[10][n + 1];
        preTable2 = new BigInteger[10][n + 1];
        preTable3 = new int[10][n + 1];

        for (int i = 0; i < 10; i++)
        {
            for (int j = 0; j <= n; j++)
            {
                //数据中存在j个i
                preTable[i][j] = powerOfN[i].multiply(BigInteger.valueOf(j));
                preTable2[i][j] = powerOf10[length - 1].subtract(preTable[i][j]);

                //符合条件数字,将位数存储进去
                if(powerOf10[n].compareTo(preTable[i][j])>0){
                    preTable3[i][j] = preTable[i][j].toString().length()-1;
                }
            }
        }
    }

核心算法

/**
     * 获取符合条件的自幂数
     * @param n n位数
     * @param list 符合条件数
     */
    public static void zmnumGet(int n,List<BigInteger> list){
        initPre(n);
        search(9, BigInteger.ZERO, n,list);
    }


    /**
     * 提前检查是否符合条件
     * @param currentIndex 当前选择的数字(0~9)
     * @param sum 当前数
     * @param leftNum 剩余可选数字(0~9)
     * @return
     */
    private static boolean preCheck(int currentIndex, BigInteger sum, int leftNum)
    {
        if (sum.compareTo(preTable[currentIndex][leftNum])<0){
            return true;
        }


        BigInteger max = sum.add(preTable[currentIndex][leftNum]);
        max = max.divide(powerOf10[preTable3[currentIndex][leftNum]]);
        sum = sum.divide(powerOf10[preTable3[currentIndex][leftNum]]);
        while (!max.equals(sum))
        {
            max = max.divide(BigInteger.TEN);
            sum = sum.divide(BigInteger.TEN);
        }

        if (max.equals(BigInteger.ZERO)){
            return true;
        }


        int[] counter = getCounter(max);

        for (int j = 9; j > currentIndex; j--){
            if (counter[j] > selected[j]){
                return false;
            }
        }

        for (int i = 0; i <= currentIndex; i++){
            leftNum -= counter[i];
        }

        return leftNum >= 0;
    }

    /**
     * 搜索自幂数(核心方法)
     * 递归
     * @param currentIndex 当前选择的数字(0~9)
     * @param sum 当前数
     * @param leftNum 剩余可选数字(0~9)
     * @param fitList 已找到自幂数列表
     */
    private static void search(int currentIndex, BigInteger sum, int leftNum,List<BigInteger> fitList)
    {
        //必须在最大值内
        if (sum.compareTo(powerOf10[length])>=0){
            return;
        }

        if (leftNum == 0)
        {
            // 数字在范围内,且符合条件
            if (sum.compareTo(powerOf10[length - 1])>=0 && check(sum)){
                fitList.add(sum);
            }
            return;
        }

        if (!preCheck(currentIndex, sum, leftNum)){
            return;
        }


        if (sum.compareTo(preTable2[currentIndex][leftNum])<0){
            return;
        }


        if (currentIndex == 0)
        {
            selected[0] = leftNum;
            search(-1, sum, 0,fitList);
            selected[currentIndex] = 0;
        }
        else
        {
            for (int i = leftNum; i >= 0 ; i--)
            {
                selected[currentIndex] = i;
                search(currentIndex - 1, sum.add(preTable[currentIndex][i]), leftNum - i,fitList);
            }
        }
    }

    /**
     * 验证是否符合条件
     * @param sum
     * @return
     */
    private static boolean check(BigInteger sum)
    {
        int[] counter = getCounter(sum);
        for (int i = 0; i < 10; i++)
        {
            if (selected[i] != counter[i]){
                return false;
            }
        }

        return true;
    }

    /**
     * 统计大数的(0~9)数字数量
     * @param value
     * @return
     */
    public static int[] getCounter(BigInteger value)
    {
        int[] counter = new int[10];
        char[] sumChar = value.toString().toCharArray();

        for (int i = 0; i < sumChar.length; i++){
            counter[sumChar[i] - '0']++;
        }

        return counter;
    }

验证结果

/**
     * 验证当前位自幂数是否正确
     * @param n
     * @param valueList
     * @return
     */
    public static List<BigInteger> test(int n,List<BigInteger> valueList){
        powerOf10 = new BigInteger[n + 1];
        powerOf10[0] = BigInteger.ONE;

        for(int i=0;i<10;i++){
            powerOfN[i] = BigInteger.valueOf(i).pow(n);
        }
        List<BigInteger> falseList = new ArrayList<>();
        for(BigInteger value : valueList){
            int[] aa = getCounter(value);
            BigInteger sum = BigInteger.ZERO;
            for(int i=0;i<aa.length;i++){
                sum = sum.add(powerOfN[i].multiply(BigInteger.valueOf(aa[i])));
            }
            if(!sum.equals(value)){
                falseList.add(value);
            }
        }
        return falseList;
    }
    public static void main(String[] args) {

        List<Long> times = new ArrayList<>();
        for(int i=1;i<60;i++){
            StopWatch stopWatch = new StopWatch();
            stopWatch.start();
            List<BigInteger> list = new ArrayList<>();
            zmnumGet(i,list);
            stopWatch.stop();
            Long tc = stopWatch.getTime(TimeUnit.MILLISECONDS);
            list.sort(new Comparator<BigInteger>() {
                @Override
                public int compare(BigInteger o1, BigInteger o2) {
                    return o1.compareTo(o2);
                }
            });
            List<BigInteger> falseList = test(i,list);
            log.info("耗时:{}毫秒,{}位自幂数({}个):{}, 错误数字:{}",tc,i,list.size(),list,falseList);
            times.add(tc);

        }
        playPng(times,"zmsget");
    }


    public static void playPng(List<Long> tlist,String name){
        LineChat lineChat = new LineChat();
        lineChat.setH1Title("自幂数算法复杂度");
        lineChat.setH2Title("时间复杂度");
        lineChat.setXName("N维度");
        lineChat.setYName("T时间");
        List<RowFlied> list = new ArrayList<>();
        String colKey = "时间";
        for(int i=1;i<=tlist.size();i++){
            RowFlied rowFlied = new RowFlied();
            rowFlied.setColKey(colKey);
            rowFlied.setX((double) i);
            StopWatch startWatch = new StopWatch();
            startWatch.start();
            rowFlied.setY((double)tlist.get(i-1));
//            log.info("构造数据,消耗时间(微秒):{}",startWatch.getTime(TimeUnit.MILLISECONDS));
            list.add(rowFlied);
        }

        lineChat.setRowFlieds(list);

        ChatUtils.lineChat(lineChat,name);
    }

输出结果:

耗时:0毫秒,1位自幂数(9个):[1, 2, 3, 4, 5, 6, 7, 8, 9], 错误数字:[]
耗时:0毫秒,2位自幂数(0个):[], 错误数字:[]
耗时:1毫秒,3位自幂数(4个):[153, 370, 371, 407], 错误数字:[]
耗时:2毫秒,4位自幂数(3个):[1634, 8208, 9474], 错误数字:[]
耗时:2毫秒,5位自幂数(3个):[54748, 92727, 93084], 错误数字:[]
耗时:1毫秒,6位自幂数(1个):[548834], 错误数字:[]
耗时:1毫秒,7位自幂数(4个):[1741725, 4210818, 9800817, 9926315], 错误数字:[]
耗时:2毫秒,8位自幂数(3个):[24678050, 24678051, 88593477], 错误数字:[]
耗时:3毫秒,9位自幂数(4个):[146511208, 472335975, 534494836, 912985153], 错误数字:[]
耗时:8毫秒,10位自幂数(1个):[4679307774], 错误数字:[]
耗时:7毫秒,11位自幂数(8个):[32164049650, 32164049651, 40028394225, 42678290603, 44708635679, 49388550606, 82693916578, 94204591914], 错误数字:[]
耗时:11毫秒,12位自幂数(0个):[], 错误数字:[]
耗时:16毫秒,13位自幂数(0个):[], 错误数字:[]
耗时:25毫秒,14位自幂数(1个):[28116440335967], 错误数字:[]
耗时:41毫秒,15位自幂数(0个):[], 错误数字:[]
耗时:39毫秒,16位自幂数(2个):[4338281769391370, 4338281769391371], 错误数字:[]
耗时:53毫秒,17位自幂数(3个):[21897142587612075, 35641594208964132, 35875699062250035], 错误数字:[]
耗时:77毫秒,18位自幂数(0个):[], 错误数字:[]
耗时:186毫秒,19位自幂数(4个):[1517841543307505039, 3289582984443187032, 4498128791164624869, 4929273885928088826], 错误数字:[]
耗时:187毫秒,20位自幂数(1个):[63105425988599693916], 错误数字:[]
耗时:107毫秒,21位自幂数(2个):[128468643043731391252, 449177399146038697307], 错误数字:[]
耗时:187毫秒,22位自幂数(0个):[], 错误数字:[]
耗时:219毫秒,23位自幂数(5个):[21887696841122916288858, 27879694893054074471405, 27907865009977052567814, 28361281321319229463398, 35452590104031691935943], 错误数字:[]
耗时:213毫秒,24位自幂数(3个):[174088005938065293023722, 188451485447897896036875, 239313664430041569350093], 错误数字:[]
耗时:213毫秒,25位自幂数(5个):[1550475334214501539088894, 1553242162893771850669378, 3706907995955475988644380, 3706907995955475988644381, 4422095118095899619457938], 错误数字:[]
耗时:268毫秒,26位自幂数(0个):[], 错误数字:[]
耗时:338毫秒,27位自幂数(5个):[121204998563613372405438066, 121270696006801314328439376, 128851796696487777842012787, 174650464499531377631639254, 177265453171792792366489765], 错误数字:[]
耗时:409毫秒,28位自幂数(0个):[], 错误数字:[]
耗时:446毫秒,29位自幂数(4个):[14607640612971980372614873089, 19008174136254279995012734740, 19008174136254279995012734741, 23866716435523975980390369295], 错误数字:[]
耗时:531毫秒,30位自幂数(0个):[], 错误数字:[]
耗时:679毫秒,31位自幂数(3个):[1145037275765491025924292050346, 1927890457142960697580636236639, 2309092682616190307509695338915], 错误数字:[]
耗时:866毫秒,32位自幂数(1个):[17333509997782249308725103962772], 错误数字:[]
耗时:830毫秒,33位自幂数(2个):[186709961001538790100634132976990, 186709961001538790100634132976991], 错误数字:[]
耗时:1062毫秒,34位自幂数(1个):[1122763285329372541592822900204593], 错误数字:[]
耗时:1269毫秒,35位自幂数(2个):[12639369517103790328947807201478392, 12679937780272278566303885594196922], 错误数字:[]
耗时:1088毫秒,36位自幂数(0个):[], 错误数字:[]
耗时:1374毫秒,37位自幂数(1个):[1219167219625434121569735803609966019], 错误数字:[]
耗时:1437毫秒,38位自幂数(1个):[12815792078366059955099770545296129367], 错误数字:[]
耗时:1292毫秒,39位自幂数(2个):[115132219018763992565095597973971522400, 115132219018763992565095597973971522401], 错误数字:[]
耗时:1582毫秒,40位自幂数(0个):[], 错误数字:[]
耗时:1373毫秒,41位自幂数(0个):[], 错误数字:[]
耗时:1294毫秒,42位自幂数(0个):[], 错误数字:[]
耗时:1128毫秒,43位自幂数(0个):[], 错误数字:[]
耗时:940毫秒,44位自幂数(0个):[], 错误数字:[]
耗时:958毫秒,45位自幂数(0个):[], 错误数字:[]
耗时:802毫秒,46位自幂数(0个):[], 错误数字:[]
耗时:582毫秒,47位自幂数(0个):[], 错误数字:[]
耗时:512毫秒,48位自幂数(0个):[], 错误数字:[]
耗时:351毫秒,49位自幂数(0个):[], 错误数字:[]
耗时:224毫秒,50位自幂数(0个):[], 错误数字:[]
耗时:153毫秒,51位自幂数(0个):[], 错误数字:[]
耗时:105毫秒,52位自幂数(0个):[], 错误数字:[]
耗时:55毫秒,53位自幂数(0个):[], 错误数字:[]
耗时:24毫秒,54位自幂数(0个):[], 错误数字:[]
耗时:13毫秒,55位自幂数(0个):[], 错误数字:[]
耗时:5毫秒,56位自幂数(0个):[], 错误数字:[]
耗时:1毫秒,57位自幂数(0个):[], 错误数字:[]
耗时:0毫秒,58位自幂数(0个):[], 错误数字:[]
耗时:0毫秒,59位自幂数(0个):[], 错误数字:[]

计算出的结果验证无误,39位自幂数用时:1292毫秒

注:

优化思路参考:水仙花数算法讨论(https://bbs.csdn.net/topics/360185693)

  • 6
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

fjza1168

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值