了解cstutoringcenter问题43解决方案bug的问题

时间:2009-09-11 13:29:36

标签: java algorithm math

问题:首先100,000,000个六边形数字中有多少可以被1到20之间的所有数字整除?

第二种解决方案 - 简单的蛮力(确实有效)

 public static void main(String[] args) {
  long hnr = 100000000L, count = 0L;

  for (long i = 1, h = getHexNr(i); i <= hnr; i++, h = getHexNr(i)) 
   if (h % 2 == 0 && h % 3 == 0 && h % 4 == 0 && h % 5 == 0
     && h % 6 == 0 && h % 7 == 0 && h % 8 == 0
     && h % 9 == 0 && h % 10 == 0 && h % 11 == 0
     && h % 12 == 0 && h % 13 == 0 && h % 14 == 0
     && h % 15 == 0 && h % 16 == 0 && h % 17 == 0
     && h % 18 == 0 && h % 19 == 0 && h % 20 == 0) count++;

  System.out.println(count);
 }

第一个解决方案(不起作用)

public static void main(String[] args) {
  long nr = 1L, hnr = 100000000L, count = 0L;
  double tmp = 0;

  for (long i = 2L; i < 21; i++)
   nr = lcm(nr, i);
  for (double qes : getQES(2, 1, -nr)) {
   if (qes < 0) continue;
   int limit = (int) (getHexNr(hnr) / Math.floor(qes));
   for (int i = 0; i < limit; i++) {
    // if ((i * qes) % 1 == 0) count++;
    if ((tmp += qes) % 1 == 0) count++;
   }
  }

  System.out.println(count);
 }

和utils:

 static long gcd(long a, long b) {
  if (b == 0) return Math.abs(a);
  return gcd(b, a % b);
 }

 static long lcm(long a, long b) {
  return (a * b) / gcd(a, b);
 }

 static long getHexNr(long n) {
  return n * (2 * n - 1);
 }

 static double[] getQES(long a, long b, long c) {
  double d = b * b - 4 * a * c;
  if (d < 0) return new double[0];
  return new double[] { (-b + Math.sqrt(d)) / (2 * a),
    (-b - Math.sqrt(d)) / (2 * a) };
 }

第一个解决方案出了什么问题? (小数精度?)

编辑: 解决方案1的算法:

  1. 找到小数字x,可以被1到20之间的所有数字整除
  2. 求解由十六进制数x = n *(2 * n - 1)
  3. 导出的二次方程
  4. 如果n的多个没有残留增加计数
  5. 重复3.而n的倍数小于100000000的六边形数
  6. 编辑2:

    1. 232792560 //右
    2. [10788.460769248566,-10788.960769248566] //对
    3. 像6418890这样的数字正在通过测试,谷歌“6418890 * 10788.460769248566%1 =”
    4. 编辑3:

      是的,蛮力版本应该只检查21岁以下素数的可分性。但更有意思的是找出戈德伯格所说的内容。我确实知道,但更像是five monkeys story

      稍后,当我记得时,我想到舍入数字,数学库不包含执行此操作的功能。但我可以使用BigDecimal,当我查找BigDecimal并加倍我找到this。多么令人愉快的似曾相识。

      四舍五入看起来像:

      public static double round(double d, int nr) {
          return new BigDecimal(Double.toString(d)).setScale(nr,
              BigDecimal.ROUND_HALF_UP).doubleValue();
      }
      

2 个答案:

答案 0 :(得分:2)

虽然可能还有其他问题:

((tmp += qes) % 1 == 0)

tmp和qes都是双打,这意味着==比较可能会失败;见What Every Computer Scientist Should Know About Floating-Point Arithmetic

答案 1 :(得分:0)

您应该链接到描述问题,以便人们可以知道六角形数字是什么。

同样在蛮力方法中,您不需要重新检查数字中的常见素因子,因为所有这些都必须是可分的,所以它可以缩短为:

public static void main(String[] args) {
  long hnr = 100000000L, count = 0L;

  for (long i = 1, h = getHexNr(i); i <= hnr; i++, h = getHexNr(i)) 
   if (h % 20 == 0 && h % 19 == 0 && h % 18 == 0 && h % 17 == 0
    && h % 16 == 0 && h % 14 == 0 && h % 13 == 0 && h % 11 == 0) count++;

  System.out.println(count);
}

我很好奇,所以我把结果计时:

include <stdio.h>
#define H(n) (n*(2ULL*n-1ULL))
#define limit 100000000ULL
int main(int argc, char** argv){
  unsigned long long int count=0, i = 1, h = 1;
  if(argc>1&&argv[1][0]=='1'){
    for (; ++i <= limit; h = H(i)) {
     if (h % 19 == 0 && h % 17 == 0 && h % 16 == 0 && h % 13 == 0
      && h % 11 == 0 && h %  9 == 0 && h %  7 == 0 && h %  5 == 0) count++;
    }
  } else if(argc>1&&argv[1][0]=='2'){
    for (; ++i <= limit; h = H(i)) {
     if (h % 20 == 0 && h % 19 == 0 && h % 18 == 0 && h % 17 == 0
      && h % 16 == 0 && h % 14 == 0 && h % 13 == 0 && h % 11 == 0) count++;
    }
  } else if(argc>1&&argv[1][0]=='3'){
    for (; ++i <= limit; h = H(i)) {
     if (h %  5 == 0 && h %  7 == 0 && h %  9 == 0 && h % 11 == 0
      && h % 13 == 0 && h % 16 == 0 && h % 17 == 0 && h % 19 == 0) count++;
    }
  } else if(argc>1&&argv[1][0]=='4'){
    for (; ++i <= limit; h = H(i)) {
     if (h % 11 == 0 && h % 14 == 0 && h % 14 == 0 && h % 16 == 0
      && h % 17 == 0 && h % 18 == 0 && h % 19 == 0 && h % 20 == 0) count++;
    }
  } else {
    for (; ++i <= limit; h = H(i)) {
     if (h %  2 == 0 && h %  3 == 0 && h %  4 == 0 && h %  5 == 0
      && h %  6 == 0 && h %  7 == 0 && h %  8 == 0
      && h %  9 == 0 && h % 10 == 0 && h % 11 == 0
      && h % 12 == 0 && h % 13 == 0 && h % 14 == 0
      && h % 15 == 0 && h % 16 == 0 && h % 17 == 0
      && h % 18 == 0 && h % 19 == 0 && h % 20 == 0) count++;
    }
  }
  printf("%llu\n",count);
}

方法1略快于2,方法4略快于3.方法3或4比1或2快,原始速度更快。奇?
投掷h%2 == 0&amp;&amp;在所有方法面前,我得到了更好的结果;原始(仍然是相同的速度)现在比所有这些都慢,但否则排名保持不变。 gcc必须优化h%2==0才能成为按位操作。

所有输出59并在我的2.1Ghz Core Duo便携式设备上运行约2秒钟。