การประมาณกรณีพิเศษของฟังก์ชัน Riemann Theta


27

ความท้าทายนี้คือการเขียนโค้ดที่รวดเร็วซึ่งสามารถทำการหาผลรวมอนันต์ที่ยากต่อการคำนวณ

อินพุต

nโดยnเมทริกซ์Pที่มีรายการจำนวนเต็มที่มีขนาดเล็กกว่า100ในค่าสัมบูรณ์ เมื่อทดสอบฉันยินดีที่จะให้ข้อมูลกับโค้ดของคุณในรูปแบบที่สมเหตุสมผลและโค้ดของคุณต้องการ ค่าเริ่มต้นจะเป็นหนึ่งบรรทัดต่อแถวของเมทริกซ์ซึ่งมีการเว้นวรรคและให้ไว้ในอินพุตมาตรฐาน

Pจะเป็นบวกแน่นอนซึ่งหมายความว่ามันจะสมมาตรเสมอ นอกเหนือจากนั้นคุณไม่จำเป็นต้องรู้ว่าหมายถึงอะไรที่ชัดเจนในเชิงบวกที่จะตอบความท้าทาย อย่างไรก็ตามมันหมายความว่าจะมีคำตอบสำหรับผลรวมที่กำหนดไว้ด้านล่าง

อย่างไรก็ตามคุณจำเป็นต้องรู้ว่าผลิตภัณฑ์matrix-vectorคืออะไร

เอาท์พุต

รหัสของคุณควรคำนวณผลรวมอนันต์:

ป้อนคำอธิบายรูปภาพที่นี่

ภายในบวกหรือลบ 0.0001 ของคำตอบที่ถูกต้อง นี่Zคือชุดของจำนวนเต็มและZ^nเป็นเวกเตอร์ที่เป็นไปได้ทั้งหมดที่มีnองค์ประกอบจำนวนเต็มและeเป็นค่าคงที่ทางคณิตศาสตร์ที่มีชื่อเสียงที่มีค่าประมาณ 2.71828 โปรดทราบว่าค่าในเลขชี้กำลังเป็นเพียงตัวเลข ดูตัวอย่างด้านล่างอย่างชัดเจน

สิ่งนี้เกี่ยวข้องกับฟังก์ชัน Riemann Theta อย่างไร

ในสัญกรณ์ของการวิจัยนี้ในที่ใกล้เคียงกับฟังก์ชั่น Riemann ทีป้อนคำอธิบายรูปภาพที่นี่เรากำลังพยายามที่จะคำนวณ ปัญหาของเราเป็นกรณีพิเศษด้วยเหตุผลอย่างน้อยสองประการ

  • เราตั้งค่าพารามิเตอร์เริ่มต้นที่เรียกว่าzในกระดาษที่เชื่อมโยงเป็น 0
  • เราสร้างเมทริกซ์Pในลักษณะดังกล่าวที่มีขนาดเล็กที่สุดของ eigenvalue 1คือ (ดูด้านล่างสำหรับวิธีสร้างเมทริกซ์)

ตัวอย่าง

P = [[ 5.,  2.,  0.,  0.],
     [ 2.,  5.,  2., -2.],
     [ 0.,  2.,  5.,  0.],
     [ 0., -2.,  0.,  5.]]

Output: 1.07551411208

รายละเอียดเพิ่มเติมให้เราเห็นเพียงหนึ่งคำในผลรวมของ P นี้ใช้ตัวอย่างเพียงหนึ่งคำในผลรวม:

ป้อนคำอธิบายรูปภาพที่นี่

x^T P x = 30และ ขอให้สังเกตว่าe^(-30)เป็นเรื่องเกี่ยวกับ10^(-14)และดังนั้นจึงไม่น่าจะมีความสำคัญสำหรับการได้รับคำตอบที่ถูกต้องถึงความอดทน จำได้ว่าผลรวมไม่สิ้นสุดจะใช้เวกเตอร์ที่มีความยาว 4 ที่เป็นไปได้ทั้งหมดโดยที่องค์ประกอบนั้นเป็นจำนวนเต็ม ฉันเพิ่งเลือกหนึ่งตัวอย่างที่ชัดเจน

P = [[ 5.,  2.,  2.,  2.],
     [ 2.,  5.,  4.,  4.],
     [ 2.,  4.,  5.,  4.],
     [ 2.,  4.,  4.,  5.]]

Output = 1.91841190706

P = [[ 6., -3.,  3., -3.,  3.],
     [-3.,  6., -5.,  5., -5.],
     [ 3., -5.,  6., -5.,  5.],
     [-3.,  5., -5.,  6., -5.],
     [ 3., -5.,  5., -5.,  6.]]

Output = 2.87091065342

P = [[6., -1., -3., 1., 3., -1., -3., 1., 3.],
     [-1., 6., -1., -5., 1., 5., -1., -5., 1.],
     [-3., -1., 6., 1., -5., -1., 5., 1., -5.],
     [1., -5., 1., 6., -1., -5., 1., 5., -1.],
     [3., 1., -5., -1., 6., 1., -5., -1., 5.],
     [-1., 5., -1., -5., 1., 6., -1., -5., 1.],
     [-3., -1., 5., 1., -5., -1., 6., 1., -5.],
     [1., -5., 1., 5., -1., -5., 1., 6., -1.],
     [3., 1., -5., -1., 5., 1., -5., -1., 6.]]

Output: 8.1443647932

P = [[ 7.,  2.,  0.,  0.,  6.,  2.,  0.,  0.,  6.],
     [ 2.,  7.,  0.,  0.,  2.,  6.,  0.,  0.,  2.],
     [ 0.,  0.,  7., -2.,  0.,  0.,  6., -2.,  0.],
     [ 0.,  0., -2.,  7.,  0.,  0., -2.,  6.,  0.],
     [ 6.,  2.,  0.,  0.,  7.,  2.,  0.,  0.,  6.],
     [ 2.,  6.,  0.,  0.,  2.,  7.,  0.,  0.,  2.],
     [ 0.,  0.,  6., -2.,  0.,  0.,  7., -2.,  0.],
     [ 0.,  0., -2.,  6.,  0.,  0., -2.,  7.,  0.],
     [ 6.,  2.,  0.,  0.,  6.,  2.,  0.,  0.,  7.]]

Output = 3.80639191181

คะแนน

ฉันจะทดสอบโค้ดของคุณในเมทริกซ์ P ที่เลือกแบบสุ่มซึ่งมีขนาดเพิ่มขึ้น

คะแนนของคุณเป็นคะแนนที่ใหญ่ที่สุดnที่ฉันได้รับคำตอบที่ถูกต้องในเวลาน้อยกว่า 30 วินาทีเมื่อเฉลี่ยมากกว่า 5 ครั้งด้วยการสุ่มเลือกPขนาดนั้น

แล้วเนคไทล่ะ?

หากมีการเสมอกันผู้ชนะจะเป็นผู้ที่มีรหัสรันเร็วที่สุดโดยเฉลี่ยมากกว่า 5 ครั้ง ในกรณีที่เวลาเท่ากันผู้ชนะคือคำตอบแรก

จะมีการสร้างอินพุตแบบสุ่มอย่างไร

  1. Let M เป็นเมตรโดยการสุ่มด้วย n เมทริกซ์กับม <= n และรายการซึ่งเป็น -1 หรือ 1 M = np.random.choice([0,1], size = (m,n))*2-1ในหลาม ในทางปฏิบัติเราจะตั้งจะเกี่ยวกับmn/2
  2. ให้ P เป็นเมทริกซ์เอกลักษณ์ + M ^ T P =np.identity(n)+np.dot(M.T,M)เมตรในหลาม ตอนนี้เรารับประกันว่าPจะเป็นบวกแน่นอนและรายการอยู่ในช่วงที่เหมาะสม

โปรดทราบว่านี่หมายความว่าค่าลักษณะเฉพาะทั้งหมดของ P มีค่าอย่างน้อย 1 ทำให้เกิดปัญหาได้ง่ายกว่าปัญหาทั่วไปของการประมาณฟังก์ชัน Riemann Theta

ภาษาและห้องสมุด

คุณสามารถใช้ภาษาหรือไลบรารีที่คุณต้องการ อย่างไรก็ตามเพื่อวัตถุประสงค์ในการให้คะแนนฉันจะเรียกใช้รหัสของคุณบนเครื่องของฉันดังนั้นโปรดให้คำแนะนำที่ชัดเจนเกี่ยวกับวิธีการเรียกใช้บน Ubuntu

My Machineเวลาของฉันจะทำงานบนเครื่องของฉัน นี่คือการติดตั้ง Ubuntu แบบมาตรฐานบนโปรเซสเซอร์ AMD FX-8350 Eight-Core 8GB นี่ก็หมายความว่าฉันต้องสามารถเรียกใช้รหัสของคุณได้


คำตอบชั้นนำ

  • n = 47ในC ++โดย Ton Hospel
  • n = 8ในPythonโดย Maltysen

มันอาจจะคุ้มค่าที่จะกล่าวถึงว่าเมทริกซ์แน่นอนบวกเป็นสมมาตรโดยนิยาม
2012rcampion

@ 2012rcampion ขอบคุณ ที่เพิ่ม

ตกลงบางทีนี่อาจเป็นคำถามที่โง่ แต่ฉันก็จ้องที่นี่มานานแล้วและฉันไม่สามารถรู้ได้ว่าคุณได้รับxจาก[-1,0,2,1]อะไร คุณสามารถทำอย่างละเอียดเกี่ยวกับเรื่องนี้? (คำแนะนำ: ฉันไม่ใช่กูรูคณิตศาสตร์)
wnnmaw

@wnnmaw ขออภัยที่สับสน ผลรวมมีระยะหนึ่งสำหรับทุกเวกเตอร์ x เป็นไปได้ของความยาว 4 ในกรณีนี้ [-1,0,2,1] เป็นเพียงสิ่งเดียวที่ฉันเลือกแบบสุ่มเพื่อแสดงอย่างชัดเจนว่าคำนั้นจะเป็นอย่างไรในกรณีนั้น

1
@Lembik วิธีที่คุณสร้างเมทริกซ์ SPD แสดงว่าไม่มีค่าเอกพจน์ที่มีค่าสัมบูรณ์ต่ำกว่า 1 เราสามารถใช้ความรู้นั้นได้หรือไม่?
ข้อบกพร่อง

คำตอบ:


15

C ++

ไม่มีวิธีการที่ไร้เดียงสามากขึ้น ประเมินเฉพาะภายในรูปวงรี

ใช้ไลบรารีตัวนิ่ม, ntl, gsl และ pthread ติดตั้งโดยใช้

apt-get install libarmadillo-dev libntl-dev libgsl-dev

รวบรวมโปรแกรมโดยใช้สิ่งที่ชอบ:

g++ -Wall -std=c++11 -O3 -fno-math-errno -funsafe-math-optimizations -ffast-math -fno-signed-zeros -fno-trapping-math -fomit-frame-pointer -march=native -s infinity.cpp -larmadillo -lntl -lgsl -lpthread -o infinity

ในบางระบบคุณอาจจำเป็นต้องเพิ่มหลัง-lgslcblas-lgsl

ทำงานด้วยขนาดของเมทริกซ์ตามด้วยองค์ประกอบบน STDIN:

./infinity < matrix.txt

matrix.txt:

4
5  2  0  0
2  5  2 -2
0  2  5  0
0 -2  0  5

หรือลองใช้ความแม่นยำ 1e-5:

./infinity -p 1e-5 < matrix.txt

infinity.cpp:

// Based on http://arxiv.org/abs/nlin/0206009

#include <iostream>
#include <vector>
#include <stdexcept>
#include <cstdlib>
#include <cmath>
#include <string>
#include <thread>
#include <future>
#include <chrono>

using namespace std;

#include <getopt.h>

#include <armadillo>

using namespace arma;

#include <NTL/mat_ZZ.h>
#include <NTL/LLL.h>

using namespace NTL;

#include <gsl/gsl_sf_gamma.h>
#include <gsl/gsl_errno.h>
#include <gsl/gsl_roots.h>

double const EPSILON = 1e-4;       // default precision
double const GROW    = 2;          // By how much we grow the ellipsoid volume
double const UPSCALE = 1e9;        // lattice reduction, upscale real to integer
double const THREAD_SEC = 0.1;     // Use threads if need more time than this
double const RADIUS_MAX = 1e6;     // Maximum radius used in root finding
double const RADIUS_INTERVAL = 1e-6; // precision of target radius
int const ITER_MAX = 1000;         // Maximum iterations in root finding
unsigned long POINTS_MIN = 1000;   // Minimum points before getting fancy

struct Result {
    Result& operator+=(Result const& add) {
        sum     += add.sum;
        elapsed += add.elapsed;
        points  += add.points;
        return *this;
    }

    friend Result operator-(Result const& left, Result const& right) {
        return Result{left.sum - right.sum,
                left.elapsed - right.elapsed,
                left.points - right.points};
    }

    double sum, elapsed;
    unsigned long points;
};

struct Params {
    double half_rho, half_N, epsilon;
};

double fill_factor_error(double r, void *void_params) {
    auto params = static_cast<Params*>(void_params);
    r -= params->half_rho;
    return gsl_sf_gamma_inc(params->half_N, r*r) - params->epsilon;
}

// Calculate radius needed for target precision
double radius(int N, double rho, double lat_det, double epsilon) {
    Params params;

    params.half_rho = rho / 2.;
    params.half_N   = N   / 2.;
    params.epsilon = epsilon*lat_det*gsl_sf_gamma(params.half_N)/pow(M_PI, params.half_N);

    // Calculate minimum allowed radius
    auto r = sqrt(params.half_N)+params.half_rho;
    auto val = fill_factor_error(r, &params);
    cout << "Minimum R=" << r << " -> " << val << endl;

    if (val > 0) {
        // The minimum radius is not good enough. Work out a better one by
        // finding the root of a tricky function
        auto low  = r;
        auto high = RADIUS_MAX * 2 * params.half_rho;
        auto val = fill_factor_error(high, &params);
        if (val >= 0)
            throw(logic_error("huge RADIUS_MAX is still not big enough"));

        gsl_function F;
        F.function = fill_factor_error;
        F.params   = &params;

        auto T = gsl_root_fsolver_brent;
        auto s = gsl_root_fsolver_alloc (T);
        gsl_root_fsolver_set (s, &F, low, high);

        int status = GSL_CONTINUE;
        for (auto iter=1; status == GSL_CONTINUE && iter <= ITER_MAX; ++iter) {
            gsl_root_fsolver_iterate (s);
            low  = gsl_root_fsolver_x_lower (s);
            high = gsl_root_fsolver_x_upper (s);
            status = gsl_root_test_interval(low, high, 0, RADIUS_INTERVAL  * 2 * params.half_rho);
        }
        r = gsl_root_fsolver_root(s);
        gsl_root_fsolver_free(s);
        if (status == GSL_CONTINUE)
            throw(logic_error("Search for R did not converge"));
    }
    return r;
}

// Recursively walk down the ellipsoids in each dimension
void ellipsoid(int d, mat const& A, double const* InvD, mat& Accu,
               Result& result, double r2) {
    auto r = sqrt(r2);
    auto offset = Accu(d, d);
    // InvD[d] = 1/ A(d, d)
    auto from = ceil((-r-offset) * InvD[d]);
    auto to   = floor((r-offset) * InvD[d]);
    for (auto v = from; v <= to; ++v) {
        auto value  = v * A(d, d)+offset;
        auto residu = r2 - value*value;
        if (d == 0) {
            result.sum += exp(residu);
            ++result.points;
        } else {
            for (auto i=0; i<d; ++i) Accu(d-1, i) = Accu(d, i) + v * A(d, i);
            ellipsoid(d-1, A, InvD, Accu, result, residu);
        }
    }
}

// Specialised version of ellipsoid() that will only process points an octant
void ellipsoid(int d, mat const& A, double const* InvD, mat& Accu,
               Result& result, double r2, unsigned int octant) {
    auto r = sqrt(r2);
    auto offset = Accu(d, d);
    // InvD[d] = 1/ A(d, d)
    long from = ceil((-r-offset) * InvD[d]);
    long to   = floor((r-offset) * InvD[d]);
    auto points = to-from+1;
    auto base = from + points/2;
    if (points & 1) {
        auto value = base * A(d, d) + offset;
        auto residu = r2 - value * value;
        if (d == 0) {
            if ((octant & (octant - 1)) == 0) {
                result.sum += exp(residu);
                ++result.points;
            }
        } else {
            for (auto i=0; i<d; ++i) Accu(d-1, i) = Accu(d, i) + base * A(d, i);
            ellipsoid(d-1, A, InvD, Accu, result, residu, octant);
        }
        ++base;
    }
    if ((octant & 1) == 0) {
        to = from + points / 2 - 1;
        base = from;
    }
    octant /= 2;
    for (auto v = base; v <= to; ++v) {
        auto value = v * A(d,d)+offset;
        auto residu = r2 - value*value;
        if (d == 0) {
            if ((octant & (octant - 1)) == 0) {
                result.sum += exp(residu);
                ++result.points;
            }
        } else {
            for (auto i=0; i<d; ++i) Accu(d-1, i) = Accu(d, i) + v * A(d, i);
            if (octant == 1)
                ellipsoid(d-1, A, InvD, Accu, result, residu);
            else
                ellipsoid(d-1, A, InvD, Accu, result, residu, octant);
        }
    }
}

// Prepare call to ellipsoid()
Result sym_ellipsoid(int N, mat const& A, const vector<double>& InvD, double r,
                     unsigned int octant = 1) {
    auto start = chrono::steady_clock::now();
    auto r2 = r*r;

    mat Accu(N, N);
    Accu.row(N-1).zeros();

    Result result{0, 0, 0};
    // 2*octant+1 forces the points into the upper half plane, skipping 0
    // This way we use the lattice symmetry and calculate only half the points
    ellipsoid(N-1, A, &InvD[0], Accu, result, r2, 2*octant+1);
    // Compensate for the extra factor exp(r*r) we always add in ellipsoid()
    result.sum /= exp(r2);
    auto end = chrono::steady_clock::now();
    result.elapsed = chrono::duration<double>{end-start}.count();

    return result;
}

// Prepare multithreaded use of sym_ellipsoid(). Each thread gets 1 octant
Result sym_ellipsoid_t(int N, mat const& A, const vector<double>& InvD, double r, unsigned int nr_threads) {
    nr_threads = pow(2, ceil(log2(nr_threads)));

    vector<future<Result>> results;
    for (auto i=nr_threads+1; i<2*nr_threads; ++i)
        results.emplace_back(async(launch::async, sym_ellipsoid, N, ref(A), ref(InvD), r, i));
    auto result = sym_ellipsoid(N, A, InvD, r, nr_threads);
    for (auto i=0U; i<nr_threads-1; ++i) result += results[i].get();
    return result;
}

int main(int argc, char* const* argv) {
    cin.exceptions(ios::failbit | ios::badbit);
    cout.precision(12);

    double epsilon    = EPSILON; // Target absolute error
    bool inv_modular  = true;    // Use modular transform to get the best matrix
    bool lat_reduce   = true;    // Use lattice reduction to align the ellipsoid
    bool conservative = false;   // Use provable error bound instead of a guess
    bool eigen_values = false;   // Show eigenvalues
    int  threads_max  = thread::hardware_concurrency();

    int option_char;
    while ((option_char = getopt(argc, argv, "p:n:MRce")) != EOF)
        switch (option_char) {
            case 'p': epsilon      = atof(optarg); break;
            case 'n': threads_max  = atoi(optarg); break;
            case 'M': inv_modular  = false;        break;
            case 'R': lat_reduce   = false;        break;
            case 'c': conservative = true;         break;
            case 'e': eigen_values = true;         break;
            default:
              cerr << "usage: " << argv[0] << " [-p epsilon] [-n threads] [-M] [-R] [-e] [-c]" << endl;
              exit(EXIT_FAILURE);
        }
    if (optind < argc) {
        cerr << "Unexpected argument" << endl;
        exit(EXIT_FAILURE);
    }
    if (threads_max < 1) threads_max = 1;
    threads_max = pow(2, ceil(log2(threads_max)));
    cout << "Using up to " << threads_max << " threads" << endl;

    int N;
    cin >> N;

    mat P(N, N);
    for (auto& v: P) cin >> v;

    if (eigen_values) {
        vec eigval = eig_sym(P);
        cout << "Eigenvalues:\n" << eigval << endl;
    }

    // Decompose P = A * A.t()
    mat A = chol(P, "lower");

    // Calculate lattice determinant
    double lat_det = 1;
    for (auto i=0; i<N; ++i) {
        if (A(i,i) <= 0) throw(logic_error("Diagonal not Positive"));
        lat_det *= A(i,i);
    }
    cout << "Lattice determinant=" << lat_det << endl;

    auto factor = lat_det / pow(M_PI, N/2.0);
    if (inv_modular && factor < 1) {
        epsilon *= factor;
        cout << "Lattice determinant is small. Using inverse instead. Factor=" << factor << endl;
        P = M_PI * M_PI * inv(P);
        A = chol(P, "lower");
        // We could simple calculate the new lat_det as pow(M_PI,N)/lat_det
        lat_det = 1;
        for (auto i=0; i<N; ++i) {
            if (A(i,i) <= 0) throw(logic_error("Diagonal not Positive"));
            lat_det *= A(i,i);
        }
        cout << "New lattice determinant=" << lat_det << endl;
    } else
        factor = 1;

    // Prepare for lattice reduction.
    // Since the library works on integer lattices we will scale up our matrix
    double min = INFINITY;
    for (auto i=0; i<N; ++i) {
        for (auto j=0; j<N;++j)
            if (A(i,j) != 0 && abs(A(i,j) < min)) min = abs(A(i,j));
    }

    auto upscale = UPSCALE/min;
    mat_ZZ a;
    a.SetDims(N,N);
    for (auto i=0; i<N; ++i)
        for (auto j=0; j<N;++j) a[i][j] = to_ZZ(A(i,j)*upscale);

    // Finally do the actual lattice reduction
    mat_ZZ u;
    auto rank = G_BKZ_FP(a, u);
    if (rank != N) throw(logic_error("Matrix is singular"));
    mat U(N,N);
    for (auto i=0; i<N;++i)
        for (auto j=0; j<N;++j) U(i,j) = to_double(u[i][j]);

    // There should now be a short lattice vector at row 0
    ZZ sum = to_ZZ(0);
    for (auto j=0; j<N;++j) sum += a[0][j]*a[0][j];
    auto rho = sqrt(to_double(sum))/upscale;
    cout << "Rho=" << rho << " (integer square " <<
        rho*rho << " ~ " <<
        static_cast<int>(rho*rho+0.5) << ")" << endl;

    // Lattice reduction doesn't gain us anything conceptually.
    // The same number of points is evaluated for the same exponential values
    // However working through the ellipsoid dimensions from large lattice
    // base vectors to small makes ellipsoid() a *lot* faster
    if (lat_reduce) {
        mat B = U * A;
        P = B * B.t();
        A = chol(P, "lower");
        if (eigen_values) {
            vec eigval = eig_sym(P);
            cout << "New eigenvalues:\n" << eigval << endl;
        }
    }

    vector<double> InvD(N);;
    for (auto i=0; i<N; ++i) InvD[i] = 1 / A(i, i);

    // Calculate radius needed for target precision
    auto r = radius(N, rho, lat_det, epsilon);
    cout << "Safe R=" << r << endl;

    auto nr_threads = threads_max;
    Result result;
    if (conservative) {
        // Walk all points inside the ellipsoid with transformed radius r
        result = sym_ellipsoid_t(N, A, InvD, r, nr_threads);
    } else {
        // First grow the radius until we saw POINTS_MIN points or reach the
        // target radius
        double i = floor(N * log2(r/rho) / log2(GROW));
        if (i < 0) i = 0;
        auto R = r * pow(GROW, -i/N);
        cout << "Initial R=" << R << endl;
        result = sym_ellipsoid_t(N, A, InvD, R, nr_threads);
        nr_threads = result.elapsed < THREAD_SEC ? 1 : threads_max;
        auto max_new_points = result.points;
        while (--i >= 0 && result.points < POINTS_MIN) {
            R = r * pow(GROW, -i/N);
            auto change = result;
            result = sym_ellipsoid_t(N, A, InvD, R, nr_threads);
            nr_threads = result.elapsed < THREAD_SEC ? 1 : threads_max;
            change = result - change;

            if (change.points > max_new_points) max_new_points = change.points;
        }

        // Now we have enough points that it's worth bothering to use threads
        while (--i >= 0) {
            R = r * pow(GROW, -i/N);
            auto change = result;
            result = sym_ellipsoid_t(N, A, InvD, R, nr_threads);
            nr_threads = result.elapsed < THREAD_SEC ? 1 : threads_max;
            change = result - change;
            // This is probably too crude and might misestimate the error
            // I've never seen it fail though
            if (change.points > max_new_points) {
                max_new_points = change.points;
                if (change.sum < epsilon/2) break;
            }
        }
        cout << "Final R=" << R << endl;
    }

    // We calculated half the points and skipped 0.
    result.sum = 2*result.sum+1;

    // Modular transform factor
    result.sum /= factor;

    // Report result
    cout <<
        "Evaluated " << result.points << " points\n" <<
        "Sum = " << result.sum << endl;
}

นี่เป็นสิ่งที่น่าประทับใจและดีกว่าแนวทางที่ไร้เดียงสาในมุมมองของฉัน ฉันหวังว่าจะได้เอกสารประกอบ :)

1
@TonHospel คุณช่วยบอกเราอีกเล็กน้อยเกี่ยวกับวิธีที่คุณคิดขอบเขต?
ข้อบกพร่อง

2
ฉันใช้ Arch Linux และต้องการให้-lgslcblasแฟล็กคอมไพล์ คำตอบที่น่าทึ่งโดยวิธี!
Rhyzomatic

2

Python 3

12 วินาที n = 8 บนคอมพิวเตอร์ของฉัน, อูบุนตู 4 คอร์

ไร้เดียงสาจริงๆไม่มีเงื่อนงำสิ่งที่ฉันทำ

from itertools import product
from math import e

P = [[ 6., -3.,  3., -3.,  3.],
     [-3.,  6., -5.,  5., -5.],
     [ 3., -5.,  6., -5.,  5.],
     [-3.,  5., -5.,  6., -5.],
     [ 3., -5.,  5., -5.,  6.]]

N = 2

n = [1]

while e** -n[-1] > 0.0001:
    n = []
    for x in product(list(range(-N, N+1)), repeat = len(P)):
        n.append(sum(k[0] * k[1] for k in zip([sum(j[0] * j[1] for j in zip(i, x)) for i in P], x)))
    N += 1

print(sum(e** -i for i in n))

สิ่งนี้จะช่วยเพิ่มช่วงของการZใช้จนกว่าจะได้คำตอบที่ดีพอ ฉันเขียนการคูณเมทริกซ์ของตัวเอง prolly ควรใช้จำนวนเต็ม


ขอบคุณมาก! คุณสามารถแสดงเอาท์พุทและการจับเวลาบนคอมพิวเตอร์ของคุณได้ไหม?

รหัสของคุณทำงานใน pypy ที่ยอดเยี่ยมและรวดเร็ว น่าเสียดาย, [[6.0, -1.0, -3.0, 1.0, 3.0, -1.0, -3.0, 1.0, 3.0], [-1.0, 6.0, -1.0, -5.0, 1.0, 5.0, -1.0, -5.0, 1.0 ], [-3.0, -1.0, 6.0, 1.0, -5.0, -1.0, 5.0, 1.0, -5.0], [1.0, -5.0, 1.0, 6.0, 6.0, -1.0, -5.0, 1.0, 5.0, -1.0] , [3.0, 1.0, -5.0, -1.0, 6.0, 1.0, -5.0, -1.0, 5.0], [-1.0, 5.0, -1.0, -5.0, 1.0, 6.0, -1.0, -5.0, 1.0], [-3.0, -1.0, 5.0, 1.0, -5.0, -1.0, 6.0, 1.0, -5.0], [1.0, -5.0, 1.0, 5.0, -1, -1, -5.0, 1.0, 6.0, -1.0], [ 3.0, 1.0, -5.0, -1.0, 5.0, 1.0, -5.0, -1.0, 6.0]] ให้คำตอบที่ผิด

8.1443647932-8.14381938863 = 0.00054540457> 0.0001

3
@Maltysen โปรแกรมของคุณจะตรวจสอบว่าคำสุดท้ายนั้นเล็กกว่าความแม่นยำที่กำหนดหรือไม่ แต่ข้อผิดพลาดที่คุณทำนั้นใหญ่กว่าเนื่องจากคุณจะต้องพิจารณาผลรวมของคำอื่น ๆ ทั้งหมดสำหรับข้อผิดพลาดด้วย!
ข้อบกพร่อง
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.