33,010
社区成员
发帖
与我相关
我的任务
分享
1. 2
2. 3
3. 4
4. 5
5. 6
6. 7
7. 8
8. 9
9. 153
10. 370
11. 371
12. 407
13. 1634
14. 8208
15. 9474
16. 54748
17. 92727
18. 93084
19. 548834
20. 1741725
21. 4210818
22. 9800817
23. 9926315
24. 24678050
25. 24678051
26. 88593477
27. 146511208
28. 472335975
29. 534494836
30. 912985153
31. 4679307774
32. 32164049650
33. 32164049651
34. 40028394225
35. 42678290603
36. 44708635679
37. 49388550606
38. 82693916578
39. 94204591914
40. 28116440335967
41. 4338281769391370
42. 4338281769391371
43. 21897142587612075
44. 35641594208964132
45. 35875699062250035
46. 1517841543307505039
47. 3289582984443187032
48. 4498128791164624869
49. 4929273885928088826
50. 63105425988599693916
51. 128468643043731391252
52. 449177399146038697307
53. 21887696841122916288858
54. 27879694893054074471405
55. 27907865009977052567814
56. 28361281321319229463398
57. 35452590104031691935943
58. 174088005938065293023722
59. 188451485447897896036875
60. 239313664430041569350093
61. 1550475334214501539088894
62. 1553242162893771850669378
63. 3706907995955475988644380
64. 3706907995955475988644381
65. 4422095118095899619457938
66. 121204998563613372405438066
67. 121270696006801314328439376
68. 128851796696487777842012787
69. 174650464499531377631639254
70. 177265453171792792366489765
71. 14607640612971980372614873089
72. 19008174136254279995012734740
73. 19008174136254279995012734741
74. 23866716435523975980390369295
75. 1145037275765491025924292050346
76. 1927890457142960697580636236639
77. 2309092682616190307509695338915
78. 17333509997782249308725103962772
79. 186709961001538790100634132976990
80. 186709961001538790100634132976991
81. 1122763285329372541592822900204593
82. 12639369517103790328947807201478392
83. 12679937780272278566303885594196922
84. 1219167219625434121569735803609966019
85. 12815792078366059955099770545296129367
86. 115132219018763992565095597973971522400
87. 115132219018763992565095597973971522401
using System;
using System.Numerics;
namespace CSharpTest
{
class Program
{
private static BigInteger[] PowerOf10;
private static BigInteger[,] PreTable;
private static BigInteger[,] PreTable2;
private static int[,] PreTable3;
private static int[] Selected = new int[10];
private static int Length;
private static int Count = 0;
public static void Main()
{
DateTime begin = DateTime.Now;
for (int i = 1; i < 40; i++)
{
InitPre(i);
Search(9, 0, i);
}
Console.WriteLine(DateTime.Now - begin);
}
private static void InitPre(int n)
{
PowerOf10 = new BigInteger[n + 1];
PowerOf10[0] = 1;
Length = n;
for (int i = 1; i <= n; i++)
PowerOf10[i] = PowerOf10[i - 1] * 10;
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++)
{
PreTable[i, j] = BigInteger.Pow(i, n) * j;
PreTable2[i, j] = PowerOf10[Length - 1] - PreTable[i, j];
for (int k = n; k >= 0; k--)
{
if (PowerOf10[k] < PreTable[i, j])
{
PreTable3[i, j] = k;
break;
}
}
}
}
}
private static bool PreCheck(int currentIndex, BigInteger sum, int remainCount)
{
if (sum < PreTable[currentIndex, remainCount])
return true;
BigInteger max = sum + PreTable[currentIndex, remainCount];
max /= PowerOf10[PreTable3[currentIndex, remainCount]];
sum /= PowerOf10[PreTable3[currentIndex, remainCount]];
while (max != sum)
{
max /= 10;
sum /= 10;
}
if (max == 0)
return true;
int[] counter = GetCounter(max);
for (int i = 9; i > currentIndex; i--)
if (counter[i] > Selected[i])
return false;
for (int i = 0; i <= currentIndex; i++)
remainCount -= counter[i];
return remainCount >= 0;
}
private static void Search(int currentIndex, BigInteger sum, int remainCount)
{
if (sum >= PowerOf10[Length])
return;
if (remainCount == 0)
{
if (sum > PowerOf10[Length - 1] && Check(sum))
{
Count++;
Console.WriteLine("{0}. {1}", Count, sum);
}
return;
}
if (!PreCheck(currentIndex, sum, remainCount))
return;
if (sum < PreTable2[currentIndex, remainCount])
return;
if (currentIndex == 0)
{
Selected[0] = remainCount;
Search(-1, sum, 0);
}
else
{
for (int i = 0; i <= remainCount; i++)
{
Selected[currentIndex] = i;
Search(currentIndex - 1, sum + PreTable[currentIndex, i], remainCount - i);
}
}
Selected[currentIndex] = 0;
}
private static bool Check(BigInteger sum)
{
int[] counter = GetCounter(sum);
for (int i = 0; i < 10; i++)
{
if (Selected[i] != counter[i])
return false;
}
return true;
}
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;
}
}
}
if (sum.compareTo(PowerOf10[Length - 1]) > 0 && Check(sum)) {// 见结束条件5
Count++;
System.out.print(Count + " ");
System.out.println(sum);
}
if (remainCount == 0) {// 没数可选时
if (Check(sum)) {// 见结束条件5
Count++;
System.out.print(Count + " ");
System.out.println(sum);
}
return;
}
if (sum.add(PreTable[currentIndex][remainCount]).compareTo(
PowerOf10[Length - 1]) < 0)// 见结束条件2
return;
import java.math.BigInteger;
/**
* 水仙花数:N位整数,它等于各位上的数字的N次方之和,例如有一个N位数字,a1a2a3a4.....aN = a1^N +a2^N+......aN^N
*
* 算法原理: 注意:以下 sum 为各位上的数字的N次方之和 sum_a为集合a中的数的sum
*
* 对于任意的N位数字,定义形如315,351,513等这样的数字都属于“1出现1次,3出现1次,5出现1次”的集合a
* 明显可以看出“包含在集合a中的数的sum都相同”,即sum_a="1^N(位数)*T1(1出现的次数)+3^N*T3+5^N*T5",
* 观察得,如果集合a包含水仙花数,则该水仙花数=水仙花数的sum(水仙花数定义)=sum_a(充要条件)。
* 可以随便举个反例215,512,125在集合b中,但b的sum_a=134明显不属于集合b,所以b不是包含水仙花数的集合
* 总结:将寻找水仙花数,转换为寻找包含水仙花数的集合,从而减少判断的次数。
*
* 结束条件:(楼主在这里进行了优化) 首先不是一次选完,而是从0到N个9,0到N个8...这样选,总数由remainCount控制 设当前情况为集合a
* 1.如果当sum_a大于最大数即10^N-1,矛盾 2.因最小的数字为10^(N -
* 1),注意到,如果选某数时sum_a小于最小值,则后面的情况不用讨论了。
* 例如3位数,已选1个3选2,发现sum_a最大为=3^3*1+2^3*2=43<100,可以断定不用选2,1,0了;
* (当前情况能表示的最大数:比如说3位数我选了1个9,8的情况选完了不行,现在开始选7,最大数就是977不可能是987)
* 3.判断sum_a和当前情况能表示的最大数首部相同部分中某数出现的次数是否比已经选择的集合中该数出现的次数多
* 设sum_a=99900当前情况能表示的最大数为99988,则当前情况的数肯定在这之间即999XX,而当前情况9已经选了且只选了1次,则矛盾。
* 4.同上:相同部分中还没选的数 的出现次数比剩余的总数还多 例如相同部分为789111,1还没选而且只剩2个数没选了,则矛盾
* 5.当选完所有数时如果sum_a属于集合a,则sum_a为水仙花数。
*
*/
public class NarcissisticNumber {
/**
* 记录10的0~N次方
*/
private static BigInteger[] PowerOf10;
/**
* 记录0到9中任意数字i的N次方乘以i出现的次数j的结果(i^N*j)
*/
private static BigInteger[][] PreTable;
/**
* 记录可能为水仙花数的下限与PreTable中对应数的差
*/
// private static BigInteger[][] PreTable2; 没什么用,变量定多了不容易理解
/**
* 记录离PreTable中对应数最近的10的k次方
*/
private static int[][] PreTable3;
/**
* 记录0到9中每个数出现的次数
*/
private static int[] Selected = new int[10];
/**
* 记录水仙花数的位数
*/
private static int Length;
/**
* 记录水仙花数出现的个数
*/
private static int Count = 0;
/**
* 记录当前的进制
*/
private static int NumberSystem = 10;
public static void main(String[] args) {
long time = System.nanoTime();
// for (int i = 1; i < 40; i++) {
NarcissisticNumber narcissisticNumber = new NarcissisticNumber(39);
narcissisticNumber.show();
// }
time = System.nanoTime() - time;
System.out.println("time:\t" + time / 1000000000.0 + "s");
}
// 初始化计算时使用的数据结构,这也是提高效率的地方
/**
* @param n
* 水仙花数的位数
*/
public NarcissisticNumber(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);
}
PreTable = new BigInteger[NumberSystem][n + 1];
// PreTable2 = new BigInteger[NumberSystem][n + 1];
PreTable3 = new int[NumberSystem][n + 1];
for (int i = 0; i < NumberSystem; i++) {
for (int j = 0; j <= n; j++) {
PreTable[i][j] = new BigInteger(new Integer(i).toString()).pow(
n).multiply(new BigInteger(new Integer(j).toString()));
// PreTable2[i][j] = PowerOf10[Length - 1]
// .subtract(PreTable[i][j]);
for (int k = n; k >= 0; k--) {
if (PowerOf10[k].compareTo(PreTable[i][j]) < 0) {
PreTable3[i][j] = k;
break;
}
}
}
}
}
private void show() {
Search(NumberSystem - 1, BigInteger.ZERO, Length);
}
/**
* @param currentIndex
* 记录当前正在选择的数字(0~9)
* @param sum
* 记录当前值(如选了3个9、2个8 就是9^N*3+8^N*2)
* @param remainCount
* 记录还可选择多少数
*/
private static void Search(int currentIndex, BigInteger sum, int remainCount) {
if (sum.compareTo(PowerOf10[Length]) >= 0)// 见结束条件1
{
return;
}
if (remainCount == 0) {// 没数可选时
if (sum.compareTo(PowerOf10[Length - 1]) > 0 && Check(sum)) {// 见结束条件5
Count++;
System.out.print(Count + " ");
System.out.println(sum);
}
return;
}
if (!PreCheck(currentIndex, sum, remainCount))// 见结束条件3,4
return;
if (sum.add(PreTable[currentIndex][remainCount]).compareTo(
PowerOf10[Length - 1]) < 0)// 见结束条件2
return;
if (currentIndex == 0) {// 选到0这个数时的处理
Selected[0] = remainCount;
Search(-1, sum, 0);
} else {
for (int i = 0; i <= remainCount; i++) {// 穷举所选数可能出现的情况
Selected[currentIndex] = i;
Search(currentIndex - 1, sum.add(PreTable[currentIndex][i]),
remainCount - i);
}
}
// 到这里说明所选数currentIndex的所有情况都遍历了
Selected[currentIndex] = 0;
}
/**
* @param currentIndex
* 记录当前正在选择的数字(0~9)
* @param sum
* 记录当前值(如选了3个9、2个8 就是9^N*3+8^N*2)
* @param remainCount
* 记录还可选择多少数
* @return 如果当前值符合条件返回true
*/
private static Boolean PreCheck(int currentIndex, BigInteger sum,
int remainCount) {
if (sum.compareTo(PreTable[currentIndex][remainCount]) < 0)// 判断当前值是否小于PreTable中对应元素的值
return true;// 说明还有很多数没选
BigInteger max = sum.add(PreTable[currentIndex][remainCount]);// 当前情况的最大值
max = max.divide(PowerOf10[PreTable3[currentIndex][remainCount]]);// 取前面一部分比较
sum = sum.divide(PowerOf10[PreTable3[currentIndex][remainCount]]);
while (!max.equals(sum)) {// 检验sum和max首部是否有相同的部分
max = max.divide(BigInteger.TEN);
sum = sum.divide(BigInteger.TEN);
}
if (max.equals(BigInteger.ZERO))// 无相同部分
return true;
int[] counter = GetCounter(max);
for (int i = 9; i > currentIndex; i--)
if (counter[i] > Selected[i])// 见结束条件3
return false;
for (int i = 0; i <= currentIndex; i++)
remainCount -= counter[i];
return remainCount >= 0;// 见结束条件4
}
/**
* @param sum
* 记录当前值(如选了3个9、2个8 就是9^N*3+8^N*2)
* @return 如果sum存在于所选集合中返回true
*/
private static Boolean Check(BigInteger sum) {
int[] counter = GetCounter(sum);
for (int i = 0; i < NumberSystem; i++) {
if (Selected[i] != counter[i])
return false;
}
return true;
}
/**
* @param value
* 需要检验的数
* @return 返回value中0到9出现的次数的集合
*/
public static int[] GetCounter(BigInteger value) {
int[] counter = new int[NumberSystem];
char[] sumChar = value.toString().toCharArray();
for (int i = 0; i < sumChar.length; i++)
counter[sumChar[i] - '0']++;
return counter;
}
}