Số chia gần đúng nhanh nhất


13

Tổng quat

Trong thử thách này, bạn sẽ được cung cấp hai số là cả hai phần bù nhỏ lớn hơn bội số của một số cỡ trung bình. Bạn phải xuất ra một số có kích thước trung bình gần như là một ước của cả hai số, ngoại trừ một phần bù nhỏ.

Kích thước của các số liên quan sẽ được tham số hóa bằng tham số độ khó , l. Mục tiêu của bạn là giải quyết vấn đề lớn nhất có thể ltrong vòng dưới 1 phút.


Thiết lập

Trong một vấn đề nhất định, sẽ có một số bí mật p, đó sẽ là một số bit ngẫu nhiên l^2( l*l). Sẽ có hai số nhân, q1, q2sẽ là l^3số bit ngẫu nhiên và sẽ có hai số bù, r1, r2sẽ là ngẫu nhiênl số bit .

Đầu vào cho chương trình của bạn sẽ được x1, x2xác định là:

x1 = p * q1 + r1
x2 = p * q2 + r2

Đây là một chương trình để tạo các trường hợp thử nghiệm, trong Python:

from random import randrange
from sys import argv

l = int(argv[1])

def randbits(bits):
    return randrange(2 ** (bits - 1), 2 ** bits)

p = randbits(l ** 2)
print(p)
for i in range(2):
    q_i = randbits(l ** 3)
    r_i = randbits(l)
    print(q_i * p + r_i)

Dòng đầu ra đầu tiên là một giải pháp khả thi, trong khi dòng thứ hai và thứ ba là đầu vào mà chương trình của bạn sẽ được cung cấp.


Chương trình của bạn

Với x1, x2l, bạn phải tìm một l^2số chút p'như vậy x1 % p'x2 % p'đều lsố bit. psẽ luôn luôn làm việc, mặc dù có thể có những khả năng khác. Đây là một chức năng để xác minh một giải pháp:

def is_correct(x1, x2, l, p_prime):
    p_prime_is_good = p_prime >> (l**2 - 1) and not p_prime >> l ** 2
    x1_is_good = (x1 % p_prime) >> (l-1) and not (x1 % p_prime) >> l
    x2_is_good = (x2 % p_prime) >> (l-1) and not (x2 % p_prime) >> l
    return bool(p_prime_is_good and x1_is_good and x2_is_good)

Thí dụ

Giả sử llà 3. Chương trình trình tạo chọn một số 9 bit p, trong trường hợp này là 442. Trình tạo chọn hai 3số bit cho r1, r2, đó là 4, 7. Trình tạo chọn hai 27số bit cho q1, q2, đó là 117964803, 101808039. Vì những lựa chọn này, x1, x252140442930, 44999153245 .

Chương trình của bạn sẽ được cung cấp 52140442930, 44999153245làm đầu vào và phải xuất ra một số 9 bit (trong phạm vi [256, 511]) sao cho số đó 5214044293044999153245modulo đó cho 3 số bit (trong phạm vi [4, 7]). 442là giá trị duy nhất như vậy trong trường hợp này, vì vậy chương trình của bạn sẽ phải xuất ra 442.


Thêm ví dụ

l = 2
x1 = 1894
x2 = 2060
p = 11
No other p'.

l = 3
x1 = 56007668599
x2 = 30611458895
p = 424
No other p'.

l = 6
x1 = 4365435975875889219149338064474396898067189178953471159903352227492495111071
x2 = 6466809655659049447127736275529851894657569985804963410176865782113074947167
p = 68101195620
I don't know whether there are other p'.

l = 12
x1 = 132503538560485423319724633262218262792296147003813662398252348727558616998821387759658729802732555377599590456096450977511271450086857949046098328487779612488702544062780731169071526325427862701033062986918854245283037892816922645703778218888876645148150396130125974518827547039720412359298502758101864465267219269598121846675000819173555118275197412936184329860639224312426860362491131729109976241526141192634523046343361089218776687819810873911761177080056675776644326080790638190845283447304699879671516831798277084926941086929776037986892223389603958335825223
x2 = 131643270083452525545713630444392174853686642378302602432151533578354175874660202842105881983788182087244225335788180044756143002547651778418104898394856368040582966040636443591550863800820890232349510212502022967044635049530630094703200089437589000344385691841539471759564428710508659169951391360884974854486267690231936418935298696990496810984630182864946252125857984234200409883080311780173125332191068011865349489020080749633049912518609380810021976861585063983190710264511339441915235691015858985314705640801109163008926275586193293353829677264797719957439635
p = 12920503469397123671484716106535636962543473
I don't know whether there are other p'.

l = 12
x1 = 202682323504122627687421150801262260096036559509855209647629958481910539332845439801686105377638207777951377858833355315514789392768449139095245989465034831121409966815913228535487871119596033570221780568122582453813989896850354963963579404589216380209702064994881800638095974725735826187029705991851861437712496046570494304535548139347915753682466465910703584162857986211423274841044480134909827293577782500978784365107166584993093904666548341384683749686200216537120741867400554787359905811760833689989323176213658734291045194879271258061845641982134589988950037
x2 = 181061672413088057213056735163589264228345385049856782741314216892873615377401934633944987733964053303318802550909800629914413353049208324641813340834741135897326747139541660984388998099026320957569795775586586220775707569049815466134899066365036389427046307790466751981020951925232623622327618223732816807936229082125018442471614910956092251885124883253591153056364654734271407552319665257904066307163047533658914884519547950787163679609742158608089946055315496165960274610016198230291033540306847172592039765417365770579502834927831791804602945514484791644440788
p = 21705376375228755718179424140760701489963164

Chấm điểm

Như đã đề cập ở trên, điểm số của chương trình của bạn là cao nhất lmà chương trình hoàn thành trong vòng dưới 1 phút. Cụ thể hơn, chương trình của bạn sẽ được chạy trên 5 trường hợp ngẫu nhiên với điều đó lvà nó phải đưa ra câu trả lời đúng cho cả 5, với thời gian trung bình dưới 1 phút. Điểm của chương trình sẽ là điểm cao nhất lmà nó thành công. Tiebreaker sẽ là thời gian trung bình trên đó l.

Để cung cấp cho bạn một ý tưởng về những điểm số để nhắm đến, tôi đã viết một người giải quyết vũ phu rất đơn giản. Nó được điểm 5. Tôi đã viết một người giải thích dễ thương hơn nhiều. Nó có điểm 12 hoặc 13, tùy thuộc vào may mắn.


Chi tiết

Để so sánh hoàn hảo giữa các câu trả lời, tôi sẽ gửi thời gian trên máy tính xách tay của mình để cho điểm số chuẩn. Tôi cũng sẽ chạy các trường hợp được chọn ngẫu nhiên tương tự trên tất cả các bài nộp, để giảm bớt phần nào may mắn. Máy tính xách tay của tôi có 4 CPU, CPU i5-4300U @ 1.9 GHz, RAM 7.5G.

Vui lòng gửi điểm tạm thời dựa trên thời gian của riêng bạn, chỉ cần làm rõ nó là tạm thời hay hợp quy.


Có thể chương trình nhanh nhất giành chiến thắng!


Liệu gần đúng có phải là gần nhất?
Yytsi

@TuukkaX Bất kỳ l^2số bit nào - lđều không phải là một yếu tố của cả hai số. Thông thường chỉ có một, tuy nhiên.
isaacg

Đây là một luận văn với một số ý tưởng thuật toán: tigerprints.clemson.edu/cgi/ . Đặc biệt tốt và đơn giản là một trong phần 5.1.1
isaacg

Các i5-4300U có 2 lõi (4 bài), chứ không phải 4 lõi.
Anders Kaseorg

Câu trả lời:


3

Rỉ sét, L = 13

src/main.rs

extern crate gmp;
extern crate rayon;

use gmp::mpz::Mpz;
use gmp::rand::RandState;
use rayon::prelude::*;
use std::cmp::max;
use std::env;
use std::mem::swap;

// Solver

fn solve(x1: &Mpz, x2: &Mpz, l: usize) -> Option<Mpz> {
    let m = l*l - 1;
    let r = -1i64 << l-2 .. 1 << l-2;
    let (mut x1, mut x2) = (x1 - (3 << l-2), x2 - (3 << l-2));
    let (mut a1, mut a2, mut b1, mut b2) = (Mpz::one(), Mpz::zero(), Mpz::zero(), Mpz::one());
    while !x2.is_zero() &&
        &(max(a1.abs(), b1.abs()) << l-2) < &x1 &&
        &(max(a2.abs(), b2.abs()) << l-2) < &x2
    {
        let q = &x1/&x2;
        swap(&mut x1, &mut x2);
        x2 -= &q*&x1;
        swap(&mut a1, &mut a2);
        a2 -= &q*&a1;
        swap(&mut b1, &mut b2);
        b2 -= &q*&b1;
    }
    r.clone().into_par_iter().map(|u| {
        let (mut y1, mut y2) = (&x1 - &a1*u + (&b1 << l-2), &x2 - &a2*u + (&b2 << l-2));
        for _ in r.clone() {
            let d = Mpz::gcd(&y1, &y2);
            if d.bit_length() >= m {
                let d = d.abs();
                let (mut k, k1) = (&d >> l*l, &d >> l*l-1);
                while k < k1 {
                    k += 1;
                    if (&d%&k).is_zero() {
                        return Some(&d/&k)
                    }
                }
            }
            y1 -= &b1;
            y2 -= &b2;
        }
        None
    }).find_any(|o| o.is_some()).unwrap_or(None)
}

// Test harness

fn randbits(rnd: &mut RandState, bits: usize) -> Mpz {
    rnd.urandom(&(Mpz::one() << bits - 1)) + (Mpz::one() << bits - 1)
}

fn gen(rnd: &mut RandState, l: usize) -> (Mpz, Mpz, Mpz) {
    let p = randbits(rnd, l*l);
    (randbits(rnd, l*l*l)*&p + randbits(rnd, l),
     randbits(rnd, l*l*l)*&p + randbits(rnd, l),
     p)
}

fn is_correct(x1: &Mpz, x2: &Mpz, l: usize, p_prime: &Mpz) -> bool {
    p_prime.bit_length() == l*l &&
        (x1 % p_prime).bit_length() == l &&
        (x2 % p_prime).bit_length() == l
}

fn random_test(l: usize, n: usize) {
    let mut rnd = RandState::new();  // deterministic seed
    for _ in 0..n {
        let (x1, x2, p) = gen(&mut rnd, l);
        println!("x1 = {}", x1);
        println!("x2 = {}", x2);
        println!("l = {}", l);
        println!("p = {}", p);
        println!("x1 % p = {}", &x1 % &p);
        println!("x2 % p = {}", &x2 % &p);
        assert!(is_correct(&x1, &x2, l, &p));
        let p_prime = solve(&x1, &x2, l).expect("no solution found!");
        println!("p' = {}", p_prime);
        assert!(is_correct(&x1, &x2, l, &p_prime));
        println!("correct");
    }
}

// Command line interface

fn main() {
    let args = &env::args().collect::<Vec<_>>();
    if args.len() == 4 && args[1] == "--random" {
        if let (Ok(l), Ok(n)) = (args[2].parse(), args[3].parse()) {
            return random_test(l, n);
        }
    }
    if args.len() == 4 {
        if let (Ok(x1), Ok(x2), Ok(l)) = (args[1].parse(), args[2].parse(), args[3].parse()) {
            match solve(&x1, &x2, l) {
                None => println!("no solution found"),
                Some(p_prime) => println!("{}", p_prime),
            }
            return;
        }
    }
    println!("Usage:");
    println!("    {} --random L N", args[0]);
    println!("    {} X1 X2 L", args[0]);
}

Cargo.toml

[package]
name = "agcd"
version = "0.1.0"
authors = ["Anders Kaseorg <andersk@mit.edu>"]

[dependencies]
rayon = "0.7.1"
rust-gmp = "0.5.0"

Chạy

cargo build --release
target/release/agcd 56007668599 30611458895 3
target/release/agcd --random 13 5

Kết quả chính thức là l = 13, với thời gian trung bình là 41,53 giây. Trung bình l = 14 mất hơn 2m một chút.
isaacg

2

Toán học, L = 5

đây là một giải pháp nhanh chóng

(l = #3;
a = #1 - Range[2^(l - 1), 2^l];
b = #2 - Range[2^(l - 1), 2^l];
Last@Intersection[
Flatten[Table[Select[Divisors@a[[i]], # < 2^l^2 &], {i, Length@a}],
 1],
Flatten[Table[Select[Divisors@b[[i]], # < 2^l^2 &], {i, Length@b}],
 1]]
) &

Đầu vào
[x1, x2, L]

để bất cứ ai cũng có thể kiểm tra điều này, đây là trình tạo khóa

l = 5;
a = RandomInteger[{2^(l^2 - 1), 2^(l^2)}]
b = RandomInteger[{2^(l^3 - 1), 2^(l^3)}];
c = RandomInteger[{2^(l - 1), 2^l - 1}];
f = RandomInteger[{2^(l^3 - 1), 2^(l^3)}];
g = RandomInteger[{2^(l - 1), 2^l - 1}];
x = a*b + c
y = a*f + g

chọn giá trị L chạy, mã và bạn sẽ nhận được ba số.
đặt hai cái cuối cùng với L làm đầu vào và bạn sẽ lấy cái đầu tiên


Tôi đã xác minh giải pháp này khi nhận được số điểm l = 5. Tôi sẽ tính thời gian nếu thời gian là cần thiết để phá vỡ.
isaacg

1

Toán học, L = 12

ClearAll[l, a, b, k, m];
(l = #3;
a = #1 - Range[2^(l - 1), 2^l];
b = #2 - Range[2^(l - 1), 2^l];
k = Length@a;
m = Length@b;
For[i = 1, i <= k, i++, 
For[j = 1, j <= m, j++, If[2^(l^2 - 1) < GCD[a[[i]], b[[j]]],
 If[GCD[a[[i]], b[[j]]] > 2^l^2, 
  Print@Max@
    Select[Divisors[GCD[a[[i]], b[[j]]]], 
     2^(l^2 - 1) < # < 2^l^2 &]; Abort[], 
  Print[GCD[a[[i]], b[[j]]]];
  Abort[]]]]]) &

đầu vào [x1, x2, L]

để bất cứ ai cũng có thể kiểm tra điều này, đây là trình tạo khóa

l = 12;
a = RandomInteger[{2^(l^2 - 1), 2^(l^2)}]
b = RandomInteger[{2^(l^3 - 1), 2^(l^3)}];
c = RandomInteger[{2^(l - 1), 2^l - 1}];
f = RandomInteger[{2^(l^3 - 1), 2^(l^3)}];
g = RandomInteger[{2^(l - 1), 2^l - 1}];
x = a*b + c
y = a*f + g

chọn giá trị L, chạy mã và bạn sẽ nhận được ba số.
đặt hai cái cuối cùng với L làm đầu vào và bạn sẽ lấy cái đầu tiên


Khi tôi thử nghiệm điều này với l = 12nó, nó đã cho một kết quả không chính xác. Cụ thể, ước số kết quả là một số 146 bit, quá lớn. Tôi nghĩ bạn sẽ chỉ cần một thay đổi nhỏ để khắc phục điều này.
isaacg

Tôi đã thêm trường hợp thất bại như ví dụ cuối cùng ở trên. Người giải của bạn đã đưa ra một câu trả lời bằng 3 * p, một chút quá lớn. Nhìn vào mã của bạn, có vẻ như bạn chỉ kiểm tra xem đầu ra của bạn có ít nhất l^2bit hay không, nhưng bạn cũng cần kiểm tra xem nó có nhiều nhất không l^2.
isaacg

Trong cùng một trường hợp thử nghiệm mà nó đã thất bại trước đó, nó vẫn không hoạt động. Tôi không quá quen thuộc với Mathicala, nhưng có vẻ như nó đã thoát mà không có đầu ra. Tôi nghĩ vấn đề là nó tìm thấy một yếu tố quá lớn, và sau đó thay vì tìm một ước số của yếu tố trong phạm vi mong muốn, nó chỉ lướt qua nó.
isaacg


Điểm chính thức là L = 12, với thời gian trung bình là 52,7 giây. Làm tốt!
isaacg

1

Python, L = 15, thời gian trung bình 39,9 giây

from sys import argv
from random import seed, randrange

from gmpy2 import gcd, mpz
import gmpy2

def mult_buckets(buckets):
    if len(buckets) == 1:
        return buckets[0]
    new_buckets = []
    for i in range(0, len(buckets), 2):
        if i == len(buckets) - 1:
            new_buckets.append(buckets[i])
        else:
            new_buckets.append(buckets[i] * buckets[i+1])
    return mult_buckets(new_buckets)


def get_products(xs, l):
    num_buckets = 1000
    lower_r = 1 << l - 1
    upper_r = 1 << l
    products = []
    bucket_size = (upper_r - lower_r)//num_buckets + 1
    for x in xs:
        buckets = []
        for bucket_num in range(num_buckets):
            product = mpz(1)
            for r in range(lower_r + bucket_num * bucket_size,
                           min(upper_r, lower_r + (bucket_num + 1) * bucket_size)):
                product *= x - mpz(r)
            buckets.append(product)
        products.append(mult_buckets(buckets))
    return products

def solve(xs, l):
    lower_r = 2**(l - 1)
    upper_r = 2**l
    lower_p = 2**(l**2 - 1)
    upper_p = 2**(l**2)

    products = get_products(xs, l)
    overlap = gcd(*products)
    candidate_lists = []
    for x in xs:
        candidates = []
        candidate_lists.append(candidates)
        for r in range(lower_r, upper_r):
            common_divisor = gcd(x-r, overlap)
            if common_divisor >= lower_p:
                candidates.append(common_divisor)
    to_reduce = []
    for l_candidate in candidate_lists[0]:
        for r_candidate in candidate_lists[1]:
            best_divisor = gcd(l_candidate, r_candidate)
            if lower_p <= best_divisor < upper_p:
                return best_divisor
            elif best_divisor > upper_p:
                to_reduce.append(best_divisor)
    for divisor in to_reduce:
        cutoff = divisor // lower_p
        non_divisors = []
        for sub_divisor in range(2, cutoff + 1):
            if any(sub_divisor % non_divisor == 0 for non_divisor in non_divisors):
                continue
            quotient, remainder = gmpy2.c_divmod(divisor, sub_divisor)
            if remainder == 0 and lower_p <= quotient < upper_p:
                return quotient
            if quotient < lower_p:
                break
            if remainder != 0:
                non_divisors.append(sub_divisor)

def is_correct(x1, x2, l, p_prime):
    p_prime_is_good = p_prime >> (l**2 - 1) and not p_prime >> l ** 2
    x1_is_good = (x1 % p_prime) >> (l-1) and not (x1 % p_prime) >> l
    x2_is_good = (x2 % p_prime) >> (l-1) and not (x2 % p_prime) >> l
    return bool(p_prime_is_good and x1_is_good and x2_is_good)

if __name__ == '__main__':
    seed(0)

    l = int(argv[1])
    reps = int(argv[2])

    def randbits(bits):
        return randrange(2 ** (bits - 1), 2 ** bits)

    for _ in range(reps):
        p = randbits(l ** 2)
        print("p = ", p)
        xs = []
        for i in range(2):
            q_i = randbits(l ** 3)
            print("q", i, "=", q_i)
            r_i = randbits(l)
            print("r", i, "=", r_i)
            x_i = q_i * p + r_i
            print("x", i, "=", x_i)
            xs.append(x_i)

        res = solve(xs, l)
        print("answer = ", res)
        print("Correct?", is_correct(xs[0], xs[1], l, res))

Mã này dựa trên thực tế là sản phẩm của x1 - r cho tất cả các giá trị có thể có của r phải là bội số của p và sản phẩm của x2 - r cũng phải như vậy. Hầu hết thời gian được dành để lấy gcd của hai sản phẩm, mỗi sản phẩm có khoảng 60.000.000 bitcoin. Gcd, chỉ có khoảng 250.000 bit, sau đó được so sánh với từng giá trị xr một lần nữa, để trích xuất các ứng cử viên p '. Nếu chúng quá lớn, phân chia thử nghiệm được sử dụng để giảm chúng xuống phạm vi thích hợp.

Điều này dựa trên thư viện gmpy2 cho Python, đây là một trình bao bọc mỏng cho thư viện GNU MP, đặc biệt có thói quen gcd thực sự tốt.

Mã này là đơn luồng.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.