上一篇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)