整数格上的误报


12

排行榜

 User            Language      Score
 =========================================
 Ell             C++11         293,619,555
 feersum         C++11         100,993,667
 Ell             C++11          78,824,732
 Geobits         Java           27,817,255
 Ell             Python         27,797,402
 Peter Taylor    Java                2,468
 <reference>     Julia                 530

背景

在整数坐标的二维网格上工作时,有时您想知道两个向量(具有整数分量)是否具有相同的大小。当然,在欧几里得几何中,向量的大小(x,y)

√(x² + y²)

因此,幼稚的实现可能会为两个向量计算该值并比较结果。这不仅会导致不必要的平方根计算,而且还会导致浮点数不准确的问题,这可能会产生误报:幅值不同但浮点表示中的有效数字都相同的向量。

出于此挑战的目的,我们将误报定义为一对坐标对,(a,b)(c,d)为此:

  • 当表示为64位无符号整数时,它们的平方大小不同。
  • 当表示为64位二进制浮点数并通过64位平方根计算得出时,它们的大小相同(根据IEEE 754)。

例如,使用16位表示(而不是64位),产生假阳性的最小对矢量为:

(25,20) and (32,0)

它们的平方平方大小分别是10251024。求平方根产量

32.01562118716424 and 32.0

但是在16位浮点数中,这两个都被截断为32.0

同样,最小的2对产生32位表示形式的假阳性

(1659,1220) and (1951,659)

1个 “最小”,以其16位浮点幅度衡量。
2 “最小”,以其32位浮点幅度衡量。

最后,以下是一些有效的64位情况:

 (51594363,51594339) and (54792160,48184783)
 (54356775,54353746) and (54620742,54088476)
 (54197313,46971217) and (51758889,49645356)
 (67102042,  956863) and (67108864,       6) *

* 最后一种情况是针对64位误报的幅度最小的几种情况之一。

挑战

在不到10,000个字节的代码中,使用一个线程,您将 在坐标范围内找到64位(二进制)浮点数的假阳性0 ≤ y ≤ x(即,仅在欧几里德平面的第一个八分圆内)使得内10分钟。如果两个提交的并发数目相同,则平局决胜局是找到这些对中最后一对的实际时间。x² + y² ≤ 253

您的程序在任何时候都不得使用超过4 GB的内存(出于实际原因)。

必须有两种模式可以运行您的程序:一种在找到时输出每对,另一种仅在末尾输出找到的对的数量。第一个将用于验证您的货币对的有效性(通过查看一些输出样本),第二个将用于实际计时提交时间。请注意,打印必须是唯一的区别。特别是,计数程序可能不会对可能找到的对数进行硬编码。它仍然必须执行与打印所有数字相同的循环,并且只忽略打印本身!

我将在Windows 8笔记本电脑上测试所有提交的内容,因此请在注释中询问是否要使用一些不太常见的语言。

请注意,在切换第一和第二坐标对的过程中,绝对不能计算两次。

还要注意,我将通过Ruby控制器运行您的进程,如果10分钟后它还没有完成,它将终止您的进程。确保输出当时找到的对的数量。您既可以自己记录时间,也可以在10分钟之前打印结果,或者只输出不定期发现的对数,我将最后一个作为您的分数。


作为附带说明,可以同时确定一个整数是否是一个完美的平方,并且还可以有效地计算其精确的平方根。以下算法比我系统上的硬件平方根快5倍(将64位无符号整数与80位长的双精度
Todd Lehman

Answers:


5

C ++,275,000,000+

我们将其幅度可精确表示的对(例如(x,0))称为诚实对,将所有其他对称为不诚实的幅度m,其中m是该对错误报告的幅度。一篇文章中的第一个程序使用了一组紧密相关的一对诚实和不诚实对:
(x,0)(x,1),足够大x。第二个程序使用相同的不诚实对集合,但通过查找所有整数级的诚实对来扩展诚实对集合。该程序不会在十分钟之内终止,但是它会在很早的时候发现绝大多数结果,这意味着大部分运行时都浪费了。该程序不会继续寻找越来越少的诚实对,而是利用业余时间来做下一个合乎逻辑的事情:扩展不诚实对的集合。

从上一篇文章中我们知道,对于所有足够大的整数rsqrt(r 2 +1)= r,其中sqrt是浮点平方根函数。我们的攻击计划是找到对P =(x,y),使得对于某个足够大的整数rx 2 + y 2 = r 2 +1。这很简单,但是天真地寻找单个这样的对太慢了,以至于没有意思。我们希望批量查找这些对,就像我们在上一个程序中对诚实对所做的那样。

{ vw }为向量的正交对。对于所有实标量r|| r v + w || 2 = r 2 +1。在2,这就是勾股定理的直接结果是:

图片1

我们正在寻找向量vw,以便存在一个整数 r,其中xy也是整数。作为一个侧面说明,请注意,我们在前面的两个程序使用的一套不诚实对只是这一点,其中一个特例{ vw ^ }是标准基础2 ; 这次我们希望找到一个更通用的解决方案。毕达哥拉斯三胞胎(整数三胞胎(a,b,c)满足a 2 + b 2 = c 2的地方,我们在之前的程序中使用过)。

(a,b,c)为毕达哥拉斯三联体。向量v =(b / c,a / c)w =(-a / c,b / c)(以及
w =(a / c,-b / c))是正交的,易于验证。事实证明,对于毕达哥拉斯三重态的任何选择,都存在一个整数r,使得xy是整数。为了证明这一点,并有效地找到rP,我们需要一些数/群理论。我将保留细节。无论哪种方式,假设我们都有积分rxy。我们还是短的几件事情:我们需要[R足够大,我们希望有一种快速的方法可以从中获得更多相似的对。幸运的是,有一种简单的方法可以完成此操作。

请注意,Pv上的投影为r v,因此r = P · v =(x,y)·(b / c,a / c)= xb / c + ya / c,所有这些都表示xb + ya = rc。结果,对于所有整数n(x + bn)2 +(y + an)2 =(x 2 + y 2)+ 2(xb + ya)n +(a 2 + b 2)n 2 =( r 2 +1)+ 2(rc)n +(c 2)n 2 =(r + cn)2 +1。换句话说,形式为
(x + bn,y + an)的对的平方大小为(r + cn)2 +1,这正是我们要寻找的对的种类!对于足够大的n,它们是幅度r + cn的不诚实对。

看一个具体的例子总是很高兴。如果我们采用毕达哥拉斯三重态(3,4,5),则在r = 2时,我们有P =(1,2)(您可以检查(1,2) ·(4/5,3/5)= 2并且,很明显,1 2 + 2 2 = 2 2 + 1)添加5ř(4,3)P带我们R '= 2 + 5 = 7P'=(1 + 4,2 + 3)=(5,5)。瞧瞧5 2 + 5 2 = 7 2 +1。下一个坐标是r''= 12P''=(9,8),同样是9 2 + 8 2 = 12 2 +1,依此类推,依此类推...

图片2

一旦r足够大,我们就开始得到幅度增加5的不诚实对。这大约是27,797,402 / 5对不诚实的对。

因此,现在我们有很多整数倍的不诚实对。我们可以很容易地将它们与第一个程序的诚实对配对以形成假阳性,并且在适当注意的情况下,我们也可以使用第二个程序的诚实对。这基本上就是该程序的工作。像以前的程序一样,它也很早就发现了大部分结果-几秒钟之内就得到了200,000,000个误报-,然后放慢了速度。

用编译g++ flspos.cpp -oflspos -std=c++11 -msse2 -mfpmath=sse -O3。要验证结果,请添加-DVERIFY(这会明显慢一些。)

用运行flspos。详细模式的任何命令行参数。

#include <cstdio>
#define _USE_MATH_DEFINES
#undef __STRICT_ANSI__
#include <cmath>
#include <cfloat>
#include <vector>
#include <iterator>
#include <algorithm>

using namespace std;

/* Make sure we actually work with 64-bit precision */
#if defined(VERIFY) && FLT_EVAL_METHOD != 0 && FLT_EVAL_METHOD != 1
#   error "invalid FLT_EVAL_METHOD (did you forget `-msse2 -mfpmath=sse'?)"
#endif

template <typename T> struct widen;
template <> struct widen<int> { typedef long long type; };

template <typename T>
inline typename widen<T>::type mul(T x, T y) {
    return typename widen<T>::type(x) * typename widen<T>::type(y);
}
template <typename T> inline T div_ceil(T a, T b) { return (a + b - 1) / b; }
template <typename T> inline typename widen<T>::type sq(T x) { return mul(x, x); }
template <typename T>
T gcd(T a, T b) { while (b) { T t = a; a = b; b = t % b; } return a; }
template <typename T>
inline typename widen<T>::type lcm(T a, T b) { return mul(a, b) / gcd(a, b); }
template <typename T>
T div_mod_n(T a, T b, T n) {
    if (b == 0) return a == 0 ? 0 : -1;
    const T n_over_b = n / b, n_mod_b = n % b;
    for (T m = 0; m < n; m += n_over_b + 1) {
        if (a % b == 0) return m + a / b;
        a -= b - n_mod_b;
        if (a < 0) a += n;
    }
    return -1;
}

template <typename T> struct pythagorean_triplet { T a, b, c; };
template <typename T>
struct pythagorean_triplet_generator {
    typedef pythagorean_triplet<T> result_type;
private:
    typedef typename widen<T>::type WT;
    result_type p_triplet;
    WT p_c2b2;
public:
    pythagorean_triplet_generator(const result_type& triplet = {3, 4, 5}) :
        p_triplet(triplet), p_c2b2(sq(triplet.c) - sq(triplet.b))
    {}
    const result_type& operator*() const { return p_triplet; }
    const result_type* operator->() const { return &p_triplet; }
    pythagorean_triplet_generator& operator++() {
        do {
            if (++p_triplet.b == p_triplet.c) {
                ++p_triplet.c;
                p_triplet.b = ceil(p_triplet.c * M_SQRT1_2);
                p_c2b2 = sq(p_triplet.c) - sq(p_triplet.b);
            } else
                p_c2b2 -= 2 * p_triplet.b - 1;
            p_triplet.a = sqrt(p_c2b2);
        } while (sq(p_triplet.a) != p_c2b2 || gcd(p_triplet.b, p_triplet.a) != 1);
        return *this;
    }
    result_type operator()() { result_type t = **this; ++*this; return t; }
};

int main(int argc, const char* argv[]) {
    const bool verbose = argc > 1;

    const int min = 1 << 26;
    const int max = sqrt(1ll << 53);

    const size_t small_triplet_count = 1000;
    vector<pythagorean_triplet<int>> small_triplets;
    small_triplets.reserve(small_triplet_count);
    generate_n(
        back_inserter(small_triplets),
        small_triplet_count,
        pythagorean_triplet_generator<int>()
    );

    int found = 0;
    auto add = [&] (int x1, int y1, int x2, int y2) {
#ifdef VERIFY
        auto n1 = sq(x1) + sq(y1), n2 = sq(x2) + sq(y2);
        if (x1 < y1 || x2 < y2 || x1 > max || x2 > max ||
            n1 == n2 || sqrt(n1) != sqrt(n2)
        ) {
            fprintf(stderr, "Wrong false-positive: (%d, %d) (%d, %d)\n",
                    x1, y1, x2, y2);
            return;
        }
#endif
        if (verbose) printf("(%d, %d) (%d, %d)\n", x1, y1, x2, y2);
        ++found;
    };

    int output_counter = 0;
    for (int x = min; x <= max; ++x) add(x, 0,    x, 1);
    for (pythagorean_triplet_generator<int> i; i->c <= max; ++i) {
        const auto& t1 = *i;

        for (int n = div_ceil(min, t1.c); n <= max / t1.c; ++n)
            add(n * t1.b, n * t1.a,    n * t1.c, 1);

        auto find_false_positives = [&] (int r, int x, int y) {
            {
                int n = div_ceil(min - r, t1.c);
                int min_r = r + n * t1.c;
                int max_n = n + (max - min_r) / t1.c;
                for (; n <= max_n; ++n)
                    add(r + n * t1.c, 0,    x + n * t1.b, y + n * t1.a);
            }
            for (const auto t2 : small_triplets) {
                int m = div_mod_n((t2.c - r % t2.c) % t2.c, t1.c % t2.c, t2.c);
                if (m < 0) continue;
                int sr = r + m * t1.c;
                int c = lcm(t1.c, t2.c);
                int min_n = div_ceil(min - sr, c);
                int min_r = sr + min_n * c;
                if (min_r > max) continue;
                int x1 = x + m * t1.b, y1 = y + m * t1.a;
                int x2 = t2.b * (sr / t2.c), y2 = t2.a * (sr / t2.c);
                int a1 = t1.a * (c / t1.c), b1 = t1.b * (c / t1.c);
                int a2 = t2.a * (c / t2.c), b2 = t2.b * (c / t2.c);
                int max_n = min_n + (max - min_r) / c;
                int max_r = sr + max_n * c;
                for (int n = min_n; n <= max_n; ++n) {
                    add(
                        x2 + n * b2, y2 + n * a2,
                        x1 + n * b1, y1 + n * a1
                    );
                }
            }
        };
        {
            int m = div_mod_n((t1.a - t1.c % t1.a) % t1.a, t1.b % t1.a, t1.a);
            find_false_positives(
                /* r = */ (mul(m, t1.c) + t1.b) / t1.a,
                /* x = */ (mul(m, t1.b) + t1.c) / t1.a,
                /* y = */ m
            );
        } {
            int m = div_mod_n((t1.b - t1.c % t1.b) % t1.b, t1.a, t1.b);
            find_false_positives(
                /* r = */ (mul(m, t1.c) + t1.a) / t1.b,
                /* x = */ m,
                /* y = */ (mul(m, t1.a) + t1.c) / t1.b
            );
        }

        if (output_counter++ % 50 == 0)
            printf("%d\n", found), fflush(stdout);
    }
    printf("%d\n", found);
}

真好!:)我的计算机上安装了293,619,555并更新了页首横幅。
Martin Ender 2014年

8

蟒蛇,27,797,402

只是将标准设置得更高一点...

from sys import argv
verbose = len(argv) > 1
found = 0
for x in xrange(67108864, 94906266):
    found += 1
    if verbose:
        print "(%d, 0) (%d, 1)" % (x, x)
print found

容易验证对于所有67,108,864 <= x <= 94,906,265 = floor(sqrt(2 53))(x,0)(x,1)是假阳性。

工作原理67,108,864 = 2 26。因此,对于某些0 <= x'<2 26,上述范围内的所有数字x的形式均为2 26 + x'。对于所有正数e(x + e)2 = x 2 + 2xe + e 2 = x 2 + 2 27 e + 2x'e + e 2。如果我们想要(x + e)2 = x 2 +1,则至少需要2 27 e <= 1,即e <= 2 -27
然而,由于双精度浮点数的尾数为52比特宽,最小Ë使得X + E> XE = 2 26 - 52 = 2 -26。换句话说,大于x的最小可表示数是x + 2 -26,sqrt(x 2 +1)的结果最多为x + 2 -27。由于默认的IEEE-754舍入模式为最近舍入;平分平局,它将始终四舍五入,从不四舍五入到x + 2 -26(其中平局决胜实际上仅与x = 67,108,864有关,如果有的话。较大的数字将四舍五入为x)。


C ++,75,000,000+

回想一下3 2 + 4 2 = 5 2。这意味着点(4,3)位于以原点为中心的半径5的圆上。实际上,对于所有整数n(4n,3n)都位于半径为5n的圆上。对于足够大的n(即5n> = 2 26),我们已经知道该圆上的所有点的假阳性:(5n,1)。大!那就是另外27,797,402 / 5个免费的假阳性对!但是为什么在这里停下来?(3、4、5)不是唯一的三元组。

该程序将查找所有正整数三元组(a,b,c),使a 2 + b 2 = c 2,并以此方式计算假阳性。它很快就达到了70,000,000个假阳性,但是随着数量的增长,它的速度大大降低。

用编译g++ flspos.cpp -oflspos -std=c++11 -msse2 -mfpmath=sse -O3。要验证结果,请添加-DVERIFY(这会明显慢一些。)

用运行flspos。详细模式的任何命令行参数。

#include <cstdio>
#include <cmath>
#include <cfloat>

using namespace std;

/* Make sure we actually work with 64-bit precision */
#if defined(VERIFY) && FLT_EVAL_METHOD != 0 && FLT_EVAL_METHOD != 1
#   error "invalid FLT_EVAL_METHOD (did you forget `-msse2 -mfpmath=sse'?)"
#endif

template <typename T> inline long long sqr(T x) { return 1ll * x * x; }
template <typename T>
T gcd(T a, T b) { while (b) { T t = a; a = b; b = t % b; } return a; }

int main(int argc, const char* argv[]) {
    const bool verbose = argc > 1;

    const int min = 1 << 26;
    const int max = sqrt(1ll << 53);

    int found = 0;
    auto add = [=, &found] (int x1, int y1, int x2, int y2) {
#ifdef VERIFY
        auto n1 = sqr(x1) + sqr(y1), n2 = sqr(x2) + sqr(y2);
        if (n1 == n2 || sqrt(n1) != sqrt(n2)) {
            fprintf(stderr, "Wrong false-positive: (%d, %d) (%d, %d)\n",
                    x1, y1, x2, y2);
            return;
        }
#endif
        if (verbose) printf("(%d, %d) (%d, %d)\n", x1, x2, y1, y2);
        ++found;
    };

    for (int x = min; x <= max; ++x) add(x, 0,    x, 1);

    for (int a = 1; a < max; ++a) {
        auto a2b2 = sqr(a) + 1;
        for (int b = 1; b <= a; a2b2 += 2 * b + 1, ++b) {
            int c = sqrt(a2b2);
            if (a2b2 == sqr(c) && gcd(a, b) == 1) {
                int max_c = max / c;
                for (int n = (min + c - 1) / c; n <= max_c; ++n)
                    add(n * a, n * b,    n * c, 1);
            }
        }

        if (a % 512 == 0) printf("%d\n", found), fflush(stdout);
    }

    printf("%d\n", found);
}

是的,这是有效的策略。我以为2**53选择边界可以排除这种情况,但我想不是。
xnor 2014年

有趣的是,在此范围内的每个数字如何工作,而x ^ 2和x ^ 2 +1的平方根的单个实例不在整数+ 1/2的不同边上。
feersum 2014年

@xnor选择边界是为了使平方大小能够在64位浮点数中准确表示。
Martin Ender 2014年

嘿,行得通,谁在乎呢?;)您是说程序应该计入虚拟循环,还是实际验证结果?
2014年

@MartinButtner哦,我知道了。似乎下限是该数量除以2的平方根。我很直观地理解了为什么这样的数字应该起作用,但是我也很好奇为什么每个数字都起作用。
xnor 2014年

4

C ++ 11-100,993,667

编辑:新程序。

旧的内存占用过多。通过使用巨型向量数组而不是哈希表,这使内存使用量减少了一半。此外,它还消除了随机线程残骸。

   /* by feersum  2014/9
   http://codegolf.stackexchange.com/questions/37627/false-positives-on-an-integer-lattice */
#include <iostream>
#include <cmath>
#include <cstdlib>
#include <cstring>
#include <functional>
#include <vector>
using namespace std;
#define ul unsigned long long

#define K const



#define INS(A)   { bool already = false; \
    for(auto e = res[A.p[0][0]].end(), it = res[A.p[0][0]].begin(); it != e; ++it) \
        if(A.p[0][1] == it->y1 && A.p[1][0] == it->x2 && A.p[1][1] == it->y2) { \
            already = true; \
            break; } \
    if(!already) res[A.p[0][0]].push_back( {A.p[0][1], A.p[1][0], A.p[1][1]} ), ++n; }

#define XMAXMIN (1<<26)

struct ints3 {
    int y1, x2, y2;
};


struct pparm {
    int a,b,c,d;
    int E[4];
    pparm(int A,int B,int C, int D):
        E{B*B+D*D,A*B+C*D,A*A+C*C+2*(B+D),A+C}
    {
        a=A;b=B;c=C;d=D;
    }

};

struct ans {
    int p[2][2];

};
ostream&operator<<(ostream&o, ans&a)
{
    o<<'('<<a.p[0][0]<<','<<a.p[0][1]<<"),("<<a.p[1][0]<<','<<a.p[1][1]<<')'<<endl;
    return o;
}



vector<ints3> res[XMAXMIN];

bool print;
int n;

void gen(K pparm&p1, K pparm&p2)
{
#ifdef DBUG
    for(int i=0;i<2;i++){
    K pparm&p=i?p2:p1;
    cout<<' '<<p.a<<' '<<p.b<<' '<<p.c<<' '<<p.d<<' ';}
    cout<<endl;
#endif

    for(ul x = 0; ; ++x) {
        ans a;
        ul s[2];
        for(int i = 0; i < 2; i++) {
            K pparm &p = i?p2:p1;
            int *pt = a.p[i];
            pt[0] = p.b+x*(p.a+x);
            pt[1] = p.d+x*(p.c+x);
            s[i] = (ul)pt[0]*pt[0] + (ul)pt[1]*pt[1];
        }
        if(*s >> 53)
            break;
if(s[1] - s[0] != 1)
exit(4);

        if(sqrt(s[0]) == sqrt(s[1])) {
             for(int i = 0; i < 2; i++)
                if(a.p[i][0] > a.p[i][1])
                    swap(a.p[i][0], a.p[i][1]);
            if(a.p[0][0] > a.p[0][1])
                for(int i = 0; i < 2; i++)
                    swap(a.p[0][i], a.p[1][i]);
            INS(a)
        }
    }
}



int main(int ac, char**av)
{
    for(int i = 1; i < ac; i++) {
        print |= !strcmp(av[1], "-P");
    }


    #define JMAX 43000000
    for(ul j = 0; j < JMAX; j++) {
        pparm p1(-~j,j,~-j,0),p2(j,1,j,j);
        gen(p1,p2);
        if(!print && !(j%1024))
#ifdef DBUG
            cout<<j<<' ',
#endif
            cout<<n<<endl;

    }
    if(print) 
        for(vector<ints3>& v: res)
            for(ints3& i: v)
                printf("(%d,%d),(%d,%d)\n", &v - res, i.y1, i.x2, i.y2);

    return 0;
}

-P参数运行以打印出点数,而不是点数。

对我来说,在计数模式下花费不到2分钟,而将打印定向到文件(约4 GB)则花费了大约5分钟,因此它并没有受到I / O的限制。

我原来的程序很整洁,但是我放弃了大部分程序,因为它只能产生10 ^ 5的结果。它要做的是寻找(x ^ 2 + Ax + B,x ^ 2 + Cx + D),(x ^ 2 + ax + b,x ^ 2 + cx + d)形式的参数化x,(x ^ 2 + Ax + B)^ 2 +(x ^ 2 + Cx + D)^ 2 =(x ^ 2 + ax + b)^ 2 +(x ^ 2 + cx + d)^ 2 + 1.当发现一组这样的参数{a,b,c,d,A,B,C,D}时,它便开始检查所有x值是否低于最大值。在查看该程序的调试输出时,我注意到参数化的某些参数化使我能够轻松生成大量数字。我选择不打印Ell的数字,因为我有很多自己的数字。希望现在不会有人打印出我们的两组数字并声称自己是赢家:)

 /* by feersum  2014/9
   http://codegolf.stackexchange.com/questions/37627/false-positives-on-an-integer-lattice */
    #include <iostream>
    #include <cmath>
    #include <cstdlib>
    #include <cstring>
    #include <functional>
    #include <unordered_set>
    #include <thread>
using namespace std;
#define ul unsigned long long

#define h(S) unordered_##S##set
#define P 2977953206964783763LL
#define K const

#define EQ(T, F)bool operator==(K T&o)K{return!memcmp(F,o.F,sizeof(F));}

struct pparm {
    int a,b,c,d;
    int E[4];
    pparm(int A,int B,int C, int D):
        E{B*B+D*D,A*B+C*D,A*A+C*C+2*(B+D),A+C}
    {
        a=A;b=B;c=C;d=D;
    }
    EQ(pparm,E)
};

struct ans {
    int p[2][2];
    EQ(ans,p)
};
ostream&operator<<(ostream&o, ans&a)
{
    o<<'('<<a.p[0][0]<<','<<a.p[0][1]<<"),("<<a.p[1][0]<<','<<a.p[1][1]<<')'<<endl;
    return o;
}

#define HASH(N,T,F) \
struct N { \
    size_t operator() (K T&p) K { \
        size_t h = 0; \
        for(int i = 4; i--; ) \
            h=h*P+((int*)p.F)[i]; \
        return h; \
    }};

#define INS(r, a) { \
    bool new1 = r.insert(a).second; \
    n += new1; \
    if(print && new1) \
        cout<<a; }

HASH(HA,ans,p)

bool print;
int n;

void gen(h()<ans,HA>&r, K pparm&p1, K pparm&p2)
{
#ifdef DBUG
    for(int i=0;i<2;i++){
    K pparm&p=i?p2:p1;
    cout<<' '<<p.a<<' '<<p.b<<' '<<p.c<<' '<<p.d<<' ';}
    cout<<endl;
#endif

    for(ul x = 0; ; ++x) {
        ans a;
        ul s[2];
        for(int i = 0; i < 2; i++) {
            K pparm &p = i?p2:p1;
            int *pt = a.p[i];
            pt[0] = p.b+x*(p.a+x);
            pt[1] = p.d+x*(p.c+x);
            s[i] = (ul)pt[0]*pt[0] + (ul)pt[1]*pt[1];
        }
        if(*s >> 53)
            break;
if(s[1] - s[0] != 1)
exit(4);

        if(sqrt(s[0]) == sqrt(s[1])) {
             for(int i = 0; i < 2; i++)
                if(a.p[i][0] > a.p[i][1])
                    swap(a.p[i][0], a.p[i][1]);
            INS(r,a)
        }
    }
    //if(!print) cout<<n<<endl;
}

void endit()
{
    this_thread::sleep_for(chrono::seconds(599));
    exit(0);
}

int main(int ac, char**av)
{
    bool kill = false;
    for(int i = 1; i < ac; i++) {
        print |= ac>1 && !stricmp(av[1], "-P");
        kill |= !stricmp(av[i], "-K");
    }

    thread KILLER;
    if(kill)
        KILLER = thread(endit);

    h()<ans, HA> res;
    res.reserve(1<<27);

    #define JMAX 43000000
    for(ul j = 0; j < JMAX; j++) {
        pparm p1(-~j,j,~-j,0),p2(j,1,j,j);
        gen(res,p1,p2);
        if(!print && !(j%1024))
#ifdef DBUG
            cout<<j<<' ',
#endif
            cout<<n<<endl;

    }
    exit(0);
}

我收到一堆编译器错误:pastebin.com/enNcY9fx有什么线索吗?
Martin Ender 2014年

@Martin不知道...我将帖子复制到一个文件中,该文件在具有相同开关的Windows 8笔记本电脑上编译。对我来说很好。您有什么版本的gcc?
feersum

顺便说一句,如果它们引起错误,您可以简单地删除所有与线程无关的位,而这些位是完全不必要的。仅当您使用不需要的“ -K”选项时,它们才会执行某些操作。
feersum

g++ (GCC) 4.8.1。好的,我删除了线程位,但是stricmp由于某种原因,它仍然无法识别。
Martin Ender 2014年

1
目前,我还有很多其他事情要做,所以我会告诉您我的想法来改进您的方法。随着半径平方附近范围的顶端,你还可以得到之间的碰撞半径平方其由2不同
彼得·泰勒

1

Java,Bresenham式圆扫描

试探性地,我希望从环的较宽端开始会产生更多的碰撞。我希望通过对每个碰撞进行一次扫描来获得一定的改进,记录每次碰撞的值surplus0和之间r2max - r2,但在我的测试中证明比该版本慢。类似地,尝试使用单个int[]缓冲区,而不是大量创建两个元素的数组和列表。性能优化确实是一个奇怪的野兽。

使用命令行参数运行,以输出对,而无需进行简单计数。

import java.util.*;

public class CodeGolf37627 {
    public static void main(String[] args) {
        final int M = 144;
        boolean[] possible = new boolean[M];
        for (int i = 0; i <= M/2; i++) {
            for (int j = 0; j <= M/2; j++) {
                possible[(i*i+j*j)%M] = true;
            }
        }

        long count = 0;
        double sqrt = 0;
        long r2max = 0;
        List<int[]> previousPoints = null;
        for (long r2 = 1L << 53; ; r2--) {
            if (!possible[(int)(r2 % M)]) continue;

            double r = Math.sqrt(r2);
            if (r != sqrt) {
                sqrt = r;
                r2max = r2;
                previousPoints = null;
            }
            else {
                if (previousPoints == null) previousPoints = findLatticePointsBresenham(r2max, (int)r);

                if (previousPoints.size() == 0) {
                    r2max = r2;
                    previousPoints = null;
                }
                else {
                    List<int[]> points = findLatticePointsBresenham(r2, (int)r);
                    for (int[] p1 : points) {
                        for (int[] p2 : previousPoints) {
                            if (args.length > 0) System.out.format("(%d, %d) (%d, %d)\n", p1[0], p1[1], p2[0], p2[1]);
                            count++;
                        }
                    }
                    previousPoints.addAll(points);
                    System.out.println(count);
                }
            }
        }
    }

    // Surprisingly, this seems to be faster than doing one scan for all two or three r2s.
    private static List<int[]> findLatticePointsBresenham(long r2, long r) {
        List<int[]> rv = new ArrayList<int[]>();
        // Require 0 = y = x
        long x = r, y = 0, surplus = r2 - r * r;
        while (y <= x) {
            if (surplus == 0) rv.add(new int[]{(int)x, (int)y});

            // Invariant: surplus = r2 - x*x - y*y >= 0
            y++;
            surplus -= 2*y - 1;
            if (surplus < 0) {
                x--;
                surplus += 2*x + 1;
            }
        }

        return rv;
    }
}

1

爪哇-27,817,255

其中大多数与Ell显示的内容相同,其余部分基于(j,0) (k,l)。对于每个j,我都会走一些方格,然后检查其余的方格是否为假阳性。这基本上占了整个时间,仅比仅有25k(约0.1%)的增益(j,0) (j,1),但是增益就是增益。

这将在不到十分钟的时间内在我的计算机上完成,但是我不知道您有什么。因为原因,如果在时间用完之前还没有完成,它的得分将大大降低。在这种情况下,您可以调整第8行的除数,以便及时完成除数(这仅决定了每个除数向后走的距离j)。对于某些除数,分数为:

11    27817255 (best on OPs machine)
10    27818200
8     27820719
7     27822419 (best on my machine)

要打开每个比赛的输出(天哪,如果这样做的话,它会很慢),只需取消注释第10行和第19行。

public class FalsePositive {
    public static void main(String[] args){
        long j = 67108864;
        long start = System.currentTimeMillis();
        long matches=0;
        while(j < 94906265 && System.currentTimeMillis()-start < 599900){
            long jSq = j*j;
            long limit = (long)Math.sqrt(j)/11; // <- tweak to fit inside 10 minutes for best results
            matches++; // count an automatic one for (j,0)(j,1)
            //System.out.println("("+j+",0) ("+j+",1)");        
            for(int i=1;i<limit;i++){
                long k = j-i;
                long kSq = k*k;
                long l = (long)Math.sqrt(jSq-kSq);
                long lSq = l*l;
                if(kSq+lSq != jSq){
                    if(Math.sqrt(kSq+lSq)==Math.sqrt(jSq)){
                        matches++;
                        //System.out.println("("+j+",0) ("+k+","+l+")");        
                    }
                }
            }
            j++;
        }
        System.out.println("\n"+matches+" Total matches, got to j="+j);
    }
}

作为参考,它给出的前20个输出(除数= 7,不包括(j,0)(j,1)类型)为:

(67110083,0) (67109538,270462)
(67110675,0) (67109990,303218)
(67111251,0) (67110710,269470)
(67111569,0) (67110668,347756)
(67112019,0) (67111274,316222)
(67112787,0) (67111762,370918)
(67115571,0) (67115518,84346)
(67117699,0) (67117698,11586)
(67117971,0) (67117958,41774)
(67120545,0) (67120040,260368)
(67121043,0) (67120118,352382)
(67122345,0) (67122320,57932)
(67122449,0) (67122444,25908)
(67122633,0) (67122328,202348)
(67122729,0) (67121972,318784)
(67122849,0) (67122568,194224)
(67124195,0) (67123818,224970)
(67125201,0) (67125172,62396)
(67125705,0) (67124632,379540)
(67126195,0) (67125882,204990)

0

朱莉娅530错误肯定

这是一个非常幼稚的蛮力搜索,您可以将其视为参考实现。

num = 0
for i = 60000000:-1:0
    for j = i:-1:ifloor(0.99*i)
        s = i*i + j*j
        for x = ifloor(sqrt(s/2)):ifloor(sqrt(s))
            min_y = ifloor(sqrt(s - x*x))
            max_y = min_y+1
            for y = min_y:max_y
                r = x*x + y*y
                if r != s && sqrt(r) == sqrt(s)
                    num += 1
                    if num % 10 == 0
                        println("Found $num pairs")
                    end
                    #@printf("(i,j) = (%d,%d); (x,y) = (%d,%d); s = %d, r = %d\n", i,j,x,y,s,r)
                end
            end
        end
    end
end

您可以通过取消注释@printf行来打印出对(及其精确的平方大小)。

基本上,这会从x = y = 6e7第一个坐标对开始搜索,并在递减x之前向下扫描到x轴的大约1%。然后,对于每个这样的坐标对,它检查大小相同(向上和向下舍入)的整个弧是否发生碰撞。

该代码假定它在64位系统上运行,因此默认的整数和浮点类型为64位(如果没有,则可以使用int64()and float64()构造函数创建它们)。

产生的结果微不足道的530。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.