整数格子の誤検知


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

バックグラウンド

整数座標の2次元グリッドで作業する場合、2つのベクトル(整数成分を持つ)が同じ大きさであるかどうかを知りたい場合があります。もちろん、ユークリッド幾何学では、ベクトルの大きさは(x,y)で与えられます

√(x² + y²)

したがって、単純な実装では、両方のベクトルについてこの値を計算し、結果を比較します。不必要な平方根の計算が発生するだけでなく、浮動小数点の不正確さの問題も発生します。

この課題の目的のために、私たちは定義偽陽性を座標ペアのペアと(a,b)して(c,d)います:

  • 64ビットの符号なし整数として表される場合、それらの2乗の大きさは異なります。
  • 64ビットの2進浮動小数点数として表され、64ビットの平方根(IEEE 754に準拠)で計算される場合、それらの大きさは同じです。

例として、(64の代わりに)16ビット表現を使用すると、誤検知を生じる最小の1組のベクトルは

(25,20) and (32,0)

それらの2乗2乗の大きさは10251024です。平方根の利回りを取得する

32.01562118716424 and 32.0

しかし、16ビットの浮動小数点数では、これらの両方がに切り捨てられ32.0ます。

同様に、32ビット表現に対して偽陽性をもたらす最小の2ペアは

(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ビットの誤検知の規模が可能な限り小さいいくつかのケースの1つです。

チャレンジ

1つのスレッドを使用する10,000バイト未満のコードで は、座標範囲(つまり、ユークリッド平面の最初の8分の1の範囲内64ビット(バイナリ)浮動小数点数の誤検出を見つけることができます。0 ≤ y ≤ xそのような10分以内に。2つのサブミッションが同じ数のペアで同点の場合、タイブレーカーは、それらのペアの最後を見つけるのにかかる実際の時間です。x² + y² ≤ 253

プログラムは、いつでも4 GBを超えるメモリを使用してはなりません(実際的な理由から)。

プログラムを2つのモードで実行できる必要があります。1つは、見つかったすべてのペアを出力するモードと、最後に見つかったペアの数だけを出力するモードです。1つ目は(出力のサンプルを見て)ペアの有効性を検証するために使用され、2つ目は実際に送信をタイミングするために使用されます。印刷が唯一の違いでなければならないことに注意してください。特に、カウントプログラムは、検出できるペアの数をハードコードしない場合があります。それでも、すべての数値を印刷するために使用されるのと同じループを実行する必要があり、印刷自体を省略します!

Windows 8ラップトップですべての提出物をテストします。あまり一般的ではない言語を使用する場合は、コメントでお問い合わせください。

最初と2番目の座標ペアの切り替えでは、ペアを2回カウントしてはならないことに注意してください。

また、Rubyコントローラーを介してプロセスを実行します。これにより、10分経ってもプロセスが終了しない場合はプロセスが強制終了されます。それまでに見つかったペアの数を必ず出力してください。自分で時間を記録して、10分が経過する直前に結果を印刷するか、散発的に見つかったペアの数を出力することができます。最後の数をスコアとして取得します。


サイドコメントとして、整数が完全な正方形であるかどうかを同時に決定し、その正確な平方根を効率的に計算することもできます。次のアルゴリズムは、私のシステムのハードウェア平方根よりも5倍高速です(64ビット符号なし整数を80ビットlong doubleと比較):math.stackexchange.com/questions/41337/…–
トッドリーマン

回答:


5

C ++、275,000,000+

我々のようなその大きさが正確に表現されペアを参照します(X、0)として、正直ペアおよび他のすべての対に不正対振幅のMMは対の誤報告の大きさです。最初のプログラム前の投稿は:honest-の緊密に関連するカップルや不正対のセット使用
(X、0)(X、1)十分な大きさのために、それぞれ、X。2番目のプログラムでは、不正なペアの同じセットを使用しましたが、整数マグニチュードのすべての正直なペアを探すことで、正直なペアのセットを拡張しました。プログラムは10分以内に終了しませんが、その結果の大部分が非常に早い段階で検出されるため、ランタイムの大部分が無駄になります。このプログラムは、あまり頻繁ではない正直なペアを探し続ける代わりに、空き時間を使って次の論理的なことを行います。不正なペアのセットを拡張します。

前の投稿から、十分に大きいすべての整数rについてsqrt(r 2 + 1)= rであることがわかります。ここで、sqrtは浮動小数点平方根関数です。攻撃の計画は、何らかの十分に大きい整数r に対してx 2 + y 2 = r 2 + 1となるようなペアP =(x、y)を見つけることです。これは簡単ですが、このようなペアを個別に探すのは遅すぎて面白くありません。前のプログラムで正直なペアを作成したのと同じように、これらのペアをまとめて見つけたいと思います。

ましょう{ VW }ベクトルの正規直交対です。すべての実スカラーrについて、|| r v + w || 2 = R 2 + 1。で2、これは、ピタゴラスの定理の直接の結果です:

画像1

私たちは、ベクトルを探しているVWが存在するような整数 Rれるxはyはまた整数であるが。ちなみに、我々は前の二つのプログラムで使用不正ペアのセットは、単に本の特別な場合、であったことを注記として、{ 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))は正規直交であり、検証が容易です。 。結局のところ、ピタゴラスのトリプレットの選択には、xyが整数であるような整数rが存在します。これを証明し、rPを効果的に見つけるには、少しの数/群理論が必要です。詳細は省きます。いずれにしても、積分rxyがある仮定します。まだいくつかのことが足りません。rが必要です。十分に大きくするために、このメソッドからより多くの同様のペアを導出する高速な方法が必要です。幸いなことに、これを達成する簡単な方法があります。

Pのvへの射影は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)のペアの2乗の大きさは(r + cn)2 + 1であり、まさに探しているペアの種類です!nが十分に大きい場合、これらは大きさr + cnの不正なペアです。

具体的な例を見るのはいつもいいことです。ピタゴラスのトリプレット(3、4、5)を取得すると、r = 2P =(1、2)になります((1、2)を確認できます(4/5、3/5)= 2を)そして、明らかに、1 2 + 2 2 = 2 2 + 1)を追加5R及び(4,3)Pはに私たちを取り、R '= 2 + 5 = 7P' =(+ 4 1、2 + 3)=(5、5)。見よ、5 2 + 5 2 = 7 2 + 1。次の座標はr '' = 12およびP '' =(9、8)であり、9 2 + 8 2 = 12 2 + 1などです...

画像2

いったんrが十分な大きさで、我々は大きさの単位で不誠実なペアを取得を開始5。それはおよそ27,797,402 / 5不正なペアです。

だから今、私たちは整数の大きさの不正なペアをたくさん持っています。最初のプログラムの正直なペアとそれらを簡単に組み合わせて誤検出を形成できます。また、慎重に2番目のプログラムの正直なペアを使用することもできます。これは基本的にこのプログラムが行うことです。前のプログラムと同様に、その結​​果のほとんどを非常に早い段階で見つけます-数秒以内に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を獲得し、リーダーボードを更新しました。
マーティン・エンダー

8

Python、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。したがって、上記の範囲内のすべての数値xは、いくつかの0 <= x '<2 26に対して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が必要な場合 、ただし、倍精度浮動小数点数の仮数は52ビット幅であるため、x + e> xとなる最小のee = 2です。
2 + 1つの、我々が必要とする、少なくとも 2 27 E <= 1である、 E <= 2 -2726から52 = 2 -26。言い換えると、 xより大きい最小の表現可能な数は x + 2 -26であり、 sqrt(x 2 + 1)の結果は最大で x + 2です。 -27です。デフォルトのIEEE-754丸めモードは最も近い値に丸められるため、均等に、それは常に xに丸められ、 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 2 + b 2あるようなすべての正の整数のトリプレット(a、b、c)を探します。 = 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 14

面白いのは、この範囲内のすべての数値が、x ^ 2とx ^ 2 + 1の平方根の単一のインスタンスが整数+ 1/2の異なる側に落ちることなく機能することです。
feersum

@xnor大きさの2乗が64ビット浮動小数点数で正確に表現できるように、境界が選択されました。
マーティンエンダー14

ねえ、うまくいく、誰がどうやって気にする?;)プログラムがダミーループでカウントする必要があるのか​​、実際に結果を検証するのか。
エル14

@MartinButtnerああ、なるほど。下限はその量を2の平方根で割った値のようです。このような数値が機能する理由を発見的に理解していますが、なぜすべてが機能するのか興味があります。
XNOR

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値をチェックしました。このプログラムからのデバッグ出力を見ると、パラメーター化のパラメーター化の特定のパラメーター化に気付きました。これにより、多くの数値を簡単に生成できました。私は自分のものをたくさん持っていたので、エルの数字を印刷しないことを選択しました。うまくいけば、誰かが私たちの数字の両方を印刷せず、勝者であると主張しないことを願っています:)

 /* 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何が起こっているのか、手がかりはありますか?
マーティンエンダー14

@Martinわからない...投稿をファイルにコピーし、同一のスイッチを備えたWindows 8ラップトップでコンパイルしました。私にとってはうまくいきます。gccのバージョンは何ですか?
feersum 14

ところで、エラーが発生した場合、完全に不要なスレッド関連ビットをすべて削除できます。不要な「-K」オプションを使用する場合にのみ、それらは何かを行います。
feersum 14

g++ (GCC) 4.8.1。さて、スレッドビットを削除しましたが、まだstricmp何らかの理由で認識されていません。
マーティンエンダー

1
現在、他にも多くのことが行われているので、アプローチを改善するためのアイデアをお伝えします。半径二乗範囲の上端近くで、あなたも2によって異なる半径の二乗の間の衝突を得ることができます
ピーター・テイラー

1

Java、ブレゼンハム風の円スキャン

発見的には、環のより広い端から開始することで、より多くの衝突が発生することを期待しています。私は、それぞれの衝突のために1回のスキャンを行うための値を記録することによって、いくつかの改善を得るために期待surplusとの間にある0r2max - r2は遅くなり、このバージョンよりも証明した私のテストでは、包括的。同様に、単一のint[]、2要素の配列とリストを大量に作成するのではなくバッファーとします。実際、パフォーマンスの最適化は奇妙なものです。

ペアの出力にはコマンドライン引数を使用して実行し、単純なカウントには使用しません。

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

Java-27,817,255

これらのほとんどはエルが示すものと同じで、残りはに基づいてい(j,0) (k,l)ます。それぞれについてj、いくつかの正方形に戻って、残りが偽陽性を与えるかどうかを確認します。これは基本的に全時間を占めます(j,0) (j,1)が、ちょうど25k(約0.1%)のゲインしかありませんが、ゲインはゲインです。

これは私のマシンで10分以内に終了しますが、何を持っているのかわかりません。理由は、時間がなくなる前に終了しないと、スコアが大幅に低下するためです。その場合、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個の出力(divisor = 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行のコメントを外すことで、ペア(およびそれらの正確な2乗の大きさ)を印刷できます。

基本的に、これは検索を開始します x = y = 6e7は最初の座標ペアし、xをデクリメントする前にx軸まで約1%スキャンダウンします。次に、このような座標ペアごとに、同じ大きさの弧全体(切り上げと切り下げ)で衝突をチェックします。

このコードは、64ビットシステムで実行されることを前提としているため、既定の整数型と浮動小数点型は64ビット型です(そうでない場合は、コンストラクタint64()float64()コンストラクタで作成できます)。

それはわずかな530の結果をもたらします。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.