21位水仙花数

本文介绍了一种高效算法,用于寻找特定位数的水仙花数,通过巧妙的数据结构设计,显著减少了搜索时间和复杂度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


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

 

[原题]

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

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

 

[解决思路]

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

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

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

Java代码 复制代码  收藏代码
  1. private int[] countArray = new int[10]; // 个数列表   
  2. private int[] countSumArray = new int[10]; // 个数总数   
  3. private BigInteger[] sumArray = new BigInteger[10];// 值总数   
  4. private int offset = 0;// 浮标  
	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画过图,有粗制滥造之嫌,请勿怪了。。。

 

[代码实现]

Java代码 复制代码  收藏代码
  1. import java.math.BigInteger;   
  2. import java.util.Hashtable;   
  3.   
  4. public class Main {   
  5.   
  6.     private static final int SIZE = 21;   
  7.     private int[] countArray = new int[10]; // 个数列表   
  8.     private int[] countSumArray = new int[10]; // 个数总数   
  9.     private BigInteger[] sumArray = new BigInteger[10];// 值总数   
  10.     private int offset = 0;// 浮标   
  11.   
  12.     /**  
  13.      * 设置当前浮标对应的个数,个数的总数,值总数  
  14.      *  
  15.      * @param num  
  16.      *            个数  
  17.      */  
  18.     private void setValue(int num) {   
  19.         countArray[offset] = num;   
  20.         if (offset == 0) {   
  21.             countSumArray[offset] = num;   
  22.             sumArray[offset] = p(9 - offset).multiply(n(num));   
  23.         } else {   
  24.             countSumArray[offset] = countSumArray[offset - 1] + num;   
  25.             sumArray[offset] = sumArray[offset - 1].add(p(9 - offset).multiply(n(num)));   
  26.         }   
  27.     }   
  28.   
  29.     /**  
  30.      * 检验当前数据是否匹配  
  31.      *  
  32.      * @return  
  33.      */  
  34.     private boolean checkPersentArray() {   
  35.         BigInteger minVal = sumArray[offset];// 当前已存在值   
  36.         BigInteger maxVal = sumArray[offset].add(p(9 - offset).multiply(n(SIZE - countSumArray[offset])));// 当前已存在值+可能存在的最大值   
  37.         // 最小值匹配   
  38.         if (minVal.compareTo(MAX) > 0) {   
  39.             return false;   
  40.         }   
  41.         // 最大值匹配   
  42.         if (maxVal.compareTo(MIN) < 0) {   
  43.             return false;   
  44.         }   
  45.         String minStr = minVal.compareTo(MIN) > 0 ? minVal.toString() : MIN.toString();   
  46.         String maxStr = maxVal.compareTo(MAX) < 0 ? maxVal.toString() : MAX.toString();   
  47.         // 找到最小值与最大值间首部相同的部分   
  48.         int[] sameCountArray = new int[10];   
  49.         for (int i = 0; i < SIZE; i++) {   
  50.             char c;   
  51.             if ((c = minStr.charAt(i)) == maxStr.charAt(i)) {   
  52.                 sameCountArray[c - '0'] = sameCountArray[c - '0'] + 1;   
  53.             } else {   
  54.                 break;   
  55.             }   
  56.         }   
  57.         // 判断如果相同部分有数据大于现在已记录的位数,返回false   
  58.         for (int i = 0; i <= offset; i++) {   
  59.             if (countArray[i] < sameCountArray[9 - i]) {   
  60.                 return false;   
  61.             }   
  62.         }   
  63.         // 如果当前值的总数为SIZE位,那么判断该值是不是需要查找的值   
  64.         if (countSumArray[offset] == SIZE) {   
  65.             String sumStr = sumArray[offset].toString();   
  66.             BigInteger sum = ZERO;   
  67.             for (int i = 0; i < sumStr.length(); i++) {   
  68.                 sum = sum.add(p(sumStr.charAt(i) - '0'));   
  69.             }   
  70.             return sum.compareTo(sumArray[offset]) == 0;   
  71.         }   
  72.         return true;   
  73.     }   
  74.   
  75.     /**  
  76.      * 退出循环,打印  
  77.      *  
  78.      * @return  
  79.      */  
  80.     private void success() {   
  81.         System.out.println("find a match number:" + sumArray[offset]);   
  82.     }   
  83.   
  84.     /**  
  85.      * 将浮标指向下一位数  
  86.      *  
  87.      * @return  
  88.      */  
  89.     private void next() {   
  90.         offset++;   
  91.         setValue(SIZE - countSumArray[offset - 1]);   
  92.     }   
  93.   
  94.     /**  
  95.      *  
  96.      * 回退浮标,找到最近的浮标,并减一  
  97.      *  
  98.      * @return  
  99.      */  
  100.     private boolean back() {   
  101.         // 回退浮标,找到最近的浮标,并减一   
  102.         if (countArray[offset] == 0) {   
  103.             while (countArray[offset] == 0) {   
  104.                 if (offset > 0) {   
  105.                     offset--;   
  106.                 } else {   
  107.                     return true;   
  108.                 }   
  109.             }   
  110.         }   
  111.         if (offset > 0) {   
  112.             setValue(countArray[offset] - 1);   
  113.             return false;   
  114.         } else {   
  115.             return true;   
  116.         }   
  117.     }   
  118.   
  119.     /**  
  120.      * 测试程序  
  121.      *  
  122.      * @param startValue  
  123.      *            测试匹配数中包含9的个数  
  124.      * @param startTime  
  125.      *            程序启动时间  
  126.      */  
  127.     private void test(int startValue, long startTime) {   
  128.         // 设置9的个数   
  129.         offset = 0;   
  130.         setValue(startValue);   
  131.         while (true) {   
  132.             if (checkPersentArray()) {// 检查当前提交数据是否匹配   
  133.                 // 匹配且总数正好为SIZE的位数,那么就是求解的值   
  134.                 if (countSumArray[offset] == SIZE) {   
  135.                     success();   
  136.                 }   
  137.                 // 总数不为SIZE,且当前值不在第10位(即不等于0)   
  138.                 if (offset != 9) {   
  139.                     next();   
  140.                     continue;   
  141.                 }   
  142.                 // 总数不为SIZE,且当前值在第10位。   
  143.                 if (back()) {   
  144.                     break;   
  145.                 }   
  146.             } else {   
  147.                 if (back()) {   
  148.                     break;   
  149.                 }   
  150.             }   
  151.         }   
  152.   
  153.         System.out.println(Thread.currentThread() + " End,Spend time " + (System.currentTimeMillis() - startTime) / 1000 + "s");   
  154.     }   
  155.   
  156.     /**  
  157.      * 主函数  
  158.      */  
  159.     public static void main(String[] args) {   
  160.         final long startTime = System.currentTimeMillis();    
  161.             int s = MAX.divide(p(9)).intValue();   
  162.             for (int i = 0; i <= s; i++) {   
  163. //            new Main().test(i, startTime);   
  164.             // 启动十个线程同时运算   
  165.             final int startValue = i;   
  166.             new Thread(new Runnable() {   
  167.   
  168.                 public void run() {   
  169.                     new Main().test(startValue, startTime);   
  170.                 }   
  171.             }).start();   
  172.         }   
  173.     }   
  174.     private static final BigInteger ZERO = new BigInteger("0");   
  175.     private static final BigInteger MIN;   
  176.     private static final BigInteger MAX;   
  177.   
  178.     /**  
  179.      * 0-SIZE间的BigInteger  
  180.      */  
  181.     private static final BigInteger n(int i) {   
  182.         return (BigInteger) ht.get("n_" + i);   
  183.     }   
  184.   
  185.     /**  
  186.      * 0-9的次方的BigInteger  
  187.      */  
  188.     private static final BigInteger p(int i) {   
  189.         return (BigInteger) ht.get("p_" + i);   
  190.     }   
  191.     /**  
  192.      * 用于缓存BigInteger数据,并初始化0-SIZE间的BigInteger和0-9的次方的BigInteger  
  193.      */  
  194.     private static Hashtable<String, Object> ht = new Hashtable<String, Object>();   
  195.   
  196.     static {   
  197.         int s = SIZE < 10 ? 10 : SIZE;   
  198.         for (int i = 0; i <= s; i++) {   
  199.             ht.put("n_" + i, new BigInteger(String.valueOf(i)));   
  200.         }   
  201.         for (int i = 0; i <= 10; i++) {   
  202.             ht.put("p_" + i, new BigInteger(String.valueOf(i)).pow(SIZE));   
  203.         }   
  204.         MIN = n(10).pow(SIZE - 1);   
  205.         MAX = n(10).pow(SIZE).subtract(n(1));   
  206.     }   
  207. }   
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));
    }
} 

 

[结论]

        运算结果如下:

Console代码 复制代码  收藏代码
  1. Thread[Thread-0,5,main] End,Spend time 0s   
  2. Thread[Thread-9,5,main] End,Spend time 0s   
  3. Thread[Thread-5,5,main] End,Spend time 0s   
  4. Thread[Thread-8,5,main] End,Spend time 0s   
  5. find a match number:449177399146038697307  
  6. Thread[Thread-4,5,main] End,Spend time 1s   
  7. Thread[Thread-7,5,main] End,Spend time 1s   
  8. Thread[Thread-6,5,main] End,Spend time 1s   
  9. Thread[Thread-3,5,main] End,Spend time 2s   
  10. find a match number:128468643043731391252  
  11. Thread[Thread-2,5,main] End,Spend time 3s   
  12. Thread[Thread-1,5,main] End,Spend time 3s  

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值