一道算法题

经楼下朋友提醒,我这个算法求出的正好是21位水仙花数。于是我对其进行了稍微的修订,使得其支持任意位数的水仙花数求值,效果还不错,理论上的水仙花最大数为34位(我算了下,至少到39位还有解),我的求解花了半分多钟,而21位数的求解只化了2秒多。

 

[原题]

        http://www.iteye.com/problems/50018

写道
一个21位的整数,它的各个位数的21次方的和加起来等于它本身. 要求:程序在三分钟内完成,Java语言实现.

 

[解决思路]

        这个我最初的思路也是想找出其中是否有数学规律,无奈大学数学就混过来的,只能穷举解决了。

        虽然是穷举,但是不同的实现,效果也不一样,如果要从100000000000000000000穷举到999999999999999999999,我想肯定麻烦大了。

        这里我主要是换个思路,穷举这个数中的每个位置上的数字的总数。从一开始,我们假设共有该数中存在9个9,我们将这个数的信息存到几个特定的数组中去:

	private int[] countArray = new int[10]; // 个数列表
	private int[] countSumArray = new int[10]; // 个数总数
	private BigInteger[] sumArray = new BigInteger[10];// 值总数
	private int offset = 0;// 浮标

        countArray记录依次从9到0每个数的个数,countSumArray是countArray中的各个数与其之前所有数的个数的总和(即countSumArray[n]=countSumArray[n-1]+countNum),sumArray是当前数的总值(即sumArray[n]=sumArray[n-1]+num)。offset是浮标,即当前判定的数的位置       

        我们对该个数进行判断,9个9后面还有12位数,那么9个9最小就是9个9的平方+12个0的平方,最大是9个9的平方+12个8的平方。我们从以下三个方面来判断:

        1. 最小值不大于999999999999999999999

        2. 最大值不小于100000000000000000000

        3. 最大值与最小值从首部是否相同的部分,如777700000000000000000与777799999999999999999,存在7777相同的部分,如果该相同的部分中有某个数的个数大于offset中相同的值的个数,那么该值也判定为失败

        还有一个很重要的判断就是,如果countSumArray中对应的offset中的值为21,那么即所有的位数都有值,那么直接判定如果该值=其各个位置上的数的21次方之和,如果不等返回失败,反之,这个数就是要求的数。

 

        总体判断如上所述,如果失败我们即查询下一个数next(),countSumArray[offset]=21,那么就是查到头了,就返回查找back()。

        用到了几个技巧,就是将BigInteger的运算结果直接存储到hashtable中去,可以节约大量运算时间。题中给予了4分钟的时间,以为很需要一段时间,就设置了多线程,后来发现,不使用多线程也只要花费2秒种,多线程的意义也就不复存在了。

 

        应楼下朋友要求,贴图描述解题思路,很少画图,更没用Dia画过图,有粗制滥造之嫌,请勿怪了。。。

 

[代码实现]

import java.math.BigInteger;
import java.util.Hashtable;

public class Main {

    private static final int SIZE = 21;
    private int[] countArray = new int[10]; // 个数列表
    private int[] countSumArray = new int[10]; // 个数总数
    private BigInteger[] sumArray = new BigInteger[10];// 值总数
    private int offset = 0;// 浮标

    /**
     * 设置当前浮标对应的个数,个数的总数,值总数
     *
     * @param num
     *            个数
     */
    private void setValue(int num) {
        countArray[offset] = num;
        if (offset == 0) {
            countSumArray[offset] = num;
            sumArray[offset] = p(9 - offset).multiply(n(num));
        } else {
            countSumArray[offset] = countSumArray[offset - 1] + num;
            sumArray[offset] = sumArray[offset - 1].add(p(9 - offset).multiply(n(num)));
        }
    }

    /**
     * 检验当前数据是否匹配
     *
     * @return
     */
    private boolean checkPersentArray() {
        BigInteger minVal = sumArray[offset];// 当前已存在值
        BigInteger maxVal = sumArray[offset].add(p(9 - offset).multiply(n(SIZE - countSumArray[offset])));// 当前已存在值+可能存在的最大值
        // 最小值匹配
        if (minVal.compareTo(MAX) > 0) {
            return false;
        }
        // 最大值匹配
        if (maxVal.compareTo(MIN) < 0) {
            return false;
        }
        String minStr = minVal.compareTo(MIN) > 0 ? minVal.toString() : MIN.toString();
        String maxStr = maxVal.compareTo(MAX) < 0 ? maxVal.toString() : MAX.toString();
        // 找到最小值与最大值间首部相同的部分
        int[] sameCountArray = new int[10];
        for (int i = 0; i < SIZE; i++) {
            char c;
            if ((c = minStr.charAt(i)) == maxStr.charAt(i)) {
                sameCountArray[c - '0'] = sameCountArray[c - '0'] + 1;
            } else {
                break;
            }
        }
        // 判断如果相同部分有数据大于现在已记录的位数,返回false
        for (int i = 0; i <= offset; i++) {
            if (countArray[i] < sameCountArray[9 - i]) {
                return false;
            }
        }
        // 如果当前值的总数为SIZE位,那么判断该值是不是需要查找的值
        if (countSumArray[offset] == SIZE) {
            String sumStr = sumArray[offset].toString();
            BigInteger sum = ZERO;
            for (int i = 0; i < sumStr.length(); i++) {
                sum = sum.add(p(sumStr.charAt(i) - '0'));
            }
            return sum.compareTo(sumArray[offset]) == 0;
        }
        return true;
    }

    /**
     * 退出循环,打印
     *
     * @return
     */
    private void success() {
        System.out.println("find a match number:" + sumArray[offset]);
    }

    /**
     * 将浮标指向下一位数
     *
     * @return
     */
    private void next() {
        offset++;
        setValue(SIZE - countSumArray[offset - 1]);
    }

    /**
     *
     * 回退浮标,找到最近的浮标,并减一
     *
     * @return
     */
    private boolean back() {
        // 回退浮标,找到最近的浮标,并减一
        if (countArray[offset] == 0) {
            while (countArray[offset] == 0) {
                if (offset > 0) {
                    offset--;
                } else {
                    return true;
                }
            }
        }
        if (offset > 0) {
            setValue(countArray[offset] - 1);
            return false;
        } else {
            return true;
        }
    }

    /**
     * 测试程序
     *
     * @param startValue
     *            测试匹配数中包含9的个数
     * @param startTime
     *            程序启动时间
     */
    private void test(int startValue, long startTime) {
        // 设置9的个数
        offset = 0;
        setValue(startValue);
        while (true) {
            if (checkPersentArray()) {// 检查当前提交数据是否匹配
                // 匹配且总数正好为SIZE的位数,那么就是求解的值
                if (countSumArray[offset] == SIZE) {
                    success();
                }
                // 总数不为SIZE,且当前值不在第10位(即不等于0)
                if (offset != 9) {
                    next();
                    continue;
                }
                // 总数不为SIZE,且当前值在第10位。
                if (back()) {
                    break;
                }
            } else {
                if (back()) {
                    break;
                }
            }
        }

        System.out.println(Thread.currentThread() + " End,Spend time " + (System.currentTimeMillis() - startTime) / 1000 + "s");
    }

    /**
     * 主函数
     */
    public static void main(String[] args) {
        final long startTime = System.currentTimeMillis(); 
            int s = MAX.divide(p(9)).intValue();
            for (int i = 0; i <= s; i++) {
//            new Main().test(i, startTime);
            // 启动十个线程同时运算
            final int startValue = i;
            new Thread(new Runnable() {

                public void run() {
                    new Main().test(startValue, startTime);
                }
            }).start();
        }
    }
    private static final BigInteger ZERO = new BigInteger("0");
    private static final BigInteger MIN;
    private static final BigInteger MAX;

    /**
     * 0-SIZE间的BigInteger
     */
    private static final BigInteger n(int i) {
        return (BigInteger) ht.get("n_" + i);
    }

    /**
     * 0-9的次方的BigInteger
     */
    private static final BigInteger p(int i) {
        return (BigInteger) ht.get("p_" + i);
    }
    /**
     * 用于缓存BigInteger数据,并初始化0-SIZE间的BigInteger和0-9的次方的BigInteger
     */
    private static Hashtable<String, Object> ht = new Hashtable<String, Object>();

    static {
        int s = SIZE < 10 ? 10 : SIZE;
        for (int i = 0; i <= s; i++) {
            ht.put("n_" + i, new BigInteger(String.valueOf(i)));
        }
        for (int i = 0; i <= 10; i++) {
            ht.put("p_" + i, new BigInteger(String.valueOf(i)).pow(SIZE));
        }
        MIN = n(10).pow(SIZE - 1);
        MAX = n(10).pow(SIZE).subtract(n(1));
    }
} 

 

[结论]

        运算结果如下:

Thread[Thread-0,5,main] End,Spend time 0s
Thread[Thread-9,5,main] End,Spend time 0s
Thread[Thread-5,5,main] End,Spend time 0s
Thread[Thread-8,5,main] End,Spend time 0s
find a match number:449177399146038697307
Thread[Thread-4,5,main] End,Spend time 1s
Thread[Thread-7,5,main] End,Spend time 1s
Thread[Thread-6,5,main] End,Spend time 1s
Thread[Thread-3,5,main] End,Spend time 2s
find a match number:128468643043731391252
Thread[Thread-2,5,main] End,Spend time 3s
Thread[Thread-1,5,main] End,Spend time 3s
VSCode 降低适用版本并且关闭自动更新 官方表示,提高了远程服务器构建工具链的最低要求,更新了对 glibc 的要求,需要最低2.28版本。最快的方法是把vs code的版本降到1.85(为什么是这个版本呢?打开VS Code的官方网站(https://code.visualstudio.com/)通过查看可以知道,具体的版本没有直接显示分类,他是根据发布日期来分类的同时和版本匹配。在网站的顶部导航栏中找到”Updates”按钮,点击进入下载页面。找了半天一直没发现,原来就在紧挨着的下面。远程主机不满足运行vs code服务器的先决条件。 阅读详情

相关推荐

QT-使用ECharts显示地图,还有更多惊艳的图表等待你的使用

QT-使用ECharts显示地图前言一、使用步骤1.下载示例2. QT使用HTML文件配置3.使用HTML文件4. 数据交互及自适应总结注意:这个地图是百度的在线地图,需要百度API密钥和在联网的状态下使用。离线使用图表 前言 我上文QT图表chart-饼状图,使用qt自带的图表库charts来显示图表,实现数据可视化,但是QT自带的图表库显示效果并不佳,通俗说,就是不好看。 然而,有这么一个开源图表库,图表种类极多,并且效果良好,能够实现具有很好的视觉冲击的数据可视化。 它就是Apache ECharts

博咯的博客 4898

【每日一道算法题】

NO.1若有 18 个元素的有序表存放在一维数组 A[19] 中,第一个元素放 A[1] 中,现进行二分查找,则查找 A [ 3 ]的比较序列的下标依次为 (      )A.1,2,3B.9,5,2,3C.9,5,3D.9,4,2,3答案:D.第一次查找,队首为下标1,队尾下标18,所以是(1+18)/2=9第二次查找,队首为1,队尾为9-1=8,所以是(1+8)/2=4第三次,队首1,队尾4-

In客笔记 761

【MM小贴士】关于SAP系统中关税的处理方式

的运费,这部分来讲,我们关税的计价基础,就应该是货物价值+包邮到中国港口的运费,完后进行报关操作,缴纳关税,后半段从中国港口拉回到企业里面的运输费,就会做到费用里面去,而不是计入到存货成本。1、我们自己的企业和国外厂家A购买一项物资,厂家A的报价会分成几个组成部分货物本身的价值或者货物本身的价值+到国外港口的运费或者货物本身的价值+到中国港口的运费或者货物本身的价值+包邮到你们自己企业的整体运费。所以我的关税就是不确定的。重点是运费是哪一段的运费,如何结算,结算的货币,何时能够确定运费。.........

竹大的博客 9029

一道算法题我的思路

摘要:本文对比了两道算法题的区别。一道题目要求判断行李总大小是否不超过容器容量,直接计算总和即可;另一道鸿蒙认证的背包问题则需要先计算物品排列组合,再与背包容量比较。后者计算更复杂,涉及组合数学(如C(5,1)到C(5,5)),还需考虑物品剔除规则。两题核心差异在于前者是简单求和判断,后者需要组合计算与容量比较。(94字)

humors221的专栏 417

RK3588s Camera ISP调优详解(一)ISP调试工具及环境配置(附下载途径)

本文主要介绍了rkisp tuner工具的连接与使用,并附上了工具及依赖库的下载地址

zongheng_raobo的博客 6273

树莓派5安装ROS1避坑指南:从系统配置到cmake报错全解决

本文详细介绍了在树莓派5上安装ROS1的完整流程,包括系统配置、环境变量设置、cmake报错解决等关键步骤。针对树莓派5的ARM架构特性,提供了从基础安装到高级调试的系统级解决方案,帮助开发者高效部署ROS1并避免常见编译陷阱。特别针对cmake报错问题,给出了深度解析和实用修复方法。

spark7igniter的博客 233

一道算法题-括号生成

最近看到一些年长的程序员在面试的时候,算法能力并不是很好。虽然随着职位的提升,写代码的时间减少,架构的时间增多,但代码能力不能丢

634

opencv实践项目-图片拼接之缝合线算法

opencv 图片缝合线算法

wyw0000的博客 2624

每天写一道算法题

今日开始立flag,每天写一道算法题,当然如果有比赛、项目、期末考等不可抗力,那也只能一声叹息,当我有时间的时候还是尽可能一天一道算法题。 写在博客上主要是给自己看的,记录下自己在写代码时候的思路、感悟、以及所犯的错误。 ...

a458895535的博客 257

matlab电偶极子源码,电偶极子的MATLAB场模拟

电偶极子周围电场电势分布的MATLAB三维模拟Matlab可以用于模拟四维场的效果,本文以电偶极子的电场和电势场为例展示MATLAB在该方面领域的应用。摘要电偶极子由一个正电荷和一个负电荷组成,其电场和空间电势分布与单电荷相比具有一些重要的特性。本文将给出电场和电势场的近似表达式。这两个量的精确表达式是复杂的,不太常用,因此 MATLAB 被采用来操作精确值和绘制三维分布的远区域, 近区域和一侧区...

weixin_35135480的博客 3150

工业激光器核心参数详解与选型实战:从理论到产线落地(五)

本文深入解析了脉冲激光加工的三大核心参数——脉宽、重复频率和脉冲能量的协同作用机制。通过公式推导和产线案例,揭示了参数设置对加工良率的关键影响:1. 脉宽决定热影响区大小,缩短脉宽可显著提升加工精度;2. 重复频率影响加工速度和热积累效应,需平衡效率与质量;3. 脉冲能量决定材料去除量,过大易导致加工缺陷。文中以ABS塑料打标、模具清洗和微孔钻孔为例,展示了参数优化如何将良率从65%提升至99%,并提供了不同应用场景的参数选型表和调试技巧,强调峰值功率比平均功率更具决定性作用。

weixin_44520581的博客 2421

分享一道算法题

来源:力扣《程序员面试金典》

渴望飞翔的小白 295

ROS机器人未知环境自主探索功能包explore_lite最全源码详细解析(五)

本系列文章主要针对ROS机器人常使用的未知环境自主探索功能包explore_lite展开全源码的详细解析,并进行概括总结。 本系列文章共包含六篇文章,前五篇文章主要介绍explore_lite功能包中 explore.cpp、costmap_tools.h、frontier_search.cpp、costmap_client.cpp等源码实现文件中全部函数的详细介绍,第六篇文章进行概括总结,包含自主探索功能包explore_lite的简介,实现未知环境自主探索功能的原理及流程总结,效果演示,使用功能包exp

慕羽★的博客 1511

每天一道算法题

【代码】每天一道算法题。

2402_83910930的博客 163

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

一道小面试算法题的思路

一道小算法题的思路 有这么一道小面试算法题:给定一个长度为 n 的整数数组,下标为 i 的元素表示第 i 天某个股票的价格,每次最多持有一股,每次买卖最多一股,在最多只买卖一次的情况下(先买后卖,不考虑融券等复杂形式),最大的收益是多少。 先考察一下可能的数据: 0 1 2 3 4 5 6 7 12 9 6 10 8 22 20 15 由于是先买后...

weixin_30755709的博客 162

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

一道值得思考的算法题

一道值得思考的算法题 这是楼主在做一道IMO的数论题中发现的,发现这个问题可以转化为一道很有挑战性的算法题目,在这里提供给大家。 . 最少的加球次数 m个盒子中开始各放若干个球,每次加球时在其中n (n<m) 个盒子中各加1个球,且满足n与m的最大公约数为1,给定数m,n以及m个盒子当中分别的球的个数,试求最小的加球次数使得各盒子中的球数相等。 示例: m=2,n=1 ball = [2,1...

CCJJKKWW的博客 198

怀旧版电脑GBC模拟器

再现原版GBC掌机的界面,电脑运行连游戏界面都一样大小,很怀旧,很真实,(英文版)强烈推荐,很小的文件,很难才找到的。

从一道算法题开始,爱上Python编程

通过这道题目,我们不仅学会了如何分析程序框图,更重要的是学会了如何用Python编写代码来解决实际问题。Python的简洁和易读性使得编程变得更加有趣和高效。希望这篇文章能激发你对Python编程的兴趣。如果你有任何疑问或建议,欢迎在评论区留言讨论。

FLK_9090的博客 467

一道很巧妙的入门算法题

下面是算法原题(来自蓝桥杯) 我对本题的一些理解,方法特别简单,希望大家可以用到。 本题要求是输入一个三个数,w,m,n。w代表每一层有几个房子,m和n分别代表房子号,应题要求房子号是按s型排列的,也就是说奇数楼层排列正常,偶数楼层逆序排列,要求输出的是两个房子号之间的最短距离。如果本题房子都是按顺序排列,那么最后求两个房子之间的距离只需要拿两个房子层数差加它们相对每层的序号差就可获得最终答案,...

Pinyk的博客 531

一道大学算法题

一道算法题 在一个盒子里有n个球,每次可以摸出1个,2个,…,n个球,问要将球全部摸出一共有几种方法。(用递归) 我的想法是在for循环里调用函数实现递归,但具体写法总是有所偏差,求大佬提供个解析 ...

qq_52900982的博客 327

一道算法题。

题目是这样的:N team competitions, numbered 0,1,2 respectively. . . . N-1, known comparison of strength of relationship, stored in a two-dimensional array w[n][n ], w[i][j ] represents the value of a number

iicooder的专栏 676

阿里巴巴一道算法题

上网中无意看到一道阿里巴巴的算法题,题目如下: 字符串数组seq[] = a,b,c,d,aa,ba,ca,da,ab,bb,cb,db,ac...,aaa,aba,... (1)aaa是第几个字符串 (2)ababacd是第几个 (3)第1000个字符串是什么 (4)编写函数find(),返回字符串在seq中是第几个(语言不限) 网上有很多答案,大多千篇一律的错误解答如下: 错

irving8的专栏 657

每日一道算法题之———移除元素

先上原题: 本人也是有点意外,这道题竟然打败了100%的人哈哈。下面上图 下面给出解题代码,请自行分析结果,由于本人之前没接触过算法题,每一题都是按照自己的思路来解题,过程中速度并不块,想这些在力扣网上简单的算法题也要思考十几分钟,甚至更多: public class Solution { public static void main(String[] args) { int nums[] = {3, 2, 2, 3}; System.out.println(r

weixin_47876844的博客 185

一道微软算法题的java解法

上回看到一道微软的算法题,题目大概是说:有一个很大的整型数组,我们怎样能够找到一个不在这个数组中的值。 假设一个数组的大小很大(比如说35000个值),我们知道整型的取值范围在某种c/c++编译器中是16位的,也就只能到65536。那么怎样才能通过最少的代价找到不在这个数组中的整型值呢?! 解答: 1、我们可以利用像数数字一样的算法,来解答这个问题。比如说我们数1到10的时候,如果1到10都是连续

waitforpwj的专栏 807

一道算法题启发——HashMap分析

HashMap 一、概述 HashMap作为面试的常考题,对一个Java开发者来说是要掌握相关知识点的。以下文章是对HashMap的resize()函数流程理解。 二、算法概述 在了解resize()函数之前可以先考一道算法题(如果不想看,可以直接跳过,进入resize()分析),我发现他们有着类似的实现。题目来自《程序员代码面试指南》。不得不佩服左神???????? 题目描述如下: 给定一个链表,再给定一个...

Shiro框架02 340

优快云的小伙伴问的一道算法题

今天上午,一个优快云初学JAVA的小伙伴问了一道算法题,如下 根据邮件的重量和用户是否选择加急计算邮费。计算规则:重量在1000克以内(含1000克) ,基本费12元。超过1000克的部分,每500克加收超重费4元,不足500克部分按500克计算。如果用户选择加急,多收10元。输入邮件重量(整数,单位为克),输入一个字符表示是否加急(y表示加急;n表示不加急),输出一个整数,表示邮费。 题目...

优质后端技术知识记录 5757

链表的游标实现 - 一道算法题

链表的游标实现 - 一道算法题输入包含多组数据。每组数据占一行,包含不超过100000个字母、下划线、字符“[”或者“]”。其中字符“[”表示Home键,“]”表示End键。输入结束标志为文件结束符(EOF)。输 入文件不超过5MB。对于每组数据,输出一行,即屏幕上的悲剧文本。//样例输入: This_is_a_[Beiju]_text [[]][][]Happy_Birthday_to_Tsin

啦啦啦 963
上一篇: 第15届问答大赛获奖感言
下一篇: 一道算法题(二)
CaiHuajiang
博客等级 码龄17年 3粉丝 8原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值