锁定老帖子 主题:一道算法题
精华帖 (2) :: 良好帖 (16) :: 新手帖 (0) :: 隐藏帖 (1)
|
|
---|---|
作者 | 正文 |
发表时间:2010-09-15
最后修改:2010-09-17
经楼下朋友提醒,我这个算法求出的正好是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 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2010-09-15
怎么看起来像水仙花数
|
|
返回顶楼 | |
发表时间:2010-09-15
lzj0470 写道 怎么看起来像水仙花数
就是21位的水仙花数。 你一提醒,想起来了。。 |
|
返回顶楼 | |
发表时间:2010-09-15
lz的精神我比较佩服.别人的问题当作自己的问题高度来处理.
|
|
返回顶楼 | |
发表时间:2010-09-15
那lz不是白忙活了啊!水仙花数这样处理也太复杂了啊!!!
|
|
返回顶楼 | |
发表时间:2010-09-15
嘿嘿,为了效率呗,理论了水仙花数最大为34位。
我稍微改了下,这个方法求解34位的水仙花数,也不过半分多钟。 |
|
返回顶楼 | |
发表时间:2010-09-15
最后修改:2010-09-15
娘的,是那里看到的说水仙花数最高位为34位,我就发现39位上还有两个解,
40位上没了。 注:百度害死人哟 |
|
返回顶楼 | |
发表时间:2010-09-16
比较牛,曲高和寡。
|
|
返回顶楼 | |
发表时间:2010-09-16
yangguo 写道 比较牛,曲高和寡。
不是说我吧,我还没这资格。。 |
|
返回顶楼 | |
发表时间:2010-09-16
为什么一开始假设9个9呢???
总共21个数不能有10个9,11个9。。。20个9吗?? |
|
返回顶楼 | |