编程中的特殊数字挑战

时间:2016-12-15 09:23:52

标签: algorithm

首先,抱歉我的英语不好 特殊数字是数字的总和可以对数字的数字整除的数字。
示例: 135是一个特殊数字,因为数字的总和是1 + 3 + 5 = 9,数字的数字是3,9可以被3整除,因为9% 3 == 0。¥2,3,9,13,17,15,225,14825也是特殊数字。

要求:
编写一个程序,该程序从名为SNUMS.INP的文件中读取数字n(n <= 10 ^ 6)(SNUMS.INP最多可包含10 ^ 6个数字)并将结果打印到文件SNUMS中.OUT。数字n是特殊数字的顺序,结果将是n阶的特殊数字(抱歉,我不知道如何表达它)。
示例: n = 3表示你必须打印出第3个特殊号码,即3,n = 10你需要打印出第10个特殊号码,即11号,n = 13你必须打印出13号特殊号码是17,n = 15你必须打印出第15个特殊号码,即20。

下面的示例将演示文件SNUMS.INP和SNUMS.OUT(记住:SNUMS.INP最多可包含10 ^ 6个数字)
SNUMS.INP:
2
14
17
22
SNUMS.OUT:
2
19
24
35
我有自己的算法,但运行时间超过1秒(我的SNUMS.INP有10 ^ 6个数字)。所以我需要最佳的算法,以便运行时间小于或等于1秒。

伙计们我决定发布自己用Java编写的代码,运行总是需要4秒多。你们可以提出一些改进建议或如何让它更快地运行

import java.util.Scanner;
import java.io.*;
public class Test
{
    public static void main(String[]args) throws IOException
    {
        File file = new File("SNUMS.INP");
        Scanner inputFile = new Scanner(file);

        int order = 1;
        int i = 1;
        int[] special = new int[1000000+1];

        // Write all 10^6 special numbers into an array named "special"
        while (order <= 1000000)
        {
            if (specialNumber(i) == true)
            {
                special[order] = i;
                order++;
            }
            i++;
        }

        // Write the result to file
        PrintWriter outputFile = new PrintWriter("SNUMS.OUT");
        outputFile.println(special[inputFile.nextInt()]);
        while (inputFile.hasNext())
            outputFile.println(special[inputFile.nextInt()]);
        outputFile.close();
    }

    public static boolean specialNumber(int i)
    {
        // This method check whether the number is a special number
        boolean specialNumber = false;
        byte count=0;
        long sum=0;
        while (i != 0)
        {
            sum = sum + (i % 10);
            count++;
            i = i / 10;
        }
        if (sum % count == 0) return true;
        else return false;
     }
}

这是文件SNUMS.INP(示例)包含10 ^ 6个数字,如果你们想要测试。 https://drive.google.com/file/d/0BwOJpa2dAZlUNkE3YmMwZmlBOTg/view?usp=sharing

2 个答案:

答案 0 :(得分:0)

我已经设法在Core i7 3.2 GHz的C#6.0(.Net 4.6 IA-64)0.6秒内用HDD 7200 rpc解决了这个问题。希望预先计算在您的工作站上足够快:

// Precompute beautiful numbers
private static int[] BeautifulNumbers(int length) {
  int[] result = new int[length];

  int index = 0;

  for (int i = 1; ; ++i) {
    int sum = 0;
    int count = 0;

    for (int v = i; v > 0; sum += v % 10, ++count, v /= 10)
      ;

    if (sum % count == 0) {
      result[index] = i;

      if (++index >= result.Length)
        return result;
    }
  } 
}

...

// Test file with 1e6 items
File.WriteAllLines(@"D:\SNUMS.INP", Enumerable
  .Range(1, 1000000)
  .Select(index => index.ToString()));

...

Stopwatch sw = new Stopwatch();

sw.Start();

// Precomputed numbers (about 0.3 seconds to be created)
int[] data = BeautifulNumbers(1000000);

// File (about 0.3 seconds for both reading and writing)
var result = File
  .ReadLines(@"D:\SNUMS.INP")
  .Select(line => data[int.Parse(line) - 1].ToString());

File.WriteAllLines(@"D:\SNUMS.OUT", result);

sw.Stop();

Console.Write("Elapsed time {0}", sw.ElapsedMilliseconds);

输出与

不同
Elapsed time 516

Elapsed time 660

平均经过时间约为580毫秒

答案 1 :(得分:0)

现在您已经在下面实现了算盘的比喻,这里有一些提示

  1. 而不是仅仅在一个周期内以1递增,我们可以更积极地增加吗?确实,我们可以,但需要额外的关注。

    • 首先,我们可以有多大的侵略性?期待11(第一个特殊的2位数),它只是增加1,我们可以增加2.看到102(特殊的3位数),我们可以增加它3.是自然的我们应该使用与数字位数相等的增量吗?
    • 现在是“额外的关注” - 每当“按位数增加”导致“进位”时,天真的增量就会中断。因为随机数会将1添加到数字之和,因此我们可能需要从某个数字中减去该数字,以保持sum of digits表现良好。
  2. 上面的一个问题是我们非常高兴地跳过“第一个特殊的N位数字”,但计算机不是我们一眼就能看到的。
    幸运的是,“N个数字的第一个特殊”很容易计算:它是10^(N-1)+(N-1) - 10^(N-1)带来1,其余为零,{{1}使其余部分使得数字的总和成为N-1可以整除的第一个数字。当然,如果N,这将会中断,但幸运的是,问题仅限于N > 10特殊号码,最多需要7位数(第百万个特定号码是6806035 - 7位);

  3. 所以,我们可以检测到“带有N位数的第一个特殊数字”,我们知道我们应该小心地尝试 将它递增N.我们现在可以更好地看看“额外的”关心“?

  4. 代码 - 比前一代快两倍,在获取数据时完全“正统”(通过getter而不是直接访问数据成员)。 随意内联:

    10^6

    从数字构造数字必然会更快。

    还记得the abacus吗?曾经用过吗?

    import java.util.ArrayList;
    import java.util.Arrays;
    
    public class Abacus {
    
      static protected int pow10[]=
        {1,10,100,1000, 10000, 100000, 1000000, 10000000, 100000000}
      ;
      // the value stored for line[i] corresponds to digit[i]*pow10[i]
      protected int lineValues[];
      protected int sumDigits;
      protected int representedNumber;
    
      public Abacus() {
        this.lineValues=new int[0];
        this.sumDigits=0;
        this.representedNumber=0;
      }
    
      public int getLineValue(int line) {
        return this.lineValues[line];
      }
      public void clearUnitLine() {
        this.sumDigits-=this.lineValues[0];
        this.representedNumber-=this.lineValues[0];
        this.lineValues[0]=0;
      }
    
      // This is how you operate the abacus in real life being asked
      // to add a number of units to the line presenting powers of 10
      public boolean addWithCarry(int units, int line) {
        if(line-1==pow10.length) {
          // don't have enough pow10 stored
          pow10=Arrays.copyOf(pow10, pow10.length+1);
          pow10[line]=pow10[line-1]*10;
        }
        if(line>=this.lineValues.length) {
          // don't have enough lines for the carry
          this.lineValues=Arrays.copyOf(this.lineValues, line+1);
        }
        int digitOnTheLine=this.lineValues[line]/pow10[line];
        int carryOnTheNextLine=0;
        while(digitOnTheLine+units>=10) {
          carryOnTheNextLine++;
          units-=10;
        }
        if(carryOnTheNextLine>0) {
          // we have a carry, the sumDigits will be affected
          // 1. the next two statememts are equiv with "set a value of zero on the line"
          this.sumDigits-=digitOnTheLine;
          this.representedNumber-=this.lineValues[line];
    
          // this is the new value of the digit to set on the line
          digitOnTheLine+=units;
    
          // 3. set that value and keep all the values synchronized
          this.sumDigits+=digitOnTheLine;
          this.lineValues[line]=digitOnTheLine*pow10[line];
          this.representedNumber+=this.lineValues[line];
    
          // 4. as we had a carry, the next line will be affected as well.
          this.addWithCarry(carryOnTheNextLine, line+1);
        }
        else { // we an simply add the provided value without carry
          int delta=units*pow10[line];
          this.lineValues[line]+=delta;
          this.representedNumber+=delta;
          this.sumDigits+=units;
        }
        return carryOnTheNextLine>0;
      }
    
      public int getSumDigits() {
        return this.sumDigits;
      }
    
      public int getRepresentedNumber() {
        return this.representedNumber;
      }
    
      public int getLinesCount() {
        return this.lineValues.length;
      }
    
    
      static public ArrayList<Integer> specials(int N) {
        ArrayList<Integer> ret=new ArrayList<>(N);
        Abacus abacus=new Abacus();
        ret.add(1);
        abacus.addWithCarry(1, 0); // to have something to add to
        int increment=abacus.getLinesCount();
        while(ret.size()<N) {
          boolean hadCarry=abacus.addWithCarry(increment, 0);
          if(hadCarry) {
            // need to resynch the sum for a perfect number
            int newIncrement=abacus.getLinesCount();
            abacus.clearUnitLine();
            if(newIncrement!=increment) {
              // we switched powers of 10
              abacus.addWithCarry(newIncrement-1, 0);
              increment=newIncrement;
            }
            else { // simple carry
              int digitsSum=abacus.getSumDigits();
              // how much we should add to the last digit to make the sumDigits
              // divisible again with the increment?
              int units=increment-digitsSum % increment;
              if(units<increment) {
                abacus.addWithCarry(units, 0);
              }
            }
          }
          ret.add(abacus.getRepresentedNumber());
        }
        return ret;
      }
    
      // to understand how the addWithCarry works, try the following code
      static void add13To90() {
        Abacus abacus; // starts with a represented number of 0
        // line==1 means units of 10^1
        abacus.addWithCary(9, 1); // so this should make the abacus store 90
        System.out.println(abacus.getRepresentedNumber());
        // line==0 means units of 10^0
        abacus.addWithCarry(13, 0); 
        System.out.println(abacus.getRepresentedNumber()); // 103
      }
    
      static public void main(String[] args) {
        int count=1000000;
        long t1=System.nanoTime();
        ArrayList<Integer> s1=Abacus.specials(count);
        long t2=System.nanoTime();
        System.out.println("t:"+(t2-t1));
      }
    }