在Java上更快地找到所有自恋(阿姆斯特朗)数字

时间:2016-03-17 16:25:49

标签: java

有更有效的方法吗? 给定数字N - 找到所有自恋(阿姆斯特朗)数字< N. 这是我的代码,但我想有更高效的解决方案。也许,我们可以通过位操作解决它吗?

public static void main(String args[]) throws  Exception
 {
    long a = System.nanoTime();
    int t [] = getNumbers(4_483_647L);
    long b = System.nanoTime();
    System.out.println("Time totally "+(b-a));
    for(int k: t)
        System.out.print(k+" ");
}
public static int[] getNumbers(long N)
{

    int length=1;
    int porog=10, r=1, s=1;
    double k;
    LinkedList<Integer> list = new LinkedList<>();

    for(int i=1; i<N; i++)
    {
        if(i==porog)
        {
            length++;
            porog*=10;
        }
        s = i;
        k=0;
        while(s>0)
        {
            r = s%10;
            k+=Math.pow(r, length);
            if(k>i)break;
            s=s/10;
        }
        if((int)k==i)
            list.add(i);
    }
   int[] result  = new int[list.size()];
    int i=0;
    for(int n: list)
    {
        result[i] = n;
        i++;
    }

    return result;  }  }

5 个答案:

答案 0 :(得分:3)

一些观察结果:

  • 如果您的初始最大值是long,那么您的结果也应该是long类型,以防万一(int适合您,因为自恋数字相距很远)
  • 如果您将返回类型更改为“大”Long,则可以使用Collections.toArray()将结果重新打包到数组中......
  • ...虽然真的,你应该只返回链表......
  • 您无需继续重新计算权力。对于外循环中的每个十年,您只需要i ^ j,其中i = 0..9,j是当前十年中的位数
  • 事实上,你根本不需要Math.pow(),因为你可以在每个十年使用乘法

从上面的评论中应用我的想法并更改方法签名,你得到的东西运行速度提高了大约30倍:

public static Long[] getNumbers(long N) {
    int porog = 10;
    LinkedList<Long> list = new LinkedList<>();
    // initial powers for the number 0-9
    long[] powers = { 0l, 1l, 2l, 3l, 4l, 5l, 6l, 7l, 8l, 9l };

    for (long i = 1; i < N; i++) {
        if (i == porog) {
            porog *= 10;
            // calculate i^length
            for (int pi = 1; pi < 10; pi++) {
                powers[pi] *= pi;
            }
        }
        long s = i;
        long k = 0;
        while (s > 0) {
            int r = (int)(s % 10);
            k += powers[r];
            if (k > i)
                break;
            s /= 10;
        }

        if (k == i)
            list.add(i);
    }

    return list.toArray(new Long[]{});
}

答案 1 :(得分:2)

来自Rosetta Code blog(不是我自己的代码)

public static boolean isNarc(long x){
    if(x < 0) return false;

    String xStr = Long.toString(x);
    int m = xStr.length();
    long sum = 0;

    for(char c : xStr.toCharArray()){
        sum += Math.pow(Character.digit(c, 10), m);
    }
    return sum == x;
}

答案 2 :(得分:1)

主要优化是来检查范围内的所有数字(for(int i=1; i<N; i++))。看看here

答案 3 :(得分:1)

可以非常有效地生成阿姆斯特朗数字。例如,可以在10-15毫秒内生成所有整数。

我们可能会注意到,对于每个多组数字,例如[1, 1, 2, 4, 5, 7, 7],只有一个权力总和,而这些权力又可能是或者不是来自集合中的数字。在示例1^7 + 1^7 + 2^7 + 4^7 + 5^7 + 7^7 + 7^7 = 1741725中,可以用数字表示,因此是阿姆斯壮的数字。

我们可以基于这种考虑建立算法。

  1. 对于1 to N
  2. 的每个数字长度
  3. 生成所有可能的多组N位数
  4. 对于digits^N
  5. 的每个多组计算总和
  6. 检查是否可以代表我们在第4步中获得的数字 来自多组
  7. 的数字
  8. 如果是 - 将数字添加到结果列表
  9. 为每个长度N计算的案例数等于组合数(N + 9, 9) = (N+9)!/(9!N!)。因此,对于小于10的所有Ns,我们将仅生成92,377个多集。 N<20:20,030,009。

    请参阅GitHub以了解一些方法的说明,以及一些基准测试和Java代码。请享用! :)

答案 4 :(得分:1)

我不是专业的编码人员,只是自学成才,没有工作经验,因此,如果我的代码有点草率,我深表歉意。

我采用了dovetalk的解决方案,并且1)自己编写了它,以便更好地理解它。b)进行了一些调整,从而大大延长了运行时间。我希望这可以帮助其他人寻求有关此问题的帮助:

 public static long[] getNumbers(long N) {

        long tempII = N;
        LinkedHashSet<Long> narcNums = new LinkedHashSet<>();

        long tempResult;
        long digitLengthTemp = 10;
        long tempI;
        long[] powers = {0l, 1l, 2l, 3l, 4l, 5l, 6l, 7l, 8l, 9l};

        for (long i = 0; i < N; i++) {
            if (i == digitLengthTemp) {
                digitLengthTemp *= 10;
                for (short x = 2; x < powers.length; x++) powers[x] *= x;
            }
            //set value of top digits of numbers past first 3 to a remedial value
            tempI = i;
            long remedialValue = 0;
            tempI /= 10; tempI /= 10; tempI /= 10;

            while (tempI > 0) {
                short index = (short) (tempI % 10);
                remedialValue += powers[index];
                tempI /= 10;
            }
            //only passes 1000 at a time to this loop and adds each result to remedial top half
            for (int j = 0; j < (tempII > 1000 ? 1000 : tempII); j++) {
                //sets digit length and increases the values in array
                if (i == 0 && j == digitLengthTemp) {
                    digitLengthTemp *= 10;
                    for (short x = 2; x < powers.length; x++) powers[x] *= x;

                }
                //resets temp results
                tempResult = remedialValue;
                tempI = j;


                //gets the sum of each (digit^numberLength) of number passed to it
                while (tempI > 0) {
                    if (tempResult > i + j) break;
                    short index = (short) (tempI % 10);
                    tempResult += powers[index];
                    tempI /= 10;
                }

                //checks if sum equals original number
                if (i + j == tempResult) narcNums.add(i + j);
            }
            i += 999; // adds to i in increments of 1000
            tempII -= 1000;
        }

        //converts to long array
        long[] results = new long[narcNums.size()];
        short i = 0;
        for (long x : narcNums) {
            results[i++] = x;
        }
        return results;
}